From 5d1fa7e412cb34b97ca924018ca8f357df9e7b29 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:49 +0100 Subject: [PATCH 0001/1334] nubus: add comment indicating reference documents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-bus.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index 5c13452308..f4410803ff 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -8,6 +8,14 @@ * */ +/* + * References: + * Nubus Specification (TI) + * http://www.bitsavers.org/pdf/ti/nubus/2242825-0001_NuBus_Spec1983.pdf + * + * Designing Cards and Drivers for the Macintosh Family (Apple) + */ + #include "qemu/osdep.h" #include "hw/nubus/nubus.h" #include "qapi/error.h" From e2c49c0515f5995b267a9795d88733865d2d0ced Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:50 +0100 Subject: [PATCH 0002/1334] nubus-device: rename slot_nb variable to slot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is in preparation for creating a qdev property of the same name. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-device.c | 14 +++++++------- include/hw/nubus/nubus.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index ffe78a8823..be01269563 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -87,7 +87,7 @@ static void nubus_register_format_block(NubusDevice *dev) char *fblock_name; fblock_name = g_strdup_printf("nubus-slot-%d-format-block", - dev->slot_nb); + dev->slot); hwaddr fblock_offset = memory_region_size(&dev->slot_mem) - FBLOCK_SIZE; memory_region_init_io(&dev->fblock_io, NULL, &nubus_format_block_ops, @@ -142,7 +142,7 @@ void nubus_register_rom(NubusDevice *dev, const uint8_t *rom, uint32_t size, /* ROM */ dev->rom = rom; - rom_name = g_strdup_printf("nubus-slot-%d-rom", dev->slot_nb); + rom_name = g_strdup_printf("nubus-slot-%d-rom", dev->slot); memory_region_init_io(&dev->rom_io, NULL, &mac_nubus_rom_ops, dev, rom_name, size); memory_region_set_readonly(&dev->rom_io, true); @@ -167,12 +167,12 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) return; } - nd->slot_nb = nubus->current_slot++; - name = g_strdup_printf("nubus-slot-%d", nd->slot_nb); + nd->slot = nubus->current_slot++; + name = g_strdup_printf("nubus-slot-%d", nd->slot); - if (nd->slot_nb < NUBUS_FIRST_SLOT) { + if (nd->slot < NUBUS_FIRST_SLOT) { /* Super */ - slot_offset = (nd->slot_nb - 6) * NUBUS_SUPER_SLOT_SIZE; + slot_offset = (nd->slot - 6) * NUBUS_SUPER_SLOT_SIZE; memory_region_init(&nd->slot_mem, OBJECT(dev), name, NUBUS_SUPER_SLOT_SIZE); @@ -180,7 +180,7 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) &nd->slot_mem); } else { /* Normal */ - slot_offset = nd->slot_nb * NUBUS_SLOT_SIZE; + slot_offset = nd->slot * NUBUS_SLOT_SIZE; memory_region_init(&nd->slot_mem, OBJECT(dev), name, NUBUS_SLOT_SIZE); memory_region_add_subregion(&nubus->slot_io, slot_offset, diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index e2b5cf260b..424309dd73 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -42,7 +42,7 @@ struct NubusBus { struct NubusDevice { DeviceState qdev; - int slot_nb; + int slot; MemoryRegion slot_mem; /* Format Block */ From 90be1dea5017e518076f2a0d3ad26e158b92a1da Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:51 +0100 Subject: [PATCH 0003/1334] nubus-device: expose separate super slot memory region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to "Designing Cards and Drivers for the Macintosh Family" each physical nubus slot can access 2 separate address ranges: a super slot memory region which is 256MB and a standard slot memory region which is 16MB. Currently a Nubus device uses the physical slot number to determine whether it is using a standard slot memory region or a super slot memory region rather than exposing both memory regions for use as required. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-4-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-device.c | 34 +++++++++++++++++----------------- include/hw/nubus/nubus.h | 1 + 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index be01269563..4e23df1280 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -168,26 +168,26 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) } nd->slot = nubus->current_slot++; - name = g_strdup_printf("nubus-slot-%d", nd->slot); - if (nd->slot < NUBUS_FIRST_SLOT) { - /* Super */ - slot_offset = (nd->slot - 6) * NUBUS_SUPER_SLOT_SIZE; - - memory_region_init(&nd->slot_mem, OBJECT(dev), name, - NUBUS_SUPER_SLOT_SIZE); - memory_region_add_subregion(&nubus->super_slot_io, slot_offset, - &nd->slot_mem); - } else { - /* Normal */ - slot_offset = nd->slot * NUBUS_SLOT_SIZE; - - memory_region_init(&nd->slot_mem, OBJECT(dev), name, NUBUS_SLOT_SIZE); - memory_region_add_subregion(&nubus->slot_io, slot_offset, - &nd->slot_mem); - } + /* Super */ + slot_offset = nd->slot * NUBUS_SUPER_SLOT_SIZE; + name = g_strdup_printf("nubus-super-slot-%x", nd->slot); + memory_region_init(&nd->super_slot_mem, OBJECT(dev), name, + NUBUS_SUPER_SLOT_SIZE); + memory_region_add_subregion(&nubus->super_slot_io, slot_offset, + &nd->super_slot_mem); g_free(name); + + /* Normal */ + slot_offset = nd->slot * NUBUS_SLOT_SIZE; + + name = g_strdup_printf("nubus-slot-%x", nd->slot); + memory_region_init(&nd->slot_mem, OBJECT(dev), name, NUBUS_SLOT_SIZE); + memory_region_add_subregion(&nubus->slot_io, slot_offset, + &nd->slot_mem); + g_free(name); + nubus_register_format_block(nd); } diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 424309dd73..89b0976aaa 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -43,6 +43,7 @@ struct NubusDevice { DeviceState qdev; int slot; + MemoryRegion super_slot_mem; MemoryRegion slot_mem; /* Format Block */ From 03deab99401a57332d8f3bdba319a41eebc1d666 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:52 +0100 Subject: [PATCH 0004/1334] nubus: use bitmap to manage available slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert nubus_device_realize() to use a bitmap to manage available slots to allow for future Nubus devices to be plugged into arbitrary slots from the command line using a new qdev "slot" parameter for nubus devices. Update mac_nubus_bridge_init() to only allow slots 0x9 to 0xe on Macintosh machines as documented in "Designing Cards and Drivers for the Macintosh Family". Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210924073808.1041-5-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/mac-nubus-bridge.c | 4 ++++ hw/nubus/nubus-bus.c | 5 +++-- hw/nubus/nubus-device.c | 29 ++++++++++++++++++++++++----- include/hw/nubus/mac-nubus-bridge.h | 4 ++++ include/hw/nubus/nubus.h | 13 ++++++------- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/hw/nubus/mac-nubus-bridge.c b/hw/nubus/mac-nubus-bridge.c index 7c329300b8..3f075789e9 100644 --- a/hw/nubus/mac-nubus-bridge.c +++ b/hw/nubus/mac-nubus-bridge.c @@ -18,6 +18,10 @@ static void mac_nubus_bridge_init(Object *obj) s->bus = NUBUS_BUS(qbus_create(TYPE_NUBUS_BUS, DEVICE(s), NULL)); + /* Macintosh only has slots 0x9 to 0xe available */ + s->bus->slot_available_mask = MAKE_64BIT_MASK(MAC_NUBUS_FIRST_SLOT, + MAC_NUBUS_SLOT_NB); + sysbus_init_mmio(sbd, &s->bus->super_slot_io); sysbus_init_mmio(sbd, &s->bus->slot_io); } diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index f4410803ff..3cd7534864 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -86,13 +86,14 @@ static void nubus_init(Object *obj) memory_region_init_io(&nubus->super_slot_io, obj, &nubus_super_slot_ops, nubus, "nubus-super-slots", - NUBUS_SUPER_SLOT_NB * NUBUS_SUPER_SLOT_SIZE); + (NUBUS_SUPER_SLOT_NB + 1) * NUBUS_SUPER_SLOT_SIZE); memory_region_init_io(&nubus->slot_io, obj, &nubus_slot_ops, nubus, "nubus-slots", NUBUS_SLOT_NB * NUBUS_SLOT_SIZE); - nubus->current_slot = NUBUS_FIRST_SLOT; + nubus->slot_available_mask = MAKE_64BIT_MASK(NUBUS_FIRST_SLOT, + NUBUS_SLOT_NB); } static void nubus_class_init(ObjectClass *oc, void *data) diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 4e23df1280..2e96d6b4fc 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -161,13 +161,26 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) char *name; hwaddr slot_offset; - if (nubus->current_slot < NUBUS_FIRST_SLOT || - nubus->current_slot > NUBUS_LAST_SLOT) { - error_setg(errp, "Cannot register nubus card, not enough slots"); - return; + if (nd->slot == -1) { + /* No slot specified, find first available free slot */ + int s = ctz32(nubus->slot_available_mask); + if (s != 32) { + nd->slot = s; + } else { + error_setg(errp, "Cannot register nubus card, no free slot " + "available"); + return; + } + } else { + /* Slot specified, make sure the slot is available */ + if (!(nubus->slot_available_mask & BIT(nd->slot))) { + error_setg(errp, "Cannot register nubus card, slot %d is " + "unavailable or already occupied", nd->slot); + return; + } } - nd->slot = nubus->current_slot++; + nubus->slot_available_mask &= ~BIT(nd->slot); /* Super */ slot_offset = nd->slot * NUBUS_SUPER_SLOT_SIZE; @@ -191,12 +204,18 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) nubus_register_format_block(nd); } +static Property nubus_device_properties[] = { + DEFINE_PROP_INT32("slot", NubusDevice, slot, -1), + DEFINE_PROP_END_OF_LIST() +}; + static void nubus_device_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = nubus_device_realize; dc->bus_type = TYPE_NUBUS_BUS; + device_class_set_props(dc, nubus_device_properties); } static const TypeInfo nubus_device_type_info = { diff --git a/include/hw/nubus/mac-nubus-bridge.h b/include/hw/nubus/mac-nubus-bridge.h index 36aa098dd4..118d67267d 100644 --- a/include/hw/nubus/mac-nubus-bridge.h +++ b/include/hw/nubus/mac-nubus-bridge.h @@ -12,6 +12,10 @@ #include "hw/nubus/nubus.h" #include "qom/object.h" +#define MAC_NUBUS_FIRST_SLOT 0x9 +#define MAC_NUBUS_LAST_SLOT 0xe +#define MAC_NUBUS_SLOT_NB (MAC_NUBUS_LAST_SLOT - MAC_NUBUS_FIRST_SLOT + 1) + #define TYPE_MAC_NUBUS_BRIDGE "mac-nubus-bridge" OBJECT_DECLARE_SIMPLE_TYPE(MacNubusState, MAC_NUBUS_BRIDGE) diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 89b0976aaa..3eea2952d5 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -14,13 +14,12 @@ #include "qom/object.h" #define NUBUS_SUPER_SLOT_SIZE 0x10000000U -#define NUBUS_SUPER_SLOT_NB 0x9 +#define NUBUS_SUPER_SLOT_NB 0xe #define NUBUS_SLOT_SIZE 0x01000000 -#define NUBUS_SLOT_NB 0xF - -#define NUBUS_FIRST_SLOT 0x9 -#define NUBUS_LAST_SLOT 0xF +#define NUBUS_FIRST_SLOT 0x0 +#define NUBUS_LAST_SLOT 0xf +#define NUBUS_SLOT_NB (NUBUS_LAST_SLOT - NUBUS_FIRST_SLOT + 1) #define TYPE_NUBUS_DEVICE "nubus-device" OBJECT_DECLARE_SIMPLE_TYPE(NubusDevice, NUBUS_DEVICE) @@ -36,13 +35,13 @@ struct NubusBus { MemoryRegion super_slot_io; MemoryRegion slot_io; - int current_slot; + uint16_t slot_available_mask; }; struct NubusDevice { DeviceState qdev; - int slot; + int32_t slot; MemoryRegion super_slot_mem; MemoryRegion slot_mem; From c10a576c19665a617ce00cb93afd115142b6a7af Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:53 +0100 Subject: [PATCH 0005/1334] nubus: move slot bitmap checks from NubusDevice realize() to BusClass check_address() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow Nubus to manage the slot allocations itself using the BusClass check_address() virtual function rather than managing this during NubusDevice realize(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210924073808.1041-6-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-bus.c | 29 +++++++++++++++++++++++++++++ hw/nubus/nubus-device.c | 21 --------------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index 3cd7534864..96ef027bad 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -96,11 +96,40 @@ static void nubus_init(Object *obj) NUBUS_SLOT_NB); } +static bool nubus_check_address(BusState *bus, DeviceState *dev, Error **errp) +{ + NubusDevice *nd = NUBUS_DEVICE(dev); + NubusBus *nubus = NUBUS_BUS(bus); + + if (nd->slot == -1) { + /* No slot specified, find first available free slot */ + int s = ctz32(nubus->slot_available_mask); + if (s != 32) { + nd->slot = s; + } else { + error_setg(errp, "Cannot register nubus card, no free slot " + "available"); + return false; + } + } else { + /* Slot specified, make sure the slot is available */ + if (!(nubus->slot_available_mask & BIT(nd->slot))) { + error_setg(errp, "Cannot register nubus card, slot %d is " + "unavailable or already occupied", nd->slot); + return false; + } + } + + nubus->slot_available_mask &= ~BIT(nd->slot); + return true; +} + static void nubus_class_init(ObjectClass *oc, void *data) { BusClass *bc = BUS_CLASS(oc); bc->realize = nubus_realize; + bc->check_address = nubus_check_address; } static const TypeInfo nubus_bus_info = { diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 2e96d6b4fc..516b13d2d5 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -161,27 +161,6 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) char *name; hwaddr slot_offset; - if (nd->slot == -1) { - /* No slot specified, find first available free slot */ - int s = ctz32(nubus->slot_available_mask); - if (s != 32) { - nd->slot = s; - } else { - error_setg(errp, "Cannot register nubus card, no free slot " - "available"); - return; - } - } else { - /* Slot specified, make sure the slot is available */ - if (!(nubus->slot_available_mask & BIT(nd->slot))) { - error_setg(errp, "Cannot register nubus card, slot %d is " - "unavailable or already occupied", nd->slot); - return; - } - } - - nubus->slot_available_mask &= ~BIT(nd->slot); - /* Super */ slot_offset = nd->slot * NUBUS_SUPER_SLOT_SIZE; From c0ad4eaf44d9aea4c2872e00bab5b36d05c9e0d6 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:54 +0100 Subject: [PATCH 0006/1334] nubus: implement BusClass get_dev_path() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-7-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-bus.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index 96ef027bad..04f11edd24 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -96,6 +96,21 @@ static void nubus_init(Object *obj) NUBUS_SLOT_NB); } +static char *nubus_get_dev_path(DeviceState *dev) +{ + NubusDevice *nd = NUBUS_DEVICE(dev); + BusState *bus = qdev_get_parent_bus(dev); + char *p = qdev_get_dev_path(bus->parent); + + if (p) { + char *ret = g_strdup_printf("%s/%s/%02x", p, bus->name, nd->slot); + g_free(p); + return ret; + } else { + return g_strdup_printf("%s/%02x", bus->name, nd->slot); + } +} + static bool nubus_check_address(BusState *bus, DeviceState *dev, Error **errp) { NubusDevice *nd = NUBUS_DEVICE(dev); @@ -130,6 +145,7 @@ static void nubus_class_init(ObjectClass *oc, void *data) bc->realize = nubus_realize; bc->check_address = nubus_check_address; + bc->get_dev_path = nubus_get_dev_path; } static const TypeInfo nubus_bus_info = { From ce0e6a2c55b13c16de4a7985c0ca41835420430a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:55 +0100 Subject: [PATCH 0007/1334] nubus: add trace-events for empty slot accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increase the max_access_size to 4 bytes for empty Nubus slot and super slot accesses to allow tracing of the Nubus enumeration process by the guest OS. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-8-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-bus.c | 10 +++++++--- hw/nubus/trace-events | 7 +++++++ hw/nubus/trace.h | 1 + meson.build | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 hw/nubus/trace-events create mode 100644 hw/nubus/trace.h diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index 04f11edd24..a9fb6ded9e 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "hw/nubus/nubus.h" #include "qapi/error.h" +#include "trace.h" static NubusBus *nubus_find(void) @@ -31,12 +32,13 @@ static void nubus_slot_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { /* read only */ + trace_nubus_slot_write(addr, val, size); } - static uint64_t nubus_slot_read(void *opaque, hwaddr addr, unsigned int size) { + trace_nubus_slot_read(addr, size); return 0; } @@ -46,7 +48,7 @@ static const MemoryRegionOps nubus_slot_ops = { .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 1, - .max_access_size = 1, + .max_access_size = 4, }, }; @@ -54,11 +56,13 @@ static void nubus_super_slot_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { /* read only */ + trace_nubus_super_slot_write(addr, val, size); } static uint64_t nubus_super_slot_read(void *opaque, hwaddr addr, unsigned int size) { + trace_nubus_super_slot_read(addr, size); return 0; } @@ -68,7 +72,7 @@ static const MemoryRegionOps nubus_super_slot_ops = { .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 1, - .max_access_size = 1, + .max_access_size = 4, }, }; diff --git a/hw/nubus/trace-events b/hw/nubus/trace-events new file mode 100644 index 0000000000..e31833d694 --- /dev/null +++ b/hw/nubus/trace-events @@ -0,0 +1,7 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# nubus-bus.c +nubus_slot_read(uint64_t addr, int size) "reading unassigned addr 0x%"PRIx64 " size %d" +nubus_slot_write(uint64_t addr, uint64_t val, int size) "writing unassigned addr 0x%"PRIx64 " value 0x%"PRIx64 " size %d" +nubus_super_slot_read(uint64_t addr, int size) "reading unassigned addr 0x%"PRIx64 " size %d" +nubus_super_slot_write(uint64_t addr, uint64_t val, int size) "writing unassigned addr 0x%"PRIx64 " value 0x%"PRIx64 " size %d" diff --git a/hw/nubus/trace.h b/hw/nubus/trace.h new file mode 100644 index 0000000000..3749420da1 --- /dev/null +++ b/hw/nubus/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_nubus.h" diff --git a/meson.build b/meson.build index 15ef4d3c41..7bdbbbdf02 100644 --- a/meson.build +++ b/meson.build @@ -2142,6 +2142,7 @@ if have_system 'hw/misc/macio', 'hw/net', 'hw/net/can', + 'hw/nubus', 'hw/nvme', 'hw/nvram', 'hw/pci', From 1d3d62dff8f069c379123690a30817d7aa575cae Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:56 +0100 Subject: [PATCH 0008/1334] nubus: generate bus error when attempting to access empty slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to "Designing Cards and Drivers for the Macintosh Family" any attempt to access an unimplemented address location on Nubus generates a bus error. MacOS uses a custom bus error handler to detect empty Nubus slots, and with the current implementation assumes that all slots are occupied as the Nubus transactions never fail. Switch nubus_slot_ops and nubus_super_slot_ops over to use {read,write}_with_attrs and hard-code them to return MEMTX_DECODE_ERROR so that unoccupied Nubus slots will generate the expected bus error. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-9-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-bus.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index a9fb6ded9e..215fdb6b4e 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -28,23 +28,23 @@ static NubusBus *nubus_find(void) return NUBUS_BUS(object_resolve_path_type("", TYPE_NUBUS_BUS, NULL)); } -static void nubus_slot_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int size) +static MemTxResult nubus_slot_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) { - /* read only */ trace_nubus_slot_write(addr, val, size); + return MEMTX_DECODE_ERROR; } -static uint64_t nubus_slot_read(void *opaque, hwaddr addr, - unsigned int size) +static MemTxResult nubus_slot_read(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) { trace_nubus_slot_read(addr, size); - return 0; + return MEMTX_DECODE_ERROR; } static const MemoryRegionOps nubus_slot_ops = { - .read = nubus_slot_read, - .write = nubus_slot_write, + .read_with_attrs = nubus_slot_read, + .write_with_attrs = nubus_slot_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 1, @@ -52,23 +52,25 @@ static const MemoryRegionOps nubus_slot_ops = { }, }; -static void nubus_super_slot_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int size) +static MemTxResult nubus_super_slot_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) { - /* read only */ trace_nubus_super_slot_write(addr, val, size); + return MEMTX_DECODE_ERROR; } -static uint64_t nubus_super_slot_read(void *opaque, hwaddr addr, - unsigned int size) +static MemTxResult nubus_super_slot_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) { trace_nubus_super_slot_read(addr, size); - return 0; + return MEMTX_DECODE_ERROR; } static const MemoryRegionOps nubus_super_slot_ops = { - .read = nubus_super_slot_read, - .write = nubus_super_slot_write, + .read_with_attrs = nubus_super_slot_read, + .write_with_attrs = nubus_super_slot_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 1, From e0591bf1a56fa59b787870fbdd4e5d7ca8d745d2 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:57 +0100 Subject: [PATCH 0009/1334] macfb: don't register declaration ROM The macfb device is an on-board framebuffer and so is initialised by the system declaration ROM included within the MacOS toolbox ROM. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-10-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index d8183b9bbd..76808b69cc 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -383,10 +383,6 @@ static void macfb_sysbus_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram); } -const uint8_t macfb_rom[] = { - 255, 0, 0, 0, -}; - static void macfb_nubus_realize(DeviceState *dev, Error **errp) { NubusDevice *nd = NUBUS_DEVICE(dev); @@ -399,8 +395,6 @@ static void macfb_nubus_realize(DeviceState *dev, Error **errp) macfb_common_realize(dev, ms, errp); memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl); memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram); - - nubus_register_rom(nd, macfb_rom, sizeof(macfb_rom), 1, 9, 0xf); } static void macfb_sysbus_reset(DeviceState *d) From 2469dc1dda596b13b4a8c7ed7f03d5991fa61b43 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:58 +0100 Subject: [PATCH 0010/1334] nubus-device: remove nubus_register_rom() and nubus_register_format_block() Since there is no need to generate a dummy declaration ROM, remove both nubus_register_rom() and nubus_register_format_block(). These will shortly be replaced with a mechanism to optionally load a declaration ROM from disk to allow real images to be used within QEMU. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-11-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-device.c | 143 --------------------------------------- include/hw/nubus/nubus.h | 19 ------ 2 files changed, 162 deletions(-) diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 516b13d2d5..d4932d64a2 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -13,147 +13,6 @@ #include "qapi/error.h" -/* The Format Block Structure */ - -#define FBLOCK_DIRECTORY_OFFSET 0 -#define FBLOCK_LENGTH 4 -#define FBLOCK_CRC 8 -#define FBLOCK_REVISION_LEVEL 12 -#define FBLOCK_FORMAT 13 -#define FBLOCK_TEST_PATTERN 14 -#define FBLOCK_RESERVED 18 -#define FBLOCK_BYTE_LANES 19 - -#define FBLOCK_SIZE 20 -#define FBLOCK_PATTERN_VAL 0x5a932bc7 - -static uint64_t nubus_fblock_read(void *opaque, hwaddr addr, unsigned int size) -{ - NubusDevice *dev = opaque; - uint64_t val; - -#define BYTE(v, b) (((v) >> (24 - 8 * (b))) & 0xff) - switch (addr) { - case FBLOCK_BYTE_LANES: - val = dev->byte_lanes; - val |= (val ^ 0xf) << 4; - break; - case FBLOCK_RESERVED: - val = 0x00; - break; - case FBLOCK_TEST_PATTERN...FBLOCK_TEST_PATTERN + 3: - val = BYTE(FBLOCK_PATTERN_VAL, addr - FBLOCK_TEST_PATTERN); - break; - case FBLOCK_FORMAT: - val = dev->rom_format; - break; - case FBLOCK_REVISION_LEVEL: - val = dev->rom_rev; - break; - case FBLOCK_CRC...FBLOCK_CRC + 3: - val = BYTE(dev->rom_crc, addr - FBLOCK_CRC); - break; - case FBLOCK_LENGTH...FBLOCK_LENGTH + 3: - val = BYTE(dev->rom_length, addr - FBLOCK_LENGTH); - break; - case FBLOCK_DIRECTORY_OFFSET...FBLOCK_DIRECTORY_OFFSET + 3: - val = BYTE(dev->directory_offset, addr - FBLOCK_DIRECTORY_OFFSET); - break; - default: - val = 0; - break; - } - return val; -} - -static void nubus_fblock_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int size) -{ - /* read only */ -} - -static const MemoryRegionOps nubus_format_block_ops = { - .read = nubus_fblock_read, - .write = nubus_fblock_write, - .endianness = DEVICE_BIG_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - } -}; - -static void nubus_register_format_block(NubusDevice *dev) -{ - char *fblock_name; - - fblock_name = g_strdup_printf("nubus-slot-%d-format-block", - dev->slot); - - hwaddr fblock_offset = memory_region_size(&dev->slot_mem) - FBLOCK_SIZE; - memory_region_init_io(&dev->fblock_io, NULL, &nubus_format_block_ops, - dev, fblock_name, FBLOCK_SIZE); - memory_region_add_subregion(&dev->slot_mem, fblock_offset, - &dev->fblock_io); - - g_free(fblock_name); -} - -static void mac_nubus_rom_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int size) -{ - /* read only */ -} - -static uint64_t mac_nubus_rom_read(void *opaque, hwaddr addr, - unsigned int size) -{ - NubusDevice *dev = opaque; - - return dev->rom[addr]; -} - -static const MemoryRegionOps mac_nubus_rom_ops = { - .read = mac_nubus_rom_read, - .write = mac_nubus_rom_write, - .endianness = DEVICE_BIG_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - - -void nubus_register_rom(NubusDevice *dev, const uint8_t *rom, uint32_t size, - int revision, int format, uint8_t byte_lanes) -{ - hwaddr rom_offset; - char *rom_name; - - /* FIXME : really compute CRC */ - dev->rom_length = 0; - dev->rom_crc = 0; - - dev->rom_rev = revision; - dev->rom_format = format; - - dev->byte_lanes = byte_lanes; - dev->directory_offset = -size; - - /* ROM */ - - dev->rom = rom; - rom_name = g_strdup_printf("nubus-slot-%d-rom", dev->slot); - memory_region_init_io(&dev->rom_io, NULL, &mac_nubus_rom_ops, - dev, rom_name, size); - memory_region_set_readonly(&dev->rom_io, true); - - rom_offset = memory_region_size(&dev->slot_mem) - FBLOCK_SIZE + - dev->directory_offset; - memory_region_add_subregion(&dev->slot_mem, rom_offset, &dev->rom_io); - - g_free(rom_name); -} - static void nubus_device_realize(DeviceState *dev, Error **errp) { NubusBus *nubus = NUBUS_BUS(qdev_get_parent_bus(dev)); @@ -179,8 +38,6 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&nubus->slot_io, slot_offset, &nd->slot_mem); g_free(name); - - nubus_register_format_block(nd); } static Property nubus_device_properties[] = { diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 3eea2952d5..187ecc00a5 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -44,25 +44,6 @@ struct NubusDevice { int32_t slot; MemoryRegion super_slot_mem; MemoryRegion slot_mem; - - /* Format Block */ - - MemoryRegion fblock_io; - - uint32_t rom_length; - uint32_t rom_crc; - uint8_t rom_rev; - uint8_t rom_format; - uint8_t byte_lanes; - int32_t directory_offset; - - /* ROM */ - - MemoryRegion rom_io; - const uint8_t *rom; }; -void nubus_register_rom(NubusDevice *dev, const uint8_t *rom, uint32_t size, - int revision, int format, uint8_t byte_lanes); - #endif From 3616f424c911f1b52629cea1fec6ef99e9da07ad Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:37:59 +0100 Subject: [PATCH 0011/1334] nubus-device: add romfile property for loading declaration ROMs The declaration ROM is located at the top-most address of the standard slot space. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-12-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-device.c | 44 +++++++++++++++++++++++++++++++++++++++- include/hw/nubus/nubus.h | 6 ++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index d4932d64a2..280f40e88a 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -9,16 +9,21 @@ */ #include "qemu/osdep.h" +#include "qemu/datadir.h" +#include "hw/loader.h" #include "hw/nubus/nubus.h" #include "qapi/error.h" +#include "qemu/error-report.h" static void nubus_device_realize(DeviceState *dev, Error **errp) { NubusBus *nubus = NUBUS_BUS(qdev_get_parent_bus(dev)); NubusDevice *nd = NUBUS_DEVICE(dev); - char *name; + char *name, *path; hwaddr slot_offset; + int64_t size; + int ret; /* Super */ slot_offset = nd->slot * NUBUS_SUPER_SLOT_SIZE; @@ -38,10 +43,47 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&nubus->slot_io, slot_offset, &nd->slot_mem); g_free(name); + + /* Declaration ROM */ + if (nd->romfile != NULL) { + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, nd->romfile); + if (path == NULL) { + path = g_strdup(nd->romfile); + } + + size = get_image_size(path); + if (size < 0) { + error_setg(errp, "failed to find romfile \"%s\"", nd->romfile); + g_free(path); + return; + } else if (size == 0) { + error_setg(errp, "romfile \"%s\" is empty", nd->romfile); + g_free(path); + return; + } else if (size > NUBUS_DECL_ROM_MAX_SIZE) { + error_setg(errp, "romfile \"%s\" too large (maximum size 128K)", + nd->romfile); + g_free(path); + return; + } + + name = g_strdup_printf("nubus-slot-%x-declaration-rom", nd->slot); + memory_region_init_rom(&nd->decl_rom, OBJECT(dev), name, size, + &error_abort); + ret = load_image_mr(path, &nd->decl_rom); + g_free(path); + if (ret < 0) { + error_setg(errp, "could not load romfile \"%s\"", nd->romfile); + return; + } + memory_region_add_subregion(&nd->slot_mem, NUBUS_SLOT_SIZE - size, + &nd->decl_rom); + } } static Property nubus_device_properties[] = { DEFINE_PROP_INT32("slot", NubusDevice, slot, -1), + DEFINE_PROP_STRING("romfile", NubusDevice, romfile), DEFINE_PROP_END_OF_LIST() }; diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 187ecc00a5..343be95841 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -12,6 +12,7 @@ #include "hw/qdev-properties.h" #include "exec/address-spaces.h" #include "qom/object.h" +#include "qemu/units.h" #define NUBUS_SUPER_SLOT_SIZE 0x10000000U #define NUBUS_SUPER_SLOT_NB 0xe @@ -38,12 +39,17 @@ struct NubusBus { uint16_t slot_available_mask; }; +#define NUBUS_DECL_ROM_MAX_SIZE (128 * KiB) + struct NubusDevice { DeviceState qdev; int32_t slot; MemoryRegion super_slot_mem; MemoryRegion slot_mem; + + char *romfile; + MemoryRegion decl_rom; }; #endif From 62437f90cf90d1a0fda855f17ca6d9e7c0204f92 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:00 +0100 Subject: [PATCH 0012/1334] nubus: move nubus to its own 32-bit address space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to "Designing Cards and Drivers for the Macintosh Family" the Nubus has its own 32-bit address space based upon physical slot addressing. Move Nubus to its own 32-bit address space and then use memory region aliases to map available slot and super slot ranges into the q800 system address space via the Macintosh Nubus bridge. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-13-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 9 ++++----- hw/nubus/mac-nubus-bridge.c | 16 ++++++++++++++-- hw/nubus/nubus-bus.c | 18 ++++++++++++++++++ include/hw/nubus/mac-nubus-bridge.h | 2 ++ include/hw/nubus/nubus.h | 6 ++++++ 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 5ba87f789c..a07912b87c 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -67,9 +67,6 @@ #define ASC_BASE (IO_BASE + 0x14000) #define SWIM_BASE (IO_BASE + 0x1E000) -#define NUBUS_SUPER_SLOT_BASE 0x60000000 -#define NUBUS_SLOT_BASE 0xf0000000 - #define SONIC_PROM_SIZE 0x1000 /* @@ -396,8 +393,10 @@ static void q800_init(MachineState *machine) dev = qdev_new(TYPE_MAC_NUBUS_BRIDGE); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, NUBUS_SUPER_SLOT_BASE); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, + MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE + + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE); nubus = MAC_NUBUS_BRIDGE(dev)->bus; diff --git a/hw/nubus/mac-nubus-bridge.c b/hw/nubus/mac-nubus-bridge.c index 3f075789e9..3af4f5d396 100644 --- a/hw/nubus/mac-nubus-bridge.c +++ b/hw/nubus/mac-nubus-bridge.c @@ -22,8 +22,20 @@ static void mac_nubus_bridge_init(Object *obj) s->bus->slot_available_mask = MAKE_64BIT_MASK(MAC_NUBUS_FIRST_SLOT, MAC_NUBUS_SLOT_NB); - sysbus_init_mmio(sbd, &s->bus->super_slot_io); - sysbus_init_mmio(sbd, &s->bus->slot_io); + /* Aliases for slots 0x9 to 0xe */ + memory_region_init_alias(&s->super_slot_alias, obj, "super-slot-alias", + &s->bus->nubus_mr, + MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE, + MAC_NUBUS_SLOT_NB * NUBUS_SUPER_SLOT_SIZE); + + memory_region_init_alias(&s->slot_alias, obj, "slot-alias", + &s->bus->nubus_mr, + NUBUS_SLOT_BASE + + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE, + MAC_NUBUS_SLOT_NB * NUBUS_SLOT_SIZE); + + sysbus_init_mmio(sbd, &s->super_slot_alias); + sysbus_init_mmio(sbd, &s->slot_alias); } static void mac_nubus_bridge_class_init(ObjectClass *klass, void *data) diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index 215fdb6b4e..07c279bde5 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -78,25 +78,42 @@ static const MemoryRegionOps nubus_super_slot_ops = { }, }; +static void nubus_unrealize(BusState *bus) +{ + NubusBus *nubus = NUBUS_BUS(bus); + + address_space_destroy(&nubus->nubus_as); +} + static void nubus_realize(BusState *bus, Error **errp) { + NubusBus *nubus = NUBUS_BUS(bus); + if (!nubus_find()) { error_setg(errp, "at most one %s device is permitted", TYPE_NUBUS_BUS); return; } + + address_space_init(&nubus->nubus_as, &nubus->nubus_mr, "nubus"); } static void nubus_init(Object *obj) { NubusBus *nubus = NUBUS_BUS(obj); + memory_region_init(&nubus->nubus_mr, obj, "nubus", 0x100000000); + memory_region_init_io(&nubus->super_slot_io, obj, &nubus_super_slot_ops, nubus, "nubus-super-slots", (NUBUS_SUPER_SLOT_NB + 1) * NUBUS_SUPER_SLOT_SIZE); + memory_region_add_subregion(&nubus->nubus_mr, 0x0, &nubus->super_slot_io); memory_region_init_io(&nubus->slot_io, obj, &nubus_slot_ops, nubus, "nubus-slots", NUBUS_SLOT_NB * NUBUS_SLOT_SIZE); + memory_region_add_subregion(&nubus->nubus_mr, + (NUBUS_SUPER_SLOT_NB + 1) * + NUBUS_SUPER_SLOT_SIZE, &nubus->slot_io); nubus->slot_available_mask = MAKE_64BIT_MASK(NUBUS_FIRST_SLOT, NUBUS_SLOT_NB); @@ -150,6 +167,7 @@ static void nubus_class_init(ObjectClass *oc, void *data) BusClass *bc = BUS_CLASS(oc); bc->realize = nubus_realize; + bc->unrealize = nubus_unrealize; bc->check_address = nubus_check_address; bc->get_dev_path = nubus_get_dev_path; } diff --git a/include/hw/nubus/mac-nubus-bridge.h b/include/hw/nubus/mac-nubus-bridge.h index 118d67267d..04451d357c 100644 --- a/include/hw/nubus/mac-nubus-bridge.h +++ b/include/hw/nubus/mac-nubus-bridge.h @@ -23,6 +23,8 @@ struct MacNubusState { SysBusDevice sysbus_dev; NubusBus *bus; + MemoryRegion super_slot_alias; + MemoryRegion slot_alias; }; #endif diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 343be95841..9f9386afed 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -17,6 +17,9 @@ #define NUBUS_SUPER_SLOT_SIZE 0x10000000U #define NUBUS_SUPER_SLOT_NB 0xe +#define NUBUS_SLOT_BASE (NUBUS_SUPER_SLOT_SIZE * \ + (NUBUS_SUPER_SLOT_NB + 1)) + #define NUBUS_SLOT_SIZE 0x01000000 #define NUBUS_FIRST_SLOT 0x0 #define NUBUS_LAST_SLOT 0xf @@ -33,6 +36,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(NubusBus, NUBUS_BUS) struct NubusBus { BusState qbus; + AddressSpace nubus_as; + MemoryRegion nubus_mr; + MemoryRegion super_slot_io; MemoryRegion slot_io; From 9bf674bc71d972426d4659e0b7769f4e45219cf7 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:01 +0100 Subject: [PATCH 0013/1334] nubus-bridge: introduce separate NubusBridge structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to allow the Nubus bridge to store its own additional state. Also update the comment in the file header to reflect that nubus-bridge is not specific to the Macintosh. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-14-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-bridge.c | 4 ++-- include/hw/nubus/mac-nubus-bridge.h | 2 +- include/hw/nubus/nubus.h | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index cd8c6a91eb..95662568c5 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -1,5 +1,5 @@ /* - * QEMU Macintosh Nubus + * QEMU Nubus * * Copyright (c) 2013-2018 Laurent Vivier * @@ -22,7 +22,7 @@ static void nubus_bridge_class_init(ObjectClass *klass, void *data) static const TypeInfo nubus_bridge_info = { .name = TYPE_NUBUS_BRIDGE, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), + .instance_size = sizeof(NubusBridge), .class_init = nubus_bridge_class_init, }; diff --git a/include/hw/nubus/mac-nubus-bridge.h b/include/hw/nubus/mac-nubus-bridge.h index 04451d357c..fa454f5fbe 100644 --- a/include/hw/nubus/mac-nubus-bridge.h +++ b/include/hw/nubus/mac-nubus-bridge.h @@ -20,7 +20,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(MacNubusState, MAC_NUBUS_BRIDGE) struct MacNubusState { - SysBusDevice sysbus_dev; + NubusBridge parent_obj; NubusBus *bus; MemoryRegion super_slot_alias; diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 9f9386afed..11bcc9bb36 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -10,6 +10,7 @@ #define HW_NUBUS_NUBUS_H #include "hw/qdev-properties.h" +#include "hw/sysbus.h" #include "exec/address-spaces.h" #include "qom/object.h" #include "qemu/units.h" @@ -32,6 +33,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(NubusDevice, NUBUS_DEVICE) OBJECT_DECLARE_SIMPLE_TYPE(NubusBus, NUBUS_BUS) #define TYPE_NUBUS_BRIDGE "nubus-bridge" +OBJECT_DECLARE_SIMPLE_TYPE(NubusBridge, NUBUS_BRIDGE); struct NubusBus { BusState qbus; @@ -58,4 +60,8 @@ struct NubusDevice { MemoryRegion decl_rom; }; +struct NubusBridge { + SysBusDevice parent_obj; +}; + #endif From f48d613484bb8969469c9edcfc0bbe410c88d645 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:02 +0100 Subject: [PATCH 0014/1334] mac-nubus-bridge: rename MacNubusState to MacNubusBridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This better reflects that the mac-nubus-bridge device is derived from the nubus-bridge device, and that the structure represents the state of the bridge device and not the Nubus itself. Also update the comment in the file header to reflect that mac-nubus-bridge is specific to the Macintosh. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-15-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/mac-nubus-bridge.c | 8 +++++--- include/hw/nubus/mac-nubus-bridge.h | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/nubus/mac-nubus-bridge.c b/hw/nubus/mac-nubus-bridge.c index 3af4f5d396..e241c581b5 100644 --- a/hw/nubus/mac-nubus-bridge.c +++ b/hw/nubus/mac-nubus-bridge.c @@ -1,5 +1,7 @@ /* - * Copyright (c) 2013-2018 Laurent Vivier + * QEMU Macintosh Nubus + * + * Copyright (c) 2013-2018 Laurent Vivier * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -13,7 +15,7 @@ static void mac_nubus_bridge_init(Object *obj) { - MacNubusState *s = MAC_NUBUS_BRIDGE(obj); + MacNubusBridge *s = MAC_NUBUS_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->bus = NUBUS_BUS(qbus_create(TYPE_NUBUS_BUS, DEVICE(s), NULL)); @@ -49,7 +51,7 @@ static const TypeInfo mac_nubus_bridge_info = { .name = TYPE_MAC_NUBUS_BRIDGE, .parent = TYPE_NUBUS_BRIDGE, .instance_init = mac_nubus_bridge_init, - .instance_size = sizeof(MacNubusState), + .instance_size = sizeof(MacNubusBridge), .class_init = mac_nubus_bridge_class_init, }; diff --git a/include/hw/nubus/mac-nubus-bridge.h b/include/hw/nubus/mac-nubus-bridge.h index fa454f5fbe..b595e1b7ef 100644 --- a/include/hw/nubus/mac-nubus-bridge.h +++ b/include/hw/nubus/mac-nubus-bridge.h @@ -17,9 +17,9 @@ #define MAC_NUBUS_SLOT_NB (MAC_NUBUS_LAST_SLOT - MAC_NUBUS_FIRST_SLOT + 1) #define TYPE_MAC_NUBUS_BRIDGE "mac-nubus-bridge" -OBJECT_DECLARE_SIMPLE_TYPE(MacNubusState, MAC_NUBUS_BRIDGE) +OBJECT_DECLARE_SIMPLE_TYPE(MacNubusBridge, MAC_NUBUS_BRIDGE) -struct MacNubusState { +struct MacNubusBridge { NubusBridge parent_obj; NubusBus *bus; From 1fa04232db5946d9648ffc03718aad9b6de87c38 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:03 +0100 Subject: [PATCH 0015/1334] nubus: move NubusBus from mac-nubus-bridge to nubus-bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that Nubus has its own address space rather than mapping directly into the system bus, move the Nubus reference from MacNubusBridge to NubusBridge. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-16-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 2 +- hw/nubus/mac-nubus-bridge.c | 11 +++++------ hw/nubus/nubus-bridge.c | 9 +++++++++ include/hw/nubus/mac-nubus-bridge.h | 1 - include/hw/nubus/nubus.h | 2 ++ 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index a07912b87c..9bdea1a362 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -398,7 +398,7 @@ static void q800_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE); - nubus = MAC_NUBUS_BRIDGE(dev)->bus; + nubus = NUBUS_BRIDGE(dev)->bus; /* framebuffer in nubus slot #9 */ diff --git a/hw/nubus/mac-nubus-bridge.c b/hw/nubus/mac-nubus-bridge.c index e241c581b5..db8640eed2 100644 --- a/hw/nubus/mac-nubus-bridge.c +++ b/hw/nubus/mac-nubus-bridge.c @@ -16,22 +16,21 @@ static void mac_nubus_bridge_init(Object *obj) { MacNubusBridge *s = MAC_NUBUS_BRIDGE(obj); + NubusBridge *nb = NUBUS_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - s->bus = NUBUS_BUS(qbus_create(TYPE_NUBUS_BUS, DEVICE(s), NULL)); - /* Macintosh only has slots 0x9 to 0xe available */ - s->bus->slot_available_mask = MAKE_64BIT_MASK(MAC_NUBUS_FIRST_SLOT, - MAC_NUBUS_SLOT_NB); + nb->bus->slot_available_mask = MAKE_64BIT_MASK(MAC_NUBUS_FIRST_SLOT, + MAC_NUBUS_SLOT_NB); /* Aliases for slots 0x9 to 0xe */ memory_region_init_alias(&s->super_slot_alias, obj, "super-slot-alias", - &s->bus->nubus_mr, + &nb->bus->nubus_mr, MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE, MAC_NUBUS_SLOT_NB * NUBUS_SUPER_SLOT_SIZE); memory_region_init_alias(&s->slot_alias, obj, "slot-alias", - &s->bus->nubus_mr, + &nb->bus->nubus_mr, NUBUS_SLOT_BASE + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE, MAC_NUBUS_SLOT_NB * NUBUS_SLOT_SIZE); diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index 95662568c5..3b68d4435c 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -12,6 +12,14 @@ #include "hw/sysbus.h" #include "hw/nubus/nubus.h" + +static void nubus_bridge_init(Object *obj) +{ + NubusBridge *s = NUBUS_BRIDGE(obj); + + s->bus = NUBUS_BUS(qbus_create(TYPE_NUBUS_BUS, DEVICE(s), NULL)); +} + static void nubus_bridge_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -22,6 +30,7 @@ static void nubus_bridge_class_init(ObjectClass *klass, void *data) static const TypeInfo nubus_bridge_info = { .name = TYPE_NUBUS_BRIDGE, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = nubus_bridge_init, .instance_size = sizeof(NubusBridge), .class_init = nubus_bridge_class_init, }; diff --git a/include/hw/nubus/mac-nubus-bridge.h b/include/hw/nubus/mac-nubus-bridge.h index b595e1b7ef..70ab50ab2d 100644 --- a/include/hw/nubus/mac-nubus-bridge.h +++ b/include/hw/nubus/mac-nubus-bridge.h @@ -22,7 +22,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(MacNubusBridge, MAC_NUBUS_BRIDGE) struct MacNubusBridge { NubusBridge parent_obj; - NubusBus *bus; MemoryRegion super_slot_alias; MemoryRegion slot_alias; }; diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 11bcc9bb36..2d00d18150 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -62,6 +62,8 @@ struct NubusDevice { struct NubusBridge { SysBusDevice parent_obj; + + NubusBus *bus; }; #endif From d585d89de172bbfaa4a2331c882c46ed186ede2a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:04 +0100 Subject: [PATCH 0016/1334] nubus-bridge: embed the NubusBus object directly within nubus-bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since nubus-bridge is a container for NubusBus then it should be embedded directly within the bridge device using qbus_create_inplace(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-17-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 2 +- hw/nubus/mac-nubus-bridge.c | 9 +++++---- hw/nubus/nubus-bridge.c | 3 ++- include/hw/nubus/nubus.h | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 9bdea1a362..074acf4fdc 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -398,7 +398,7 @@ static void q800_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE); - nubus = NUBUS_BRIDGE(dev)->bus; + nubus = &NUBUS_BRIDGE(dev)->bus; /* framebuffer in nubus slot #9 */ diff --git a/hw/nubus/mac-nubus-bridge.c b/hw/nubus/mac-nubus-bridge.c index db8640eed2..a0da5a8b2f 100644 --- a/hw/nubus/mac-nubus-bridge.c +++ b/hw/nubus/mac-nubus-bridge.c @@ -18,19 +18,20 @@ static void mac_nubus_bridge_init(Object *obj) MacNubusBridge *s = MAC_NUBUS_BRIDGE(obj); NubusBridge *nb = NUBUS_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + NubusBus *bus = &nb->bus; /* Macintosh only has slots 0x9 to 0xe available */ - nb->bus->slot_available_mask = MAKE_64BIT_MASK(MAC_NUBUS_FIRST_SLOT, - MAC_NUBUS_SLOT_NB); + bus->slot_available_mask = MAKE_64BIT_MASK(MAC_NUBUS_FIRST_SLOT, + MAC_NUBUS_SLOT_NB); /* Aliases for slots 0x9 to 0xe */ memory_region_init_alias(&s->super_slot_alias, obj, "super-slot-alias", - &nb->bus->nubus_mr, + &bus->nubus_mr, MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE, MAC_NUBUS_SLOT_NB * NUBUS_SUPER_SLOT_SIZE); memory_region_init_alias(&s->slot_alias, obj, "slot-alias", - &nb->bus->nubus_mr, + &bus->nubus_mr, NUBUS_SLOT_BASE + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE, MAC_NUBUS_SLOT_NB * NUBUS_SLOT_SIZE); diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index 3b68d4435c..1adda7f5a6 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -16,8 +16,9 @@ static void nubus_bridge_init(Object *obj) { NubusBridge *s = NUBUS_BRIDGE(obj); + NubusBus *bus = &s->bus; - s->bus = NUBUS_BUS(qbus_create(TYPE_NUBUS_BUS, DEVICE(s), NULL)); + qbus_create_inplace(bus, sizeof(s->bus), TYPE_NUBUS_BUS, DEVICE(s), NULL); } static void nubus_bridge_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 2d00d18150..63c69a7586 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -63,7 +63,7 @@ struct NubusDevice { struct NubusBridge { SysBusDevice parent_obj; - NubusBus *bus; + NubusBus bus; }; #endif From 094f5b2b09d46c57a2afbbbd6aa9d4a5213e4648 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:05 +0100 Subject: [PATCH 0017/1334] nubus-bridge: make slot_available_mask a qdev property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to allow Macintosh machines to further specify which slots are available since the number of addressable slots may not match the number of physical slots present in the machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210924073808.1041-18-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-bridge.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index 1adda7f5a6..7b51722f66 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -21,11 +21,18 @@ static void nubus_bridge_init(Object *obj) qbus_create_inplace(bus, sizeof(s->bus), TYPE_NUBUS_BUS, DEVICE(s), NULL); } +static Property nubus_bridge_properties[] = { + DEFINE_PROP_UINT16("slot-available-mask", NubusBridge, + bus.slot_available_mask, 0xffff), + DEFINE_PROP_END_OF_LIST() +}; + static void nubus_bridge_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->fw_name = "nubus"; + device_class_set_props(dc, nubus_bridge_properties); } static const TypeInfo nubus_bridge_info = { From d2cf28a0c6bcfba9d95e83ac628476e0f9cc178f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:06 +0100 Subject: [PATCH 0018/1334] nubus: add support for slot IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each Nubus slot has an IRQ line that can be used to request service from the CPU. Connect the IRQs to the Nubus bridge so that they can be wired up using qdev gpios accordingly, and introduce a new nubus_set_irq() function that can be used by Nubus devices to control the slot IRQ. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210924073808.1041-19-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/nubus/nubus-bridge.c | 2 ++ hw/nubus/nubus-device.c | 8 ++++++++ include/hw/nubus/nubus.h | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index 7b51722f66..c517a8a704 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -19,6 +19,8 @@ static void nubus_bridge_init(Object *obj) NubusBus *bus = &s->bus; qbus_create_inplace(bus, sizeof(s->bus), TYPE_NUBUS_BUS, DEVICE(s), NULL); + + qdev_init_gpio_out(DEVICE(s), bus->irqs, NUBUS_IRQS); } static Property nubus_bridge_properties[] = { diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 280f40e88a..0f1852f671 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -10,12 +10,20 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" +#include "hw/irq.h" #include "hw/loader.h" #include "hw/nubus/nubus.h" #include "qapi/error.h" #include "qemu/error-report.h" +void nubus_set_irq(NubusDevice *nd, int level) +{ + NubusBus *nubus = NUBUS_BUS(qdev_get_parent_bus(DEVICE(nd))); + + qemu_set_irq(nubus->irqs[nd->slot], level); +} + static void nubus_device_realize(DeviceState *dev, Error **errp) { NubusBus *nubus = NUBUS_BUS(qdev_get_parent_bus(dev)); diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index 63c69a7586..b3b4d2eadb 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -26,6 +26,8 @@ #define NUBUS_LAST_SLOT 0xf #define NUBUS_SLOT_NB (NUBUS_LAST_SLOT - NUBUS_FIRST_SLOT + 1) +#define NUBUS_IRQS 16 + #define TYPE_NUBUS_DEVICE "nubus-device" OBJECT_DECLARE_SIMPLE_TYPE(NubusDevice, NUBUS_DEVICE) @@ -45,6 +47,8 @@ struct NubusBus { MemoryRegion slot_io; uint16_t slot_available_mask; + + qemu_irq irqs[NUBUS_IRQS]; }; #define NUBUS_DECL_ROM_MAX_SIZE (128 * KiB) @@ -60,6 +64,8 @@ struct NubusDevice { MemoryRegion decl_rom; }; +void nubus_set_irq(NubusDevice *nd, int level); + struct NubusBridge { SysBusDevice parent_obj; From b297843ef5666c094c153a2a7d78c6b010dd9154 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:07 +0100 Subject: [PATCH 0019/1334] q800: wire up nubus IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nubus IRQs are routed to the CPU through the VIA2 device so wire up the IRQs using gpios accordingly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-20-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 074acf4fdc..5bc9df58a0 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -398,6 +398,12 @@ static void q800_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE); + for (i = 0; i < VIA2_NUBUS_IRQ_NB; i++) { + qdev_connect_gpio_out(dev, 9 + i, + qdev_get_gpio_in_named(via2_dev, "nubus-irq", + VIA2_NUBUS_IRQ_9 + i)); + } + nubus = &NUBUS_BRIDGE(dev)->bus; /* framebuffer in nubus slot #9 */ From 5ef251416b5116bbf7723f31ddf8a6949a2ac271 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 24 Sep 2021 08:38:08 +0100 Subject: [PATCH 0020/1334] q800: configure nubus available slots for Quadra 800 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slot 0x9 is reserved for use by the in-built framebuffer whilst only slots 0xc, 0xd and 0xe physically exist on the Quadra 800. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210924073808.1041-21-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 5bc9df58a0..09b3366024 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -78,6 +78,13 @@ #define MAC_CLOCK 3686418 +/* + * Slot 0x9 is reserved for use by the in-built framebuffer whilst only + * slots 0xc, 0xd and 0xe physically exist on the Quadra 800 + */ +#define Q800_NUBUS_SLOTS_AVAILABLE (BIT(0x9) | BIT(0xc) | BIT(0xd) | \ + BIT(0xe)) + /* * The GLUE (General Logic Unit) is an Apple custom integrated circuit chip * that performs a variety of functions (RAM management, clock generation, ...). @@ -392,6 +399,8 @@ static void q800_init(MachineState *machine) /* NuBus */ dev = qdev_new(TYPE_MAC_NUBUS_BRIDGE); + qdev_prop_set_uint32(dev, "slot-available-mask", + Q800_NUBUS_SLOTS_AVAILABLE); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE); From 06c0259a086f0f4ddd57a14ba811bba0b9e45130 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 10 Sep 2021 08:26:03 -0300 Subject: [PATCH 0021/1334] host-utils: Fix overflow detection in divu128() The previous code didn't detect overflows if the high 64-bit of the dividend were equal to the 64-bit divisor. In that case, 64 bits wouldn't be enough to hold the quotient. Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20210910112624.72748-2-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- util/host-utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/host-utils.c b/util/host-utils.c index 7b9322071d..a789a11b46 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -102,7 +102,7 @@ int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) *plow = dlo / divisor; *phigh = dlo % divisor; return 0; - } else if (dhi > divisor) { + } else if (dhi >= divisor) { return 1; } else { From 4ff2a971f4dd533140c01607a0c776ba8d837bc7 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 10 Sep 2021 08:26:04 -0300 Subject: [PATCH 0022/1334] host-utils: fix missing zero-extension in divs128 *plow (lower 64 bits of the dividend) is passed into divs128() as a signed 64-bit integer. When building an __int128_t from it, it must be zero-extended, instead of sign-extended. Suggested-by: Richard Henderson Signed-off-by: Luis Pires Message-Id: <20210910112624.72748-3-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- include/qemu/host-utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 711b221704..753b9fb89f 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -70,7 +70,7 @@ static inline int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) if (divisor == 0) { return 1; } else { - __int128_t dividend = ((__int128_t)*phigh << 64) | *plow; + __int128_t dividend = ((__int128_t)*phigh << 64) | (uint64_t)*plow; __int128_t result = dividend / divisor; *plow = result; *phigh = dividend % divisor; From d03bba0bfbe6dcacc9d7be2b664d70fde081cc47 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 10 Sep 2021 08:26:05 -0300 Subject: [PATCH 0023/1334] host-utils: introduce uabs64() Introduce uabs64(), a function that returns the absolute value of a 64-bit int as an unsigned value. This avoids the undefined behavior for common abs implementations, where abs of the most negative value is undefined. Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Reviewed-by: Eduardo Habkost Message-Id: <20210910112624.72748-4-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- include/qemu/host-utils.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 753b9fb89f..ca9f3f021b 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -357,6 +357,14 @@ static inline uint64_t revbit64(uint64_t x) #endif } +/** + * Return the absolute value of a 64-bit integer as an unsigned 64-bit value + */ +static inline uint64_t uabs64(int64_t v) +{ + return v < 0 ? -v : v; +} + /** * sadd32_overflow - addition with overflow indication * @x, @y: addends From bb89646c75e46db0074e263d74770dac6f0a29fc Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 10 Sep 2021 08:26:06 -0300 Subject: [PATCH 0024/1334] i386/kvm: Replace abs64() with uabs64() from host-utils Drop abs64() and use uabs64() from host-utils, which avoids an undefined behavior when taking abs of the most negative value. Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Reviewed-by: Eduardo Habkost Message-Id: <20210910112624.72748-5-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- hw/i386/kvm/i8254.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index fa68669e8a..191a26fa57 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -59,11 +59,6 @@ struct KVMPITClass { DeviceRealize parent_realize; }; -static int64_t abs64(int64_t v) -{ - return v < 0 ? -v : v; -} - static void kvm_pit_update_clock_offset(KVMPITState *s) { int64_t offset, clock_offset; @@ -81,7 +76,7 @@ static void kvm_pit_update_clock_offset(KVMPITState *s) clock_gettime(CLOCK_MONOTONIC, &ts); offset -= ts.tv_nsec; offset -= (int64_t)ts.tv_sec * 1000000000; - if (abs64(offset) < abs64(clock_offset)) { + if (uabs64(offset) < uabs64(clock_offset)) { clock_offset = offset; } } From 3ad2111175dddb4e396c25ce242a5c7763d42631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 1 Sep 2021 11:41:47 +0200 Subject: [PATCH 0025/1334] ppc/spapr: Add a POWER10 DD2 CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Message-Id: <20210901094153.227671-3-clg@kaod.org> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 4f316a6f9d..58e7341cb7 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -382,6 +382,7 @@ static const TypeInfo spapr_cpu_core_type_infos[] = { DEFINE_SPAPR_CPU_CORE_TYPE("power9_v1.0"), DEFINE_SPAPR_CPU_CORE_TYPE("power9_v2.0"), DEFINE_SPAPR_CPU_CORE_TYPE("power10_v1.0"), + DEFINE_SPAPR_CPU_CORE_TYPE("power10_v2.0"), #ifdef CONFIG_KVM DEFINE_SPAPR_CPU_CORE_TYPE("host"), #endif From 40ef88ba77ac84baab3a78171a49829a6cd1d3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 1 Sep 2021 11:41:48 +0200 Subject: [PATCH 0026/1334] ppc/pnv: Add a comment on the "primary-topology-index" property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On P10, the chip id is calculated from the "Primary topology table index". See skiboot commits for more information [1]. This information is extracted from the hdata on real systems which QEMU needs to emulate. Add this property for all machines even if it is only used on POWER10. [1] https://github.com/open-power/skiboot/commit/2ce3f083f399 https://github.com/open-power/skiboot/commit/a2d4d7f9e14a Signed-off-by: Cédric Le Goater Message-Id: <20210901094153.227671-4-clg@kaod.org> Signed-off-by: David Gibson --- hw/ppc/pnv_xscom.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index faa488e311..9ce018dbc2 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -284,6 +284,10 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, _FDT(xscom_offset); g_free(name); _FDT((fdt_setprop_cell(fdt, xscom_offset, "ibm,chip-id", chip->chip_id))); + /* + * On P10, the xscom bus id has been deprecated and the chip id is + * calculated from the "Primary topology table index". See skiboot. + */ _FDT((fdt_setprop_cell(fdt, xscom_offset, "ibm,primary-topology-index", chip->chip_id))); _FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1))); From 0e5e9ff455dc529ee9e3c485d4543cdcaa04ab8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 1 Sep 2021 11:41:49 +0200 Subject: [PATCH 0027/1334] ppc/pnv: Remove useless variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Message-Id: <20210901094153.227671-5-clg@kaod.org> Signed-off-by: David Gibson --- hw/ppc/pnv.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 2f5358b70c..a62e90b15e 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -838,8 +838,7 @@ static void pnv_init(MachineState *machine) for (i = 0; i < pnv->num_chips; i++) { char chip_name[32]; Object *chip = OBJECT(qdev_new(chip_typename)); - int chip_id = i; - uint64_t chip_ram_size = pnv_chip_get_ram_size(pnv, chip_id); + uint64_t chip_ram_size = pnv_chip_get_ram_size(pnv, i); pnv->chips[i] = PNV_CHIP(chip); @@ -850,9 +849,9 @@ static void pnv_init(MachineState *machine) &error_fatal); chip_ram_start += chip_ram_size; - snprintf(chip_name, sizeof(chip_name), "chip[%d]", chip_id); + snprintf(chip_name, sizeof(chip_name), "chip[%d]", i); object_property_add_child(OBJECT(pnv), chip_name, chip); - object_property_set_int(chip, "chip-id", chip_id, &error_fatal); + object_property_set_int(chip, "chip-id", i, &error_fatal); object_property_set_int(chip, "nr-cores", machine->smp.cores, &error_fatal); object_property_set_int(chip, "nr-threads", machine->smp.threads, From 89d2468d964e635eb80d4d00c29074a28d0e6d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 1 Sep 2021 11:41:51 +0200 Subject: [PATCH 0028/1334] ppc/xive: Export priority_to_ipb() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Message-Id: <20210901094153.227671-7-clg@kaod.org> Signed-off-by: David Gibson --- hw/intc/xive.c | 21 ++++++--------------- include/hw/ppc/xive.h | 11 +++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index b817ee8e37..b0c4f76b1d 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -27,17 +27,6 @@ * XIVE Thread Interrupt Management context */ -/* - * Convert a priority number to an Interrupt Pending Buffer (IPB) - * register, which indicates a pending interrupt at the priority - * corresponding to the bit number - */ -static uint8_t priority_to_ipb(uint8_t priority) -{ - return priority > XIVE_PRIORITY_MAX ? - 0 : 1 << (XIVE_PRIORITY_MAX - priority); -} - /* * Convert an Interrupt Pending Buffer (IPB) register to a Pending * Interrupt Priority Register (PIPR), which contains the priority of @@ -89,7 +78,7 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) regs[TM_CPPR] = cppr; /* Reset the pending buffer bit */ - regs[TM_IPB] &= ~priority_to_ipb(cppr); + regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]); /* Drop Exception bit */ @@ -353,7 +342,7 @@ static void xive_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { - xive_tctx_ipb_update(tctx, TM_QW1_OS, priority_to_ipb(value & 0xff)); + xive_tctx_ipb_update(tctx, TM_QW1_OS, xive_priority_to_ipb(value & 0xff)); } static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk, @@ -1535,7 +1524,8 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, /* handle CPU exception delivery */ if (count) { trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring); - xive_tctx_ipb_update(match.tctx, match.ring, priority_to_ipb(priority)); + xive_tctx_ipb_update(match.tctx, match.ring, + xive_priority_to_ipb(priority)); } return !!count; @@ -1682,7 +1672,8 @@ static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk, * use. The presenter will resend the interrupt when the vCPU * is dispatched again on a HW thread. */ - ipb = xive_get_field32(NVT_W4_IPB, nvt.w4) | priority_to_ipb(priority); + ipb = xive_get_field32(NVT_W4_IPB, nvt.w4) | + xive_priority_to_ipb(priority); nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, ipb); xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4); diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index db76411654..29b130eaea 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -458,6 +458,17 @@ struct XiveENDSource { */ #define XIVE_PRIORITY_MAX 7 +/* + * Convert a priority number to an Interrupt Pending Buffer (IPB) + * register, which indicates a pending interrupt at the priority + * corresponding to the bit number + */ +static inline uint8_t xive_priority_to_ipb(uint8_t priority) +{ + return priority > XIVE_PRIORITY_MAX ? + 0 : 1 << (XIVE_PRIORITY_MAX - priority); +} + /* * XIVE Thread Interrupt Management Aera (TIMA) * From daf115cf9a42bb42eabaf6177e59084c22cc7b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 1 Sep 2021 11:41:52 +0200 Subject: [PATCH 0029/1334] ppc/xive: Export xive_tctx_word2() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Message-Id: <20210901094153.227671-8-clg@kaod.org> Signed-off-by: David Gibson --- hw/intc/xive.c | 5 ----- include/hw/ppc/xive.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index b0c4f76b1d..6c82326ec7 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -141,11 +141,6 @@ void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb) xive_tctx_notify(tctx, ring); } -static inline uint32_t xive_tctx_word2(uint8_t *ring) -{ - return *((uint32_t *) &ring[TM_WORD2]); -} - /* * XIVE Thread Interrupt Management Area (TIMA) */ diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 29b130eaea..252c58a1d6 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -335,6 +335,11 @@ struct XiveTCTX { XivePresenter *xptr; }; +static inline uint32_t xive_tctx_word2(uint8_t *ring) +{ + return *((uint32_t *) &ring[TM_WORD2]); +} + /* * XIVE Router */ From 92612f1550fb11c2bda09821bad11c6349c283cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 1 Sep 2021 11:41:53 +0200 Subject: [PATCH 0030/1334] ppc/pnv: Rename "id" to "quad-id" in PnvQuad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This to avoid possible conflicts with the "id" property of QOM objects. Signed-off-by: Cédric Le Goater Message-Id: <20210901094153.227671-9-clg@kaod.org> Signed-off-by: David Gibson --- hw/ppc/pnv.c | 4 ++-- hw/ppc/pnv_core.c | 4 ++-- include/hw/ppc/pnv_core.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a62e90b15e..03c86508d2 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1368,10 +1368,10 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) sizeof(*eq), TYPE_PNV_QUAD, &error_fatal, NULL); - object_property_set_int(OBJECT(eq), "id", core_id, &error_fatal); + object_property_set_int(OBJECT(eq), "quad-id", core_id, &error_fatal); qdev_realize(DEVICE(eq), NULL, &error_fatal); - pnv_xscom_add_subregion(chip, PNV9_XSCOM_EQ_BASE(eq->id), + pnv_xscom_add_subregion(chip, PNV9_XSCOM_EQ_BASE(eq->quad_id), &eq->xscom_regs); } } diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 4de8414df2..19e8eb885f 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -407,13 +407,13 @@ static void pnv_quad_realize(DeviceState *dev, Error **errp) PnvQuad *eq = PNV_QUAD(dev); char name[32]; - snprintf(name, sizeof(name), "xscom-quad.%d", eq->id); + snprintf(name, sizeof(name), "xscom-quad.%d", eq->quad_id); pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev), &pnv_quad_xscom_ops, eq, name, PNV9_XSCOM_EQ_SIZE); } static Property pnv_quad_properties[] = { - DEFINE_PROP_UINT32("id", PnvQuad, id, 0), + DEFINE_PROP_UINT32("quad-id", PnvQuad, quad_id, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index 6ecee98a76..c22eab2e1f 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -67,7 +67,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PnvQuad, PNV_QUAD) struct PnvQuad { DeviceState parent_obj; - uint32_t id; + uint32_t quad_id; MemoryRegion xscom_regs; }; #endif /* PPC_PNV_CORE_H */ From 325ba52a4e53fac6db65fcb3bd995e8e56a89eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 2 Sep 2021 15:09:09 +0200 Subject: [PATCH 0031/1334] docs/system: ppc: Update the URL for OpenPOWER firmware images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also fixes a small skiboot/skiroot typo and removes the links to the specific POWER8 and POWER9 images since the firmware images can be used to run all machines. Signed-off-by: Cédric Le Goater Message-Id: <20210902130928.528803-2-clg@kaod.org> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- docs/system/ppc/powernv.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/system/ppc/powernv.rst b/docs/system/ppc/powernv.rst index 4c4cdea527..86186b7d2c 100644 --- a/docs/system/ppc/powernv.rst +++ b/docs/system/ppc/powernv.rst @@ -53,8 +53,7 @@ initramfs ``skiroot``. Source code can be found on GitHub: https://github.com/open-power. -Prebuilt images of ``skiboot`` and ``skiboot`` are made available on the `OpenPOWER `__ site. To boot a POWER9 machine, use the `witherspoon `__ images. For POWER8, use -the `palmetto `__ images. +Prebuilt images of ``skiboot`` and ``skiroot`` are made available on the `OpenPOWER `__ site. QEMU includes a prebuilt image of ``skiboot`` which is updated when a more recent version is required by the models. From f640afec1a14e99d561f7da93e3fc8a5e1206384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 2 Sep 2021 15:09:10 +0200 Subject: [PATCH 0032/1334] ppc/pnv: Add an assert when calculating the RAM distribution on chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Message-Id: <20210902130928.528803-3-clg@kaod.org> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/pnv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 03c86508d2..71e45515f1 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -723,6 +723,8 @@ static uint64_t pnv_chip_get_ram_size(PnvMachineState *pnv, int chip_id) return QEMU_ALIGN_DOWN(ram_per_chip, 1 * MiB); } + assert(pnv->num_chips > 1); + ram_per_chip = (machine->ram_size - 1 * GiB) / (pnv->num_chips - 1); return chip_id == 0 ? 1 * GiB : QEMU_ALIGN_DOWN(ram_per_chip, 1 * MiB); } From a3d67f3e5d5bfe480eeb83d24546191b681b7c38 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Mon, 23 Aug 2021 12:02:35 -0300 Subject: [PATCH 0033/1334] target/ppc: fix setting of CR flags in bcdcfsq According to the ISA, CR should be set based on the source value, and not on the packed decimal result. The way this was implemented would cause GT, LT and EQ to be set incorrectly when the source value was too large and the 31 least significant digits of the packed decimal result ended up being all zero. This would happen for source values of +/-10^31, +/-10^32, etc. The new implementation fixes this and also skips the result calculation altogether in case of src overflow. Signed-off-by: Luis Pires Message-Id: <20210823150235.35759-1-luis.pires@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/int_helper.c | 67 +++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index c2d3248d1e..f5dac3aa87 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -2480,10 +2480,26 @@ uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) return cr; } +/** + * Compare 2 128-bit unsigned integers, passed in as unsigned 64-bit pairs + * + * Returns: + * > 0 if ahi|alo > bhi|blo, + * 0 if ahi|alo == bhi|blo, + * < 0 if ahi|alo < bhi|blo + */ +static inline int ucmp128(uint64_t alo, uint64_t ahi, + uint64_t blo, uint64_t bhi) +{ + return (ahi == bhi) ? + (alo > blo ? 1 : (alo == blo ? 0 : -1)) : + (ahi > bhi ? 1 : -1); +} + uint32_t helper_bcdcfsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) { int i; - int cr = 0; + int cr; uint64_t lo_value; uint64_t hi_value; ppc_avr_t ret = { .u64 = { 0, 0 } }; @@ -2492,29 +2508,48 @@ uint32_t helper_bcdcfsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) lo_value = -b->VsrSD(1); hi_value = ~b->VsrD(0) + !lo_value; bcd_put_digit(&ret, 0xD, 0); + + cr = CRF_LT; } else { lo_value = b->VsrD(1); hi_value = b->VsrD(0); bcd_put_digit(&ret, bcd_preferred_sgn(0, ps), 0); + + if (hi_value == 0 && lo_value == 0) { + cr = CRF_EQ; + } else { + cr = CRF_GT; + } } - if (divu128(&lo_value, &hi_value, 1000000000000000ULL) || - lo_value > 9999999999999999ULL) { - cr = CRF_SO; + /* + * Check src limits: abs(src) <= 10^31 - 1 + * + * 10^31 - 1 = 0x0000007e37be2022 c0914b267fffffff + */ + if (ucmp128(lo_value, hi_value, + 0xc0914b267fffffffULL, 0x7e37be2022ULL) > 0) { + cr |= CRF_SO; + + /* + * According to the ISA, if src wouldn't fit in the destination + * register, the result is undefined. + * In that case, we leave r unchanged. + */ + } else { + divu128(&lo_value, &hi_value, 1000000000000000ULL); + + for (i = 1; i < 16; hi_value /= 10, i++) { + bcd_put_digit(&ret, hi_value % 10, i); + } + + for (; i < 32; lo_value /= 10, i++) { + bcd_put_digit(&ret, lo_value % 10, i); + } + + *r = ret; } - for (i = 1; i < 16; hi_value /= 10, i++) { - bcd_put_digit(&ret, hi_value % 10, i); - } - - for (; i < 32; lo_value /= 10, i++) { - bcd_put_digit(&ret, lo_value % 10, i); - } - - cr |= bcd_cmp_zero(&ret); - - *r = ret; - return cr; } From 99b2c0622513fc98e8ed9dac56cafb6d29240e87 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Sep 2021 21:47:49 -0300 Subject: [PATCH 0034/1334] memory_hotplug.c: handle dev->id = NULL in acpi_memory_hotplug_write() qapi_event_send_mem_unplug_error() deals with @device being NULL by replacing it with an empty string ("") when emitting the event. Aside from the fact that this behavior (qapi visitor mapping NULL pointer to "") can be patched/changed someday, there's also the lack of utility that the event brings to listeners, e.g. "a memory unplug error happened somewhere". In theory we should just avoit emitting this event at all if dev->id is NULL, but this would be an incompatible change to existing guests. Instead, let's make the forementioned behavior explicit: if dev->id is NULL, pass an empty string to qapi_event_send_mem_unplug_error(). Suggested-by: Markus Armbruster Reviewed-by: Igor Mammedov Reviewed-by: Greg Kurz Reviewed-by: David Gibson Reviewed-by: Markus Armbruster Signed-off-by: Daniel Henrique Barboza Message-Id: <20210907004755.424931-2-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/acpi/memory_hotplug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index af37889423..6a71de408b 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -178,7 +178,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, hotplug_handler_unplug(hotplug_ctrl, dev, &local_err); if (local_err) { trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector); - qapi_event_send_mem_unplug_error(dev->id, + qapi_event_send_mem_unplug_error(dev->id ? : "", error_get_pretty(local_err)); error_free(local_err); break; From 44d886abab268043157b08b77147cc29bff22090 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Sep 2021 21:47:50 -0300 Subject: [PATCH 0035/1334] spapr.c: handle dev->id in spapr_memory_unplug_rollback() As done in hw/acpi/memory_hotplug.c, pass an empty string if dev->id is NULL to qapi_event_send_mem_unplug_error() to avoid relying on a behavior that can be changed in the future. Suggested-by: Markus Armbruster Reviewed-by: Greg Kurz Reviewed-by: David Gibson Reviewed-by: Markus Armbruster Signed-off-by: Daniel Henrique Barboza Message-Id: <20210907004755.424931-3-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d39fd4e644..ac11c8a728 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3690,7 +3690,7 @@ void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev) */ qapi_error = g_strdup_printf("Memory hotunplug rejected by the guest " "for device %s", dev->id); - qapi_event_send_mem_unplug_error(dev->id, qapi_error); + qapi_event_send_mem_unplug_error(dev->id ? : "", qapi_error); } /* Callback to be called during DRC release. */ From 91bd95ce162cd34ba29e9f6470ecdcaaaf1d768d Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Sep 2021 21:47:51 -0300 Subject: [PATCH 0036/1334] spapr_drc.c: do not error_report() when drc->dev->id == NULL The error_report() call in drc_unisolate_logical() is not considering that drc->dev->id can be NULL, and the underlying functions error_report() calls to do its job (vprintf(), g_strdup_printf() ...) has undefined behavior when trying to handle "%s" with NULL arguments. Besides, there is no utility into reporting that an unknown device was rejected by the guest. Acked-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Markus Armbruster Signed-off-by: Daniel Henrique Barboza Message-Id: <20210907004755.424931-4-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr_drc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index a2f2634601..a4d9496f76 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -167,8 +167,11 @@ static uint32_t drc_unisolate_logical(SpaprDrc *drc) } drc->unplug_requested = false; - error_report("Device hotunplug rejected by the guest " - "for device %s", drc->dev->id); + + if (drc->dev->id) { + error_report("Device hotunplug rejected by the guest " + "for device %s", drc->dev->id); + } /* * TODO: send a QAPI DEVICE_UNPLUG_ERROR event when From a5bc19c542aa2416292b820cf22ae9afa6f25916 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Sep 2021 21:47:52 -0300 Subject: [PATCH 0037/1334] qapi/qdev.json: fix DEVICE_DELETED parameters doc Clarify that @device is optional and that 'path' is the device path from QOM. This change follows Markus' suggestion verbatim, provided in full context here: https://lists.gnu.org/archive/html/qemu-devel/2021-07/msg01891.html Suggested-by: Markus Armbruster Reviewed-by: Greg Kurz Reviewed-by: Markus Armbruster Reviewed-by: David Gibson Signed-off-by: Daniel Henrique Barboza Message-Id: <20210907004755.424931-5-danielhb413@gmail.com> Signed-off-by: David Gibson --- qapi/qdev.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qapi/qdev.json b/qapi/qdev.json index b83178220b..0e9cb2ae88 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -108,9 +108,9 @@ # At this point, it's safe to reuse the specified device ID. Device removal can # be initiated by the guest or by HMP/QMP commands. # -# @device: device name +# @device: the device's ID if it has one # -# @path: device path +# @path: the device's QOM path # # Since: 1.5 # From 09615257058a0ae87b837bb041f56f7312d9ead8 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Fri, 13 Aug 2021 23:55:19 +0300 Subject: [PATCH 0038/1334] qemu-nbd: Change default cache mode to writeback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both qemu and qemu-img use writeback cache mode by default, which is already documented in qemu(1). qemu-nbd uses writethrough cache mode by default, and the default cache mode is not documented. According to the qemu-nbd(8): --cache=CACHE The cache mode to be used with the file. See the documentation of the emulator's -drive cache=... option for allowed values. qemu(1) says: The default mode is cache=writeback. So users have no reason to assume that qemu-nbd is using writethough cache mode. The only hint is the painfully slow writing when using the defaults. Looking in git history, it seems that qemu used writethrough in the past to support broken guests that did not flush data properly, or could not flush due to limitations in qemu. But qemu-nbd clients can use NBD_CMD_FLUSH to flush data, so using writethrough does not help anyone. Change the default cache mode to writback, and document the default and available values properly in the online help and manual. With this change converting image via qemu-nbd is 3.5 times faster. $ qemu-img create dst.img 50g $ qemu-nbd -t -f raw -k /tmp/nbd.sock dst.img Before this change: $ hyperfine -r3 "./qemu-img convert -p -f raw -O raw -T none -W fedora34.img nbd+unix:///?socket=/tmp/nbd.sock" Benchmark #1: ./qemu-img convert -p -f raw -O raw -T none -W fedora34.img nbd+unix:///?socket=/tmp/nbd.sock Time (mean ± σ): 83.639 s ± 5.970 s [User: 2.733 s, System: 6.112 s] Range (min … max): 76.749 s … 87.245 s 3 runs After this change: $ hyperfine -r3 "./qemu-img convert -p -f raw -O raw -T none -W fedora34.img nbd+unix:///?socket=/tmp/nbd.sock" Benchmark #1: ./qemu-img convert -p -f raw -O raw -T none -W fedora34.img nbd+unix:///?socket=/tmp/nbd.sock Time (mean ± σ): 23.522 s ± 0.433 s [User: 2.083 s, System: 5.475 s] Range (min … max): 23.234 s … 24.019 s 3 runs Users can avoid the issue by using --cache=writeback[1] but the defaults should give good performance for the common use case. [1] https://bugzilla.redhat.com/1990656 Signed-off-by: Nir Soffer Message-Id: <20210813205519.50518-1-nsoffer@redhat.com> Reviewed-by: Eric Blake CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake --- docs/tools/qemu-nbd.rst | 6 ++++-- qemu-nbd.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst index e39a9f4b1a..56e54cd441 100644 --- a/docs/tools/qemu-nbd.rst +++ b/docs/tools/qemu-nbd.rst @@ -99,8 +99,10 @@ driver options if ``--image-opts`` is specified. .. option:: --cache=CACHE - The cache mode to be used with the file. See the documentation of - the emulator's ``-drive cache=...`` option for allowed values. + The cache mode to be used with the file. Valid values are: + ``none``, ``writeback`` (the default), ``writethrough``, + ``directsync`` and ``unsafe``. See the documentation of + the emulator's ``-drive cache=...`` option for more info. .. option:: -n, --nocache diff --git a/qemu-nbd.c b/qemu-nbd.c index 65ebec598f..9d895ba24b 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -135,7 +135,9 @@ static void usage(const char *name) " 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" " '[ID_OR_NAME]'\n" " -n, --nocache disable host cache\n" -" --cache=MODE set cache mode (none, writeback, ...)\n" +" --cache=MODE set cache mode used to access the disk image, the\n" +" valid options are: 'none', 'writeback' (default),\n" +" 'writethrough', 'directsync' and 'unsafe'\n" " --aio=MODE set AIO mode (native, io_uring or threads)\n" " --discard=MODE set discard mode (ignore, unmap)\n" " --detect-zeroes=MODE set detect-zeroes mode (off, on, unmap)\n" @@ -552,7 +554,7 @@ int main(int argc, char **argv) bool alloc_depth = false; const char *tlscredsid = NULL; bool imageOpts = false; - bool writethrough = true; + bool writethrough = false; /* Client will flush as needed. */ bool fork_process = false; bool list = false; int old_stderr = -1; From b984b2968b415759307558493b1a2bb6070a2251 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:27:57 +0300 Subject: [PATCH 0039/1334] block/io: bring request check to bdrv_co_(read,write)v_vmstate Only qcow2 driver supports vmstate. In qcow2 these requests go through .bdrv_co_p{read,write}v_part handlers. So, let's do our basic check for the request on vmstate generic handlers. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Message-Id: <20210903102807.27127-2-vsementsov@virtuozzo.com> Signed-off-by: Eric Blake --- block/io.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/block/io.c b/block/io.c index 99ee182ca4..58602f84db 100644 --- a/block/io.c +++ b/block/io.c @@ -2810,7 +2810,12 @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { BlockDriver *drv = bs->drv; BlockDriverState *child_bs = bdrv_primary_bs(bs); - int ret = -ENOTSUP; + int ret; + + ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); + if (ret < 0) { + return ret; + } if (!drv) { return -ENOMEDIUM; @@ -2822,6 +2827,8 @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ret = drv->bdrv_load_vmstate(bs, qiov, pos); } else if (child_bs) { ret = bdrv_co_readv_vmstate(child_bs, qiov, pos); + } else { + ret = -ENOTSUP; } bdrv_dec_in_flight(bs); @@ -2834,7 +2841,12 @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { BlockDriver *drv = bs->drv; BlockDriverState *child_bs = bdrv_primary_bs(bs); - int ret = -ENOTSUP; + int ret; + + ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); + if (ret < 0) { + return ret; + } if (!drv) { return -ENOMEDIUM; @@ -2846,6 +2858,8 @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ret = drv->bdrv_save_vmstate(bs, qiov, pos); } else if (child_bs) { ret = bdrv_co_writev_vmstate(child_bs, qiov, pos); + } else { + ret = -ENOTSUP; } bdrv_dec_in_flight(bs); From 558902cc3dc61231930001b82dcd95d20d58b417 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:27:58 +0300 Subject: [PATCH 0040/1334] qcow2: check request on vmstate save/load path We modify the request by adding an offset to vmstate. Let's check the modified request. It will help us to safely move .bdrv_co_preadv_part and .bdrv_co_pwritev_part to int64_t type of offset and bytes. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Message-Id: <20210903102807.27127-3-vsementsov@virtuozzo.com> Signed-off-by: Eric Blake --- block/io.c | 6 +++--- block/qcow2.c | 43 +++++++++++++++++++++++++++++++++------ include/block/block_int.h | 3 +++ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/block/io.c b/block/io.c index 58602f84db..a4f124f755 100644 --- a/block/io.c +++ b/block/io.c @@ -956,9 +956,9 @@ bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, return waited; } -static int bdrv_check_qiov_request(int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - Error **errp) +int bdrv_check_qiov_request(int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + Error **errp) { /* * Check generic offset/bytes correctness diff --git a/block/qcow2.c b/block/qcow2.c index 02f9f3e636..1c3cf7f91d 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5227,24 +5227,55 @@ static int qcow2_has_zero_init(BlockDriverState *bs) } } +/* + * Check the request to vmstate. On success return + * qcow2_vm_state_offset(bs) + @pos + */ +static int64_t qcow2_check_vmstate_request(BlockDriverState *bs, + QEMUIOVector *qiov, int64_t pos) +{ + BDRVQcow2State *s = bs->opaque; + int64_t vmstate_offset = qcow2_vm_state_offset(s); + int ret; + + /* Incoming requests must be OK */ + bdrv_check_qiov_request(pos, qiov->size, qiov, 0, &error_abort); + + if (INT64_MAX - pos < vmstate_offset) { + return -EIO; + } + + pos += vmstate_offset; + ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); + if (ret < 0) { + return ret; + } + + return pos; +} + static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { - BDRVQcow2State *s = bs->opaque; + int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); + if (offset < 0) { + return offset; + } BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); - return bs->drv->bdrv_co_pwritev_part(bs, qcow2_vm_state_offset(s) + pos, - qiov->size, qiov, 0, 0); + return bs->drv->bdrv_co_pwritev_part(bs, offset, qiov->size, qiov, 0, 0); } static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { - BDRVQcow2State *s = bs->opaque; + int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); + if (offset < 0) { + return offset; + } BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); - return bs->drv->bdrv_co_preadv_part(bs, qcow2_vm_state_offset(s) + pos, - qiov->size, qiov, 0, 0); + return bs->drv->bdrv_co_preadv_part(bs, offset, qiov->size, qiov, 0, 0); } /* diff --git a/include/block/block_int.h b/include/block/block_int.h index 5451f89b8d..ed60495938 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -94,6 +94,9 @@ typedef struct BdrvTrackedRequest { struct BdrvTrackedRequest *waiting_for; } BdrvTrackedRequest; +int bdrv_check_qiov_request(int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + Error **errp); int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp); struct BlockDriver { From f7ef38dd1310d7d9db76d0aa16899cbc5744f36d Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:27:59 +0300 Subject: [PATCH 0041/1334] block: use int64_t instead of uint64_t in driver read handlers We are generally moving to int64_t for both offset and bytes parameters on all io paths. Main motivation is realization of 64-bit write_zeroes operation for fast zeroing large disk chunks, up to the whole disk. We chose signed type, to be consistent with off_t (which is signed) and with possibility for signed return type (where negative value means error). So, convert driver read handlers parameters which are already 64bit to signed type. While being here, convert also flags parameter to be BdrvRequestFlags. Now let's consider all callers. Simple git grep '\->bdrv_\(aio\|co\)_preadv\(_part\)\?' shows that's there three callers of driver function: bdrv_driver_preadv() in block/io.c, passes int64_t, checked by bdrv_check_qiov_request() to be non-negative. qcow2_load_vmstate() does bdrv_check_qiov_request(). do_perform_cow_read() has uint64_t argument. And a lot of things in qcow2 driver are uint64_t, so converting it is big job. But we must not work with requests that don't satisfy bdrv_check_qiov_request(), so let's just assert it here. Still, the functions may be called directly, not only by drv->... Let's check: git grep '\.bdrv_\(aio\|co\)_preadv\(_part\)\?\s*=' | \ awk '{print $4}' | sed 's/,//' | sed 's/&//' | sort | uniq | \ while read func; do git grep "$func(" | \ grep -v "$func(BlockDriverState"; done The only one such caller: QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1); ... ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0); in tests/unit/test-bdrv-drain.c, and it's OK obviously. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210903102807.27127-4-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: fix typos] Signed-off-by: Eric Blake --- block/blkdebug.c | 4 ++-- block/blklogwrites.c | 4 ++-- block/blkreplay.c | 2 +- block/blkverify.c | 4 ++-- block/bochs.c | 4 ++-- block/cloop.c | 4 ++-- block/commit.c | 2 +- block/copy-before-write.c | 4 ++-- block/copy-on-read.c | 4 ++-- block/crypto.c | 4 ++-- block/curl.c | 3 ++- block/dmg.c | 4 ++-- block/file-posix.c | 6 +++--- block/file-win32.c | 4 ++-- block/filter-compress.c | 4 ++-- block/mirror.c | 2 +- block/nbd.c | 5 +++-- block/nfs.c | 6 +++--- block/null.c | 9 +++++---- block/nvme.c | 5 +++-- block/preallocate.c | 4 ++-- block/qcow.c | 6 +++--- block/qcow2-cluster.c | 14 +++++++++++++- block/qcow2.c | 5 +++-- block/quorum.c | 4 ++-- block/raw-format.c | 20 ++++++++++---------- block/rbd.c | 6 +++--- block/throttle.c | 5 +++-- block/vdi.c | 4 ++-- block/vmdk.c | 4 ++-- block/vpc.c | 4 ++-- block/vvfat.c | 4 ++-- include/block/block_int.h | 11 ++++++----- tests/unit/test-bdrv-drain.c | 16 +++++++++------- tests/unit/test-block-iothread.c | 19 ++++++++++++++----- 35 files changed, 120 insertions(+), 90 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 8b67554bec..12b8419065 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -631,8 +631,8 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes, } static int coroutine_fn -blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +blkdebug_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int err; diff --git a/block/blklogwrites.c b/block/blklogwrites.c index b7579370a3..de3d4ba2b6 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -301,8 +301,8 @@ static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp) } static int coroutine_fn -blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +blk_log_writes_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } diff --git a/block/blkreplay.c b/block/blkreplay.c index 4a247752fd..13ea2166bb 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -72,7 +72,7 @@ static void block_request_create(uint64_t reqid, BlockDriverState *bs, } static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); diff --git a/block/blkverify.c b/block/blkverify.c index 188d7632fa..5e35744b8a 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -221,8 +221,8 @@ blkverify_co_prwv(BlockDriverState *bs, BlkverifyRequest *r, uint64_t offset, } static int coroutine_fn -blkverify_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +blkverify_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BlkverifyRequest r; QEMUIOVector raw_qiov; diff --git a/block/bochs.c b/block/bochs.c index 2f010ab40a..4d68658087 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -238,8 +238,8 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) } static int coroutine_fn -bochs_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +bochs_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVBochsState *s = bs->opaque; uint64_t sector_num = offset >> BDRV_SECTOR_BITS; diff --git a/block/cloop.c b/block/cloop.c index c99192a57f..b8c6d0eccd 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -245,8 +245,8 @@ static inline int cloop_read_block(BlockDriverState *bs, int block_num) } static int coroutine_fn -cloop_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +cloop_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVCloopState *s = bs->opaque; uint64_t sector_num = offset >> BDRV_SECTOR_BITS; diff --git a/block/commit.c b/block/commit.c index 42792b4556..10cc5ff451 100644 --- a/block/commit.c +++ b/block/commit.c @@ -207,7 +207,7 @@ static const BlockJobDriver commit_job_driver = { }; static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 2a5e57deca..591cc3ac75 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -40,8 +40,8 @@ typedef struct BDRVCopyBeforeWriteState { } BDRVCopyBeforeWriteState; static coroutine_fn int cbw_co_preadv( - BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } diff --git a/block/copy-on-read.c b/block/copy-on-read.c index c428682272..d34add4476 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -128,10 +128,10 @@ static int64_t cor_getlength(BlockDriverState *bs) static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, - int flags) + BdrvRequestFlags flags) { int64_t n; int local_flags; diff --git a/block/crypto.c b/block/crypto.c index 1d30fde38e..a732a36d10 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -397,8 +397,8 @@ static int block_crypto_reopen_prepare(BDRVReopenState *state, #define BLOCK_CRYPTO_MAX_IO_SIZE (1024 * 1024) static coroutine_fn int -block_crypto_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +block_crypto_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BlockCrypto *crypto = bs->opaque; uint64_t cur_bytes; /* number of bytes in current iteration */ diff --git a/block/curl.c b/block/curl.c index 50e741a0d7..4a8ae2b269 100644 --- a/block/curl.c +++ b/block/curl.c @@ -896,7 +896,8 @@ out: } static int coroutine_fn curl_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { CURLAIOCB acb = { .co = qemu_coroutine_self(), diff --git a/block/dmg.c b/block/dmg.c index ef35a505f2..447901fbb8 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -689,8 +689,8 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) } static int coroutine_fn -dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +dmg_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVDMGState *s = bs->opaque; uint64_t sector_num = offset >> BDRV_SECTOR_BITS; diff --git a/block/file-posix.c b/block/file-posix.c index d81e15efa4..df4a67f8b2 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2077,9 +2077,9 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, return raw_thread_pool_submit(bs, handle_aiocb_rw, &acb); } -static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - int flags) +static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_READ); } diff --git a/block/file-win32.c b/block/file-win32.c index b97c58d642..4e8947009b 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -440,8 +440,8 @@ fail: } static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { BDRVRawState *s = bs->opaque; diff --git a/block/filter-compress.c b/block/filter-compress.c index 5136371bf8..54a16c6c64 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -63,10 +63,10 @@ static int64_t compress_getlength(BlockDriverState *bs) static int coroutine_fn compress_co_preadv_part(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, - int flags) + BdrvRequestFlags flags) { return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset, flags); diff --git a/block/mirror.c b/block/mirror.c index 85b781bc21..e24d0aedd5 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1402,7 +1402,7 @@ static void coroutine_fn active_write_settle(MirrorOp *op) } static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } diff --git a/block/nbd.c b/block/nbd.c index f6ff1c4fb4..c816933d7d 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1322,8 +1322,9 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, return ret ? ret : request_ret; } -static int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, int flags) +static int nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { int ret, request_ret; Error *local_err = NULL; diff --git a/block/nfs.c b/block/nfs.c index 9aeaefb364..27f9ab8307 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -262,9 +262,9 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, nfs_co_generic_bh_cb, task); } -static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *iov, - int flags) +static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *iov, + BdrvRequestFlags flags) { NFSClient *client = bs->opaque; NFSRPC task; diff --git a/block/null.c b/block/null.c index cc9b1d4ea7..343dbb580d 100644 --- a/block/null.c +++ b/block/null.c @@ -116,8 +116,9 @@ static coroutine_fn int null_co_common(BlockDriverState *bs) } static coroutine_fn int null_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVNullState *s = bs->opaque; @@ -187,8 +188,8 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, } static BlockAIOCB *null_aio_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { diff --git a/block/nvme.c b/block/nvme.c index abfe305baf..f812eb1cc2 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1251,8 +1251,9 @@ static int nvme_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, } static coroutine_fn int nvme_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { return nvme_co_prw(bs, offset, bytes, qiov, false, flags); } diff --git a/block/preallocate.c b/block/preallocate.c index b619206304..5709443612 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -227,8 +227,8 @@ static void preallocate_reopen_abort(BDRVReopenState *state) } static coroutine_fn int preallocate_co_preadv_part( - BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, int flags) + BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) { return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset, flags); diff --git a/block/qcow.c b/block/qcow.c index f8919a44d1..1151b8d79b 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -617,9 +617,9 @@ static void qcow_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.request_alignment = BDRV_SECTOR_SIZE; } -static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - int flags) +static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVQcowState *s = bs->opaque; int offset_in_cluster; diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 4ebb49a087..5727f92dcb 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -505,7 +505,19 @@ static int coroutine_fn do_perform_cow_read(BlockDriverState *bs, return -ENOMEDIUM; } - /* Call .bdrv_co_readv() directly instead of using the public block-layer + /* + * We never deal with requests that don't satisfy + * bdrv_check_qiov_request(), and aligning requests to clusters never + * breaks this condition. So, do some assertions before calling + * bs->drv->bdrv_co_preadv_part() which has int64_t arguments. + */ + assert(src_cluster_offset <= INT64_MAX); + assert(src_cluster_offset + offset_in_cluster <= INT64_MAX); + assert(qiov->size <= INT64_MAX); + bdrv_check_qiov_request(src_cluster_offset + offset_in_cluster, qiov->size, + qiov, 0, &error_abort); + /* + * Call .bdrv_co_readv() directly instead of using the public block-layer * interface. This avoids double I/O throttling and request tracking, * which can lead to deadlock when block layer copy-on-read is enabled. */ diff --git a/block/qcow2.c b/block/qcow2.c index 1c3cf7f91d..680ef70532 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2310,9 +2310,10 @@ static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task) } static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset, int flags) + size_t qiov_offset, + BdrvRequestFlags flags) { BDRVQcow2State *s = bs->opaque; int ret = 0; diff --git a/block/quorum.c b/block/quorum.c index f2c0805000..57c73b2156 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -663,8 +663,8 @@ static int read_fifo_child(QuorumAIOCB *acb) return ret; } -static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, int flags) +static int quorum_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVQuorumState *s = bs->opaque; QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); diff --git a/block/raw-format.c b/block/raw-format.c index c26f493688..a5728e7b0c 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -181,8 +181,8 @@ static void raw_reopen_abort(BDRVReopenState *state) } /* Check and adjust the offset, against 'offset' and 'size' options. */ -static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset, - uint64_t bytes, bool is_write) +static inline int raw_adjust_offset(BlockDriverState *bs, int64_t *offset, + int64_t bytes, bool is_write) { BDRVRawState *s = bs->opaque; @@ -201,9 +201,9 @@ static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset, return 0; } -static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - int flags) +static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { int ret; @@ -259,7 +259,7 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, qiov = &local_qiov; } - ret = raw_adjust_offset(bs, &offset, bytes, true); + ret = raw_adjust_offset(bs, (int64_t *)&offset, bytes, true); if (ret) { goto fail; } @@ -294,7 +294,7 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, { int ret; - ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); + ret = raw_adjust_offset(bs, &offset, bytes, true); if (ret) { return ret; } @@ -306,7 +306,7 @@ static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, { int ret; - ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); + ret = raw_adjust_offset(bs, &offset, bytes, true); if (ret) { return ret; } @@ -541,7 +541,7 @@ static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, { int ret; - ret = raw_adjust_offset(bs, &src_offset, bytes, false); + ret = raw_adjust_offset(bs, (int64_t *)&src_offset, bytes, false); if (ret) { return ret; } @@ -560,7 +560,7 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, { int ret; - ret = raw_adjust_offset(bs, &dst_offset, bytes, true); + ret = raw_adjust_offset(bs, (int64_t *)&dst_offset, bytes, true); if (ret) { return ret; } diff --git a/block/rbd.c b/block/rbd.c index dcf82b15b8..21438dfb7c 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1164,9 +1164,9 @@ static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs, } static int -coroutine_fn qemu_rbd_co_preadv(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - int flags) +coroutine_fn qemu_rbd_co_preadv(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { return qemu_rbd_start_co(bs, offset, bytes, qiov, flags, RBD_AIO_READ); } diff --git a/block/throttle.c b/block/throttle.c index b685166ad4..20362b5fe5 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -112,8 +112,9 @@ static int64_t throttle_getlength(BlockDriverState *bs) } static int coroutine_fn throttle_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { ThrottleGroupMember *tgm = bs->opaque; diff --git a/block/vdi.c b/block/vdi.c index 548f8a057b..b394cf6ca6 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -544,8 +544,8 @@ static int coroutine_fn vdi_co_block_status(BlockDriverState *bs, } static int coroutine_fn -vdi_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +vdi_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVVdiState *s = bs->opaque; QEMUIOVector local_qiov; diff --git a/block/vmdk.c b/block/vmdk.c index 4499f136bd..78592160d0 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1888,8 +1888,8 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, } static int coroutine_fn -vmdk_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +vmdk_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVVmdkState *s = bs->opaque; int ret; diff --git a/block/vpc.c b/block/vpc.c index 17a705b482..29c8517ff8 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -608,8 +608,8 @@ static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) } static int coroutine_fn -vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +vpc_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVVPCState *s = bs->opaque; int ret; diff --git a/block/vvfat.c b/block/vvfat.c index 34bf1e3a86..9c53c2d0a4 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1522,8 +1522,8 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num, } static int coroutine_fn -vvfat_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +vvfat_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret; BDRVVVFATState *s = bs->opaque; diff --git a/include/block/block_int.h b/include/block/block_int.h index ed60495938..9b1a276fa1 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -235,8 +235,8 @@ struct BlockDriver { /* aio */ BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, - BlockCompletionFunc *cb, void *opaque); + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_pwritev)(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque); @@ -265,10 +265,11 @@ struct BlockDriver { * The buffer in @qiov may point directly to guest memory. */ int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags); + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); int coroutine_fn (*bdrv_co_preadv_part)(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, int flags); + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags); /** diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index ce071b5fc5..2d3c17e566 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -65,8 +65,9 @@ static void co_reenter_bh(void *opaque) } static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVTestState *s = bs->opaque; @@ -1106,8 +1107,9 @@ static void bdrv_test_top_close(BlockDriverState *bs) } static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVTestTopState *tts = bs->opaque; return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags); @@ -1855,10 +1857,10 @@ static void bdrv_replace_test_close(BlockDriverState *bs) * Set .has_read to true and return success. */ static int coroutine_fn bdrv_replace_test_co_preadv(BlockDriverState *bs, - uint64_t offset, - uint64_t bytes, + int64_t offset, + int64_t bytes, QEMUIOVector *qiov, - int flags) + BdrvRequestFlags flags) { BDRVReplaceTestState *s = bs->opaque; diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index c39e70b2f5..f18fa6e0fb 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -31,9 +31,18 @@ #include "qemu/main-loop.h" #include "iothread.h" -static int coroutine_fn bdrv_test_co_prwv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + return 0; +} + +static int coroutine_fn bdrv_test_co_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, + int flags) { return 0; } @@ -66,8 +75,8 @@ static BlockDriver bdrv_test = { .format_name = "test", .instance_size = 1, - .bdrv_co_preadv = bdrv_test_co_prwv, - .bdrv_co_pwritev = bdrv_test_co_prwv, + .bdrv_co_preadv = bdrv_test_co_preadv, + .bdrv_co_pwritev = bdrv_test_co_pwritev, .bdrv_co_pdiscard = bdrv_test_co_pdiscard, .bdrv_co_truncate = bdrv_test_co_truncate, .bdrv_co_block_status = bdrv_test_co_block_status, From e75abedab7e10043ed81b1cbc3033409aff0159e Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:28:00 +0300 Subject: [PATCH 0042/1334] block: use int64_t instead of uint64_t in driver write handlers We are generally moving to int64_t for both offset and bytes parameters on all io paths. Main motivation is realization of 64-bit write_zeroes operation for fast zeroing large disk chunks, up to the whole disk. We chose signed type, to be consistent with off_t (which is signed) and with possibility for signed return type (where negative value means error). So, convert driver write handlers parameters which are already 64bit to signed type. While being here, convert also flags parameter to be BdrvRequestFlags. Now let's consider all callers. Simple git grep '\->bdrv_\(aio\|co\)_pwritev\(_part\)\?' shows that's there three callers of driver function: bdrv_driver_pwritev() and bdrv_driver_pwritev_compressed() in block/io.c, both pass int64_t, checked by bdrv_check_qiov_request() to be non-negative. qcow2_save_vmstate() does bdrv_check_qiov_request(). Still, the functions may be called directly, not only by drv->... Let's check: git grep '\.bdrv_\(aio\|co\)_pwritev\(_part\)\?\s*=' | \ awk '{print $4}' | sed 's/,//' | sed 's/&//' | sort | uniq | \ while read func; do git grep "$func(" | \ grep -v "$func(BlockDriverState"; done shows several callers: qcow2: qcow2_co_truncate() write at most up to @offset, which is checked in generic qcow2_co_truncate() by bdrv_check_request(). qcow2_co_pwritev_compressed_task() pass the request (or part of the request) that already went through normal write path, so it should be OK qcow: qcow_co_pwritev_compressed() pass int64_t, it's updated by this patch quorum: quorum_co_pwrite_zeroes() pass int64_t and int - OK throttle: throttle_co_pwritev_compressed() pass int64_t, it's updated by this patch vmdk: vmdk_co_pwritev_compressed() pass int64_t, it's updated by this patch Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210903102807.27127-5-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/blkdebug.c | 4 ++-- block/blklogwrites.c | 4 ++-- block/blkreplay.c | 2 +- block/blkverify.c | 4 ++-- block/copy-before-write.c | 7 ++++--- block/copy-on-read.c | 11 ++++++----- block/crypto.c | 4 ++-- block/file-posix.c | 6 +++--- block/file-win32.c | 4 ++-- block/filter-compress.c | 7 ++++--- block/io.c | 6 ++++-- block/mirror.c | 2 +- block/nbd.c | 5 +++-- block/nfs.c | 6 +++--- block/null.c | 9 +++++---- block/nvme.c | 5 +++-- block/preallocate.c | 6 +++--- block/qcow.c | 10 +++++----- block/qcow2.c | 6 +++--- block/quorum.c | 5 +++-- block/raw-format.c | 8 ++++---- block/rbd.c | 6 +++--- block/throttle.c | 9 +++++---- block/trace-events | 2 +- block/vdi.c | 4 ++-- block/vmdk.c | 8 ++++---- block/vpc.c | 4 ++-- block/vvfat.c | 4 ++-- include/block/block_int.h | 16 ++++++++-------- tests/unit/test-block-iothread.c | 4 ++-- 30 files changed, 94 insertions(+), 84 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 12b8419065..e686cd9799 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -652,8 +652,8 @@ blkdebug_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn -blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +blkdebug_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int err; diff --git a/block/blklogwrites.c b/block/blklogwrites.c index de3d4ba2b6..ca174ab135 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -460,8 +460,8 @@ blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr) } static int coroutine_fn -blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +blk_log_writes_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { return blk_log_writes_co_log(bs, offset, bytes, qiov, flags, blk_log_writes_co_do_file_pwritev, 0, false); diff --git a/block/blkreplay.c b/block/blkreplay.c index 13ea2166bb..7ba62dcac1 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -83,7 +83,7 @@ static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs, } static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); diff --git a/block/blkverify.c b/block/blkverify.c index 5e35744b8a..d1facf5ba9 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -250,8 +250,8 @@ blkverify_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn -blkverify_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +blkverify_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BlkverifyRequest r; return blkverify_co_prwv(bs, &r, offset, bytes, qiov, qiov, flags, true); diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 591cc3ac75..74360b4853 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -86,9 +86,10 @@ static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs, } static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs, - uint64_t offset, - uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, + int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); if (ret < 0) { diff --git a/block/copy-on-read.c b/block/copy-on-read.c index d34add4476..b2ec36b6fc 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -181,10 +181,11 @@ static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs, static int coroutine_fn cor_co_pwritev_part(BlockDriverState *bs, - uint64_t offset, - uint64_t bytes, + int64_t offset, + int64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset, int flags) + size_t qiov_offset, + BdrvRequestFlags flags) { return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset, flags); @@ -207,8 +208,8 @@ static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, static int coroutine_fn cor_co_pwritev_compressed(BlockDriverState *bs, - uint64_t offset, - uint64_t bytes, + int64_t offset, + int64_t bytes, QEMUIOVector *qiov) { return bdrv_co_pwritev(bs->file, offset, bytes, qiov, diff --git a/block/crypto.c b/block/crypto.c index a732a36d10..c8ba4681e2 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -460,8 +460,8 @@ block_crypto_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, static coroutine_fn int -block_crypto_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +block_crypto_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BlockCrypto *crypto = bs->opaque; uint64_t cur_bytes; /* number of bytes in current iteration */ diff --git a/block/file-posix.c b/block/file-posix.c index df4a67f8b2..994f1c26ca 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2084,9 +2084,9 @@ static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_READ); } -static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - int flags) +static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { assert(flags == 0); return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_WRITE); diff --git a/block/file-win32.c b/block/file-win32.c index 4e8947009b..ec9d64d0e4 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -455,8 +455,8 @@ static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs, } static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { BDRVRawState *s = bs->opaque; diff --git a/block/filter-compress.c b/block/filter-compress.c index 54a16c6c64..505822a44f 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -74,10 +74,11 @@ static int coroutine_fn compress_co_preadv_part(BlockDriverState *bs, static int coroutine_fn compress_co_pwritev_part(BlockDriverState *bs, - uint64_t offset, - uint64_t bytes, + int64_t offset, + int64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset, int flags) + size_t qiov_offset, + BdrvRequestFlags flags) { return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset, flags | BDRV_REQ_WRITE_COMPRESSED); diff --git a/block/io.c b/block/io.c index a4f124f755..aa6f7b075e 100644 --- a/block/io.c +++ b/block/io.c @@ -1230,7 +1230,8 @@ out: static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset, int flags) + size_t qiov_offset, + BdrvRequestFlags flags) { BlockDriver *drv = bs->drv; int64_t sector_num; @@ -2073,7 +2074,8 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes, */ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, BdrvTrackedRequest *req, int64_t offset, int64_t bytes, - int64_t align, QEMUIOVector *qiov, size_t qiov_offset, int flags) + int64_t align, QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; diff --git a/block/mirror.c b/block/mirror.c index e24d0aedd5..c4c623ceed 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1456,7 +1456,7 @@ out: } static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { MirrorBDSOpaque *s = bs->opaque; QEMUIOVector bounce_qiov; diff --git a/block/nbd.c b/block/nbd.c index c816933d7d..caee396525 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1381,8 +1381,9 @@ static int nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, return ret ? ret : request_ret; } -static int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, int flags) +static int nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { diff --git a/block/nfs.c b/block/nfs.c index 27f9ab8307..577aea1d22 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -296,9 +296,9 @@ static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset, return 0; } -static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *iov, - int flags) +static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *iov, + BdrvRequestFlags flags) { NFSClient *client = bs->opaque; NFSRPC task; diff --git a/block/null.c b/block/null.c index 343dbb580d..75f7d0db40 100644 --- a/block/null.c +++ b/block/null.c @@ -130,8 +130,9 @@ static coroutine_fn int null_co_preadv(BlockDriverState *bs, } static coroutine_fn int null_co_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { return null_co_common(bs); } @@ -203,8 +204,8 @@ static BlockAIOCB *null_aio_preadv(BlockDriverState *bs, } static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { diff --git a/block/nvme.c b/block/nvme.c index f812eb1cc2..c44db18939 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1259,8 +1259,9 @@ static coroutine_fn int nvme_co_preadv(BlockDriverState *bs, } static coroutine_fn int nvme_co_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { return nvme_co_prw(bs, offset, bytes, qiov, true, flags); } diff --git a/block/preallocate.c b/block/preallocate.c index 5709443612..c19885af17 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -349,11 +349,11 @@ static int coroutine_fn preallocate_co_pwrite_zeroes(BlockDriverState *bs, } static coroutine_fn int preallocate_co_pwritev_part(BlockDriverState *bs, - uint64_t offset, - uint64_t bytes, + int64_t offset, + int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, - int flags) + BdrvRequestFlags flags) { handle_write(bs, offset, bytes, false); diff --git a/block/qcow.c b/block/qcow.c index 1151b8d79b..c39940f33e 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -714,9 +714,9 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, int64_t offset, return ret; } -static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - int flags) +static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVQcowState *s = bs->opaque; int offset_in_cluster; @@ -1047,8 +1047,8 @@ static int qcow_make_empty(BlockDriverState *bs) /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ static coroutine_fn int -qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov) +qcow_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; z_stream strm; diff --git a/block/qcow2.c b/block/qcow2.c index 680ef70532..bb5455ed3d 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2597,8 +2597,8 @@ static coroutine_fn int qcow2_co_pwritev_task_entry(AioTask *task) } static coroutine_fn int qcow2_co_pwritev_part( - BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, int flags) + BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) { BDRVQcow2State *s = bs->opaque; int offset_in_cluster; @@ -4631,7 +4631,7 @@ static coroutine_fn int qcow2_co_pwritev_compressed_task_entry(AioTask *task) */ static coroutine_fn int qcow2_co_pwritev_compressed_part(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) { BDRVQcow2State *s = bs->opaque; diff --git a/block/quorum.c b/block/quorum.c index 57c73b2156..f4b76ea010 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -714,8 +714,9 @@ static void write_quorum_entry(void *opaque) } } -static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, int flags) +static int quorum_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVQuorumState *s = bs->opaque; QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); diff --git a/block/raw-format.c b/block/raw-format.c index a5728e7b0c..d223298dfc 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -216,9 +216,9 @@ static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } -static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - int flags) +static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { void *buf = NULL; BlockDriver *drv; @@ -259,7 +259,7 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, qiov = &local_qiov; } - ret = raw_adjust_offset(bs, (int64_t *)&offset, bytes, true); + ret = raw_adjust_offset(bs, &offset, bytes, true); if (ret) { goto fail; } diff --git a/block/rbd.c b/block/rbd.c index 21438dfb7c..efc0835ee7 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1172,9 +1172,9 @@ coroutine_fn qemu_rbd_co_preadv(BlockDriverState *bs, int64_t offset, } static int -coroutine_fn qemu_rbd_co_pwritev(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - int flags) +coroutine_fn qemu_rbd_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVRBDState *s = bs->opaque; /* diff --git a/block/throttle.c b/block/throttle.c index 20362b5fe5..1330e844c3 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -124,8 +124,9 @@ static int coroutine_fn throttle_co_preadv(BlockDriverState *bs, } static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { ThrottleGroupMember *tgm = bs->opaque; throttle_group_co_io_limits_intercept(tgm, bytes, true); @@ -153,8 +154,8 @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, } static int coroutine_fn throttle_co_pwritev_compressed(BlockDriverState *bs, - uint64_t offset, - uint64_t bytes, + int64_t offset, + int64_t bytes, QEMUIOVector *qiov) { return throttle_co_pwritev(bs, offset, bytes, qiov, diff --git a/block/trace-events b/block/trace-events index f4f1267c8c..983dd54830 100644 --- a/block/trace-events +++ b/block/trace-events @@ -75,7 +75,7 @@ luring_resubmit_short_read(void *s, void *luringcb, int nread) "LuringState %p l # qcow2.c qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t host_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu" -qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" +qcow2_writev_start_req(void *co, int64_t offset, int64_t bytes) "co %p offset 0x%" PRIx64 " bytes %" PRId64 qcow2_writev_done_req(void *co, int ret) "co %p ret %d" qcow2_writev_start_part(void *co) "co %p" qcow2_writev_done_part(void *co, int cur_bytes) "co %p cur_bytes %d" diff --git a/block/vdi.c b/block/vdi.c index b394cf6ca6..bdc58d726e 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -600,8 +600,8 @@ vdi_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn -vdi_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVVdiState *s = bs->opaque; QEMUIOVector local_qiov; diff --git a/block/vmdk.c b/block/vmdk.c index 78592160d0..8d49e54bdd 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -2068,8 +2068,8 @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, } static int coroutine_fn -vmdk_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +vmdk_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret; BDRVVmdkState *s = bs->opaque; @@ -2080,8 +2080,8 @@ vmdk_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, } static int coroutine_fn -vmdk_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov) +vmdk_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov) { if (bytes == 0) { /* The caller will write bytes 0 to signal EOF. diff --git a/block/vpc.c b/block/vpc.c index 29c8517ff8..1b4c7333af 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -658,8 +658,8 @@ fail: } static int coroutine_fn -vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +vpc_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVVPCState *s = bs->opaque; int64_t image_offset; diff --git a/block/vvfat.c b/block/vvfat.c index 9c53c2d0a4..05e78e3c27 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3061,8 +3061,8 @@ DLOG(checkpoint()); } static int coroutine_fn -vvfat_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret; BDRVVVFATState *s = bs->opaque; diff --git a/include/block/block_int.h b/include/block/block_int.h index 9b1a276fa1..2cf5f1722a 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -238,8 +238,8 @@ struct BlockDriver { int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_pwritev)(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, - BlockCompletionFunc *cb, void *opaque); + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_pdiscard)(BlockDriverState *bs, @@ -288,10 +288,11 @@ struct BlockDriver { * The buffer in @qiov may point directly to guest memory. */ int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags); + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); int coroutine_fn (*bdrv_co_pwritev_part)(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, int flags); + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); /* * Efficiently zero a region of the disk image. Typically an image format @@ -438,10 +439,9 @@ struct BlockDriver { Error **errp); int coroutine_fn (*bdrv_co_pwritev_compressed)(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov); + int64_t offset, int64_t bytes, QEMUIOVector *qiov); int coroutine_fn (*bdrv_co_pwritev_compressed_part)(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset); + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); int (*bdrv_snapshot_create)(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index f18fa6e0fb..c86954c7ba 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -40,9 +40,9 @@ static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, } static int coroutine_fn bdrv_test_co_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, - int flags) + BdrvRequestFlags flags) { return 0; } From 485350497b7a9be3477d453fe0fdf380b8535303 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:28:01 +0300 Subject: [PATCH 0043/1334] block: use int64_t instead of uint64_t in copy_range driver handlers We are generally moving to int64_t for both offset and bytes parameters on all io paths. Main motivation is realization of 64-bit write_zeroes operation for fast zeroing large disk chunks, up to the whole disk. We chose signed type, to be consistent with off_t (which is signed) and with possibility for signed return type (where negative value means error). So, convert driver copy_range handlers parameters which are already 64bit to signed type. Now let's consider all callers. Simple git grep '\->bdrv_co_copy_range' shows the only caller: bdrv_co_copy_range_internal(), which does bdrv_check_request32(), so everything is OK. Still, the functions may be called directly, not only by drv->... Let's check: git grep '\.bdrv_co_copy_range_\(from\|to\)\s*=' | \ awk '{print $4}' | sed 's/,//' | sed 's/&//' | sort | uniq | \ while read func; do git grep "$func(" | \ grep -v "$func(BlockDriverState"; done shows no more callers. So, we are done. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Message-Id: <20210903102807.27127-6-vsementsov@virtuozzo.com> Signed-off-by: Eric Blake --- block/file-posix.c | 10 +++++----- block/iscsi.c | 12 ++++++------ block/qcow2.c | 12 ++++++------ block/raw-format.c | 16 ++++++++-------- include/block/block_int.h | 12 ++++++------ 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 994f1c26ca..ed71e8d2df 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3203,8 +3203,8 @@ static void raw_abort_perm_update(BlockDriverState *bs) } static int coroutine_fn raw_co_copy_range_from( - BlockDriverState *bs, BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, + BlockDriverState *bs, BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, @@ -3213,10 +3213,10 @@ static int coroutine_fn raw_co_copy_range_from( static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, BdrvChild *src, - uint64_t src_offset, + int64_t src_offset, BdrvChild *dst, - uint64_t dst_offset, - uint64_t bytes, + int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { diff --git a/block/iscsi.c b/block/iscsi.c index 852384086b..01fdd1775f 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2169,10 +2169,10 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, BdrvChild *src, - uint64_t src_offset, + int64_t src_offset, BdrvChild *dst, - uint64_t dst_offset, - uint64_t bytes, + int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { @@ -2310,10 +2310,10 @@ static void iscsi_xcopy_data(struct iscsi_data *data, static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, BdrvChild *src, - uint64_t src_offset, + int64_t src_offset, BdrvChild *dst, - uint64_t dst_offset, - uint64_t bytes, + int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { diff --git a/block/qcow2.c b/block/qcow2.c index bb5455ed3d..520ae37a29 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -4026,9 +4026,9 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, static int coroutine_fn qcow2_co_copy_range_from(BlockDriverState *bs, - BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags read_flags, + BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { BDRVQcow2State *s = bs->opaque; @@ -4109,9 +4109,9 @@ out: static int coroutine_fn qcow2_co_copy_range_to(BlockDriverState *bs, - BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags read_flags, + BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { BDRVQcow2State *s = bs->opaque; diff --git a/block/raw-format.c b/block/raw-format.c index d223298dfc..345137813e 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -532,16 +532,16 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, BdrvChild *src, - uint64_t src_offset, + int64_t src_offset, BdrvChild *dst, - uint64_t dst_offset, - uint64_t bytes, + int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { int ret; - ret = raw_adjust_offset(bs, (int64_t *)&src_offset, bytes, false); + ret = raw_adjust_offset(bs, &src_offset, bytes, false); if (ret) { return ret; } @@ -551,16 +551,16 @@ static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, BdrvChild *src, - uint64_t src_offset, + int64_t src_offset, BdrvChild *dst, - uint64_t dst_offset, - uint64_t bytes, + int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { int ret; - ret = raw_adjust_offset(bs, (int64_t *)&dst_offset, bytes, true); + ret = raw_adjust_offset(bs, &dst_offset, bytes, true); if (ret) { return ret; } diff --git a/include/block/block_int.h b/include/block/block_int.h index 2cf5f1722a..5536f49bc6 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -314,10 +314,10 @@ struct BlockDriver { */ int coroutine_fn (*bdrv_co_copy_range_from)(BlockDriverState *bs, BdrvChild *src, - uint64_t offset, + int64_t offset, BdrvChild *dst, - uint64_t dst_offset, - uint64_t bytes, + int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); @@ -331,10 +331,10 @@ struct BlockDriver { */ int coroutine_fn (*bdrv_co_copy_range_to)(BlockDriverState *bs, BdrvChild *src, - uint64_t src_offset, + int64_t src_offset, BdrvChild *dst, - uint64_t dst_offset, - uint64_t bytes, + int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); From d544f5d3b1df79d71eae4d7fac528f65d22265ad Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:28:02 +0300 Subject: [PATCH 0044/1334] block: make BlockLimits::max_pwrite_zeroes 64bit We are going to support 64 bit write-zeroes requests. Now update the limit variable. It's absolutely safe. The variable is set in some drivers, and used in bdrv_co_do_pwrite_zeroes(). Update also max_write_zeroes variable in bdrv_co_do_pwrite_zeroes(), so that bdrv_co_do_pwrite_zeroes() is now prepared to 64bit requests. The remaining logic including num, offset and bytes variables is already supporting 64bit requests. So the only thing that prevents 64 bit requests is limiting max_write_zeroes variable to INT_MAX in bdrv_co_do_pwrite_zeroes(). We'll drop this limitation after updating all block drivers. Ah, we also have bdrv_check_request32() in bdrv_co_pwritev_part(). It will be modified to do bdrv_check_request() for write-zeroes path. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210903102807.27127-7-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/io.c | 2 +- include/block/block_int.h | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/block/io.c b/block/io.c index aa6f7b075e..0090224603 100644 --- a/block/io.c +++ b/block/io.c @@ -1869,7 +1869,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int head = 0; int tail = 0; - int max_write_zeroes = MIN_NON_ZERO(bs->bl.max_pwrite_zeroes, INT_MAX); + int64_t max_write_zeroes = MIN_NON_ZERO(bs->bl.max_pwrite_zeroes, INT_MAX); int alignment = MAX(bs->bl.pwrite_zeroes_alignment, bs->bl.request_alignment); int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); diff --git a/include/block/block_int.h b/include/block/block_int.h index 5536f49bc6..24958acd33 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -686,10 +686,11 @@ typedef struct BlockLimits { * that is set. May be 0 if bl.request_alignment is good enough */ uint32_t pdiscard_alignment; - /* Maximum number of bytes that can zeroized at once (since it is - * signed, it must be < 2G, if set). Must be multiple of - * pwrite_zeroes_alignment. May be 0 if no inherent 32-bit limit */ - int32_t max_pwrite_zeroes; + /* + * Maximum number of bytes that can zeroized at once. Must be multiple of + * pwrite_zeroes_alignment. 0 means no limit. + */ + int64_t max_pwrite_zeroes; /* Optimal alignment for write zeroes requests in bytes. A power * of 2 is best but not mandatory. Must be a multiple of From f34b2bcf8c393929c7d2100eb2f3909cac0d1cab Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:28:03 +0300 Subject: [PATCH 0045/1334] block: use int64_t instead of int in driver write_zeroes handlers We are generally moving to int64_t for both offset and bytes parameters on all io paths. Main motivation is realization of 64-bit write_zeroes operation for fast zeroing large disk chunks, up to the whole disk. We chose signed type, to be consistent with off_t (which is signed) and with possibility for signed return type (where negative value means error). So, convert driver write_zeroes handlers bytes parameter to int64_t. The only caller of all updated function is bdrv_co_do_pwrite_zeroes(). bdrv_co_do_pwrite_zeroes() itself is of course OK with widening of callee parameter type. Also, bdrv_co_do_pwrite_zeroes()'s max_write_zeroes is limited to INT_MAX. So, updated functions all are safe, they will not get "bytes" larger than before. Still, let's look through all updated functions, and add assertions to the ones which are actually unprepared to values larger than INT_MAX. For these drivers also set explicit max_pwrite_zeroes limit. Let's go: blkdebug: calculations can't overflow, thanks to bdrv_check_qiov_request() in generic layer. rule_check() and bdrv_co_pwrite_zeroes() both have 64bit argument. blklogwrites: pass to blk_log_writes_co_log() with 64bit argument. blkreplay, copy-on-read, filter-compress: pass to bdrv_co_pwrite_zeroes() which is OK copy-before-write: Calls cbw_do_copy_before_write() and bdrv_co_pwrite_zeroes, both have 64bit argument. file-posix: both handler calls raw_do_pwrite_zeroes, which is updated. In raw_do_pwrite_zeroes() calculations are OK due to bdrv_check_qiov_request(), bytes go to RawPosixAIOData::aio_nbytes which is uint64_t. Check also where that uint64_t gets handed: handle_aiocb_write_zeroes_block() passes a uint64_t[2] to ioctl(BLKZEROOUT), handle_aiocb_write_zeroes() calls do_fallocate() which takes off_t (and we compile to always have 64-bit off_t), as does handle_aiocb_write_zeroes_unmap. All look safe. gluster: bytes go to GlusterAIOCB::size which is int64_t and to glfs_zerofill_async works with off_t. iscsi: Aha, here we deal with iscsi_writesame16_task() that has uint32_t num_blocks argument and iscsi_writesame16_task() has uint16_t argument. Make comments, add assertions and clarify max_pwrite_zeroes calculation. iscsi_allocmap_() functions already has int64_t argument is_byte_request_lun_aligned is simple to update, do it. mirror_top: pass to bdrv_mirror_top_do_write which has uint64_t argument nbd: Aha, here we have protocol limitation, and NBDRequest::len is uint32_t. max_pwrite_zeroes is cleanly set to 32bit value, so we are OK for now. nvme: Again, protocol limitation. And no inherent limit for write-zeroes at all. But from code that calculates cdw12 it's obvious that we do have limit and alignment. Let's clarify it. Also, obviously the code is not prepared to handle bytes=0. Let's handle this case too. trace events already 64bit preallocate: pass to handle_write() and bdrv_co_pwrite_zeroes(), both 64bit. rbd: pass to qemu_rbd_start_co() which is 64bit. qcow2: offset + bytes and alignment still works good (thanks to bdrv_check_qiov_request()), so tail calculation is OK qcow2_subcluster_zeroize() has 64bit argument, should be OK trace events updated qed: qed_co_request wants int nb_sectors. Also in code we have size_t used for request length which may be 32bit. So, let's just keep INT_MAX as a limit (aligning it down to pwrite_zeroes_alignment) and don't care. raw-format: Is OK. raw_adjust_offset and bdrv_co_pwrite_zeroes are both 64bit. throttle: Both throttle_group_co_io_limits_intercept() and bdrv_co_pwrite_zeroes() are 64bit. vmdk: pass to vmdk_pwritev which is 64bit quorum: pass to quorum_co_pwritev() which is 64bit Hooray! At this point all block drivers are prepared to support 64bit write-zero requests, or have explicitly set max_pwrite_zeroes. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210903102807.27127-8-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: use <= rather than < in assertions relying on max_pwrite_zeroes] Signed-off-by: Eric Blake --- block/blkdebug.c | 2 +- block/blklogwrites.c | 4 ++-- block/blkreplay.c | 2 +- block/copy-before-write.c | 2 +- block/copy-on-read.c | 2 +- block/file-posix.c | 6 +++--- block/filter-compress.c | 2 +- block/gluster.c | 6 +++--- block/iscsi.c | 30 ++++++++++++++++++++---------- block/mirror.c | 2 +- block/nbd.c | 6 ++++-- block/nvme.c | 24 +++++++++++++++++++++--- block/preallocate.c | 2 +- block/qcow2.c | 2 +- block/qed.c | 9 ++++++++- block/quorum.c | 2 +- block/raw-format.c | 2 +- block/rbd.c | 4 ++-- block/throttle.c | 2 +- block/trace-events | 4 ++-- block/vmdk.c | 2 +- include/block/block_int.h | 2 +- 22 files changed, 78 insertions(+), 41 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index e686cd9799..742b4a3834 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -684,7 +684,7 @@ static int blkdebug_co_flush(BlockDriverState *bs) } static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { uint32_t align = MAX(bs->bl.request_alignment, diff --git a/block/blklogwrites.c b/block/blklogwrites.c index ca174ab135..d7ae64c22d 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -468,8 +468,8 @@ blk_log_writes_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn -blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, - BdrvRequestFlags flags) +blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, + int64_t bytes, BdrvRequestFlags flags) { return blk_log_writes_co_log(bs, offset, bytes, NULL, flags, blk_log_writes_co_do_file_pwrite_zeroes, 0, diff --git a/block/blkreplay.c b/block/blkreplay.c index 7ba62dcac1..89d74a3cca 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -94,7 +94,7 @@ static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs, } static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 74360b4853..d210e87a45 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -75,7 +75,7 @@ static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, } static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); if (ret < 0) { diff --git a/block/copy-on-read.c b/block/copy-on-read.c index b2ec36b6fc..f83dd83f14 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -193,7 +193,7 @@ static int coroutine_fn cor_co_pwritev_part(BlockDriverState *bs, static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); diff --git a/block/file-posix.c b/block/file-posix.c index ed71e8d2df..f375070f25 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2972,7 +2972,7 @@ raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) } static int coroutine_fn -raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, +raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestFlags flags, bool blkdev) { BDRVRawState *s = bs->opaque; @@ -3040,7 +3040,7 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, static int coroutine_fn raw_co_pwrite_zeroes( BlockDriverState *bs, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { return raw_do_pwrite_zeroes(bs, offset, bytes, flags, false); } @@ -3605,7 +3605,7 @@ hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) } static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { int rc; diff --git a/block/filter-compress.c b/block/filter-compress.c index 505822a44f..fb85686b69 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -86,7 +86,7 @@ static int coroutine_fn compress_co_pwritev_part(BlockDriverState *bs, static int coroutine_fn compress_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); diff --git a/block/gluster.c b/block/gluster.c index d51938e447..4e3c9cd14f 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1003,19 +1003,19 @@ static void qemu_gluster_reopen_abort(BDRVReopenState *state) #ifdef CONFIG_GLUSTERFS_ZEROFILL static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int size, + int64_t bytes, BdrvRequestFlags flags) { int ret; GlusterAIOCB acb; BDRVGlusterState *s = bs->opaque; - acb.size = size; + acb.size = bytes; acb.ret = 0; acb.coroutine = qemu_coroutine_self(); acb.aio_context = bdrv_get_aio_context(bs); - ret = glfs_zerofill_async(s->fd, offset, size, gluster_finish_aiocb, &acb); + ret = glfs_zerofill_async(s->fd, offset, bytes, gluster_finish_aiocb, &acb); if (ret < 0) { return -errno; } diff --git a/block/iscsi.c b/block/iscsi.c index 01fdd1775f..74ff7e307e 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -427,14 +427,14 @@ static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun) return sector * BDRV_SECTOR_SIZE / iscsilun->block_size; } -static bool is_byte_request_lun_aligned(int64_t offset, int count, +static bool is_byte_request_lun_aligned(int64_t offset, int64_t bytes, IscsiLun *iscsilun) { - if (offset % iscsilun->block_size || count % iscsilun->block_size) { + if (offset % iscsilun->block_size || bytes % iscsilun->block_size) { error_report("iSCSI misaligned request: " "iscsilun->block_size %u, offset %" PRIi64 - ", count %d", - iscsilun->block_size, offset, count); + ", bytes %" PRIi64, + iscsilun->block_size, offset, bytes); return false; } return true; @@ -1202,12 +1202,12 @@ out_unlock: static int coroutine_fn iscsi_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { IscsiLun *iscsilun = bs->opaque; struct IscsiTask iTask; uint64_t lba; - uint32_t nb_blocks; + uint64_t nb_blocks; bool use_16_for_ws = iscsilun->use_16_for_rw; int r = 0; @@ -1247,11 +1247,21 @@ coroutine_fn iscsi_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, iscsi_co_init_iscsitask(iscsilun, &iTask); retry: if (use_16_for_ws) { + /* + * iscsi_writesame16_task num_blocks argument is uint32_t. We rely here + * on our max_pwrite_zeroes limit. + */ + assert(nb_blocks <= UINT32_MAX); iTask.task = iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba, iscsilun->zeroblock, iscsilun->block_size, nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), 0, 0, iscsi_co_generic_cb, &iTask); } else { + /* + * iscsi_writesame10_task num_blocks argument is uint16_t. We rely here + * on our max_pwrite_zeroes limit. + */ + assert(nb_blocks <= UINT16_MAX); iTask.task = iscsi_writesame10_task(iscsilun->iscsi, iscsilun->lun, lba, iscsilun->zeroblock, iscsilun->block_size, nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), @@ -2071,10 +2081,10 @@ static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.pdiscard_alignment = iscsilun->block_size; } - if (iscsilun->bl.max_ws_len < 0xffffffff / block_size) { - bs->bl.max_pwrite_zeroes = - iscsilun->bl.max_ws_len * iscsilun->block_size; - } + bs->bl.max_pwrite_zeroes = + MIN_NON_ZERO(iscsilun->bl.max_ws_len * iscsilun->block_size, + max_xfer_len * iscsilun->block_size); + if (iscsilun->lbp.lbpws) { bs->bl.pwrite_zeroes_alignment = iscsilun->bl.opt_unmap_gran * iscsilun->block_size; diff --git a/block/mirror.c b/block/mirror.c index c4c623ceed..fab75087b0 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1501,7 +1501,7 @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) } static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, offset, bytes, NULL, flags); diff --git a/block/nbd.c b/block/nbd.c index caee396525..c0c479abe9 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1407,15 +1407,17 @@ static int nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, } static int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { .type = NBD_CMD_WRITE_ZEROES, .from = offset, - .len = bytes, + .len = bytes, /* .len is uint32_t actually */ }; + assert(bytes <= UINT32_MAX); /* rely on max_pwrite_zeroes */ + assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); if (!(s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) { return -ENOTSUP; diff --git a/block/nvme.c b/block/nvme.c index c44db18939..2e0fd9e76a 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1296,19 +1296,29 @@ static coroutine_fn int nvme_co_flush(BlockDriverState *bs) static coroutine_fn int nvme_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int bytes, + int64_t bytes, BdrvRequestFlags flags) { BDRVNVMeState *s = bs->opaque; NVMeQueuePair *ioq = s->queues[INDEX_IO(0)]; NVMeRequest *req; - - uint32_t cdw12 = ((bytes >> s->blkshift) - 1) & 0xFFFF; + uint32_t cdw12; if (!s->supports_write_zeroes) { return -ENOTSUP; } + if (bytes == 0) { + return 0; + } + + cdw12 = ((bytes >> s->blkshift) - 1) & 0xFFFF; + /* + * We should not lose information. pwrite_zeroes_alignment and + * max_pwrite_zeroes guarantees it. + */ + assert(((cdw12 + 1) << s->blkshift) == bytes); + NvmeCmd cmd = { .opcode = NVME_CMD_WRITE_ZEROES, .nsid = cpu_to_le32(s->nsid), @@ -1472,6 +1482,14 @@ static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.opt_mem_alignment = s->page_size; bs->bl.request_alignment = s->page_size; bs->bl.max_transfer = s->max_transfer; + + /* + * Look at nvme_co_pwrite_zeroes: after shift and decrement we should get + * at most 0xFFFF + */ + bs->bl.max_pwrite_zeroes = 1ULL << (s->blkshift + 16); + bs->bl.pwrite_zeroes_alignment = MAX(bs->bl.request_alignment, + 1UL << s->blkshift); } static void nvme_detach_aio_context(BlockDriverState *bs) diff --git a/block/preallocate.c b/block/preallocate.c index c19885af17..99e28d9f08 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -337,7 +337,7 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, } static int coroutine_fn preallocate_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { bool want_merge_zero = !(flags & ~(BDRV_REQ_ZERO_WRITE | BDRV_REQ_NO_FALLBACK)); diff --git a/block/qcow2.c b/block/qcow2.c index 520ae37a29..4b2e869495 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3941,7 +3941,7 @@ static bool is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) } static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { int ret; BDRVQcow2State *s = bs->opaque; diff --git a/block/qed.c b/block/qed.c index f45c640513..558d3646c4 100644 --- a/block/qed.c +++ b/block/qed.c @@ -582,6 +582,7 @@ static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp) BDRVQEDState *s = bs->opaque; bs->bl.pwrite_zeroes_alignment = s->header.cluster_size; + bs->bl.max_pwrite_zeroes = QEMU_ALIGN_DOWN(INT_MAX, s->header.cluster_size); } /* We have nothing to do for QED reopen, stubs just return @@ -1397,7 +1398,7 @@ static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs, static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int bytes, + int64_t bytes, BdrvRequestFlags flags) { BDRVQEDState *s = bs->opaque; @@ -1408,6 +1409,12 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, */ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes); + /* + * QED is not prepared for 63bit write-zero requests, so rely on + * max_pwrite_zeroes. + */ + assert(bytes <= INT_MAX); + /* Fall back if the request is not aligned */ if (qed_offset_into_cluster(s, offset) || qed_offset_into_cluster(s, bytes)) { diff --git a/block/quorum.c b/block/quorum.c index f4b76ea010..c28dda7baa 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -746,7 +746,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, int64_t offset, } static int quorum_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { return quorum_co_pwritev(bs, offset, bytes, NULL, diff --git a/block/raw-format.c b/block/raw-format.c index 345137813e..a2485926b8 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -289,7 +289,7 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, } static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { int ret; diff --git a/block/rbd.c b/block/rbd.c index efc0835ee7..053eb8e48f 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1205,9 +1205,9 @@ static int coroutine_fn qemu_rbd_co_pdiscard(BlockDriverState *bs, #ifdef LIBRBD_SUPPORTS_WRITE_ZEROES static int coroutine_fn qemu_rbd_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int count, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { - return qemu_rbd_start_co(bs, offset, count, NULL, flags, + return qemu_rbd_start_co(bs, offset, bytes, NULL, flags, RBD_AIO_WRITE_ZEROES); } #endif diff --git a/block/throttle.c b/block/throttle.c index 1330e844c3..c13fe9067f 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -135,7 +135,7 @@ static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs, } static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { ThrottleGroupMember *tgm = bs->opaque; diff --git a/block/trace-events b/block/trace-events index 983dd54830..d8a08563f1 100644 --- a/block/trace-events +++ b/block/trace-events @@ -80,8 +80,8 @@ qcow2_writev_done_req(void *co, int ret) "co %p ret %d" qcow2_writev_start_part(void *co) "co %p" qcow2_writev_done_part(void *co, int cur_bytes) "co %p cur_bytes %d" qcow2_writev_data(void *co, uint64_t offset) "co %p offset 0x%" PRIx64 -qcow2_pwrite_zeroes_start_req(void *co, int64_t offset, int count) "co %p offset 0x%" PRIx64 " count %d" -qcow2_pwrite_zeroes(void *co, int64_t offset, int count) "co %p offset 0x%" PRIx64 " count %d" +qcow2_pwrite_zeroes_start_req(void *co, int64_t offset, int64_t bytes) "co %p offset 0x%" PRIx64 " bytes %" PRId64 +qcow2_pwrite_zeroes(void *co, int64_t offset, int64_t bytes) "co %p offset 0x%" PRIx64 " bytes %" PRId64 qcow2_skip_cow(void *co, uint64_t offset, int nb_clusters) "co %p offset 0x%" PRIx64 " nb_clusters %d" # qcow2-cluster.c diff --git a/block/vmdk.c b/block/vmdk.c index 8d49e54bdd..fb4cc9da90 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -2109,7 +2109,7 @@ vmdk_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int bytes, + int64_t bytes, BdrvRequestFlags flags) { int ret; diff --git a/include/block/block_int.h b/include/block/block_int.h index 24958acd33..d518703e3e 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -301,7 +301,7 @@ struct BlockDriver { * will be called instead. */ int coroutine_fn (*bdrv_co_pwrite_zeroes)(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags); + int64_t offset, int64_t bytes, BdrvRequestFlags flags); int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs, int64_t offset, int bytes); From 2aaa3f9b33afca9733700b94d764e45ac009b8e5 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:28:04 +0300 Subject: [PATCH 0046/1334] block/io: allow 64bit write-zeroes requests Now that all drivers are updated by previous commit, we can drop two last limiters on write-zeroes path: INT_MAX in bdrv_co_do_pwrite_zeroes() and bdrv_check_request32() in bdrv_co_pwritev_part(). Now everything is prepared for implementing incredibly cool and fast big-write-zeroes in NBD and qcow2. And any other driver which wants it of course. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Message-Id: <20210903102807.27127-9-vsementsov@virtuozzo.com> Signed-off-by: Eric Blake --- block/io.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/block/io.c b/block/io.c index 0090224603..e40462742e 100644 --- a/block/io.c +++ b/block/io.c @@ -1869,7 +1869,8 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int head = 0; int tail = 0; - int64_t max_write_zeroes = MIN_NON_ZERO(bs->bl.max_pwrite_zeroes, INT_MAX); + int64_t max_write_zeroes = MIN_NON_ZERO(bs->bl.max_pwrite_zeroes, + INT64_MAX); int alignment = MAX(bs->bl.pwrite_zeroes_alignment, bs->bl.request_alignment); int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); @@ -2248,7 +2249,11 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, return -ENOMEDIUM; } - ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); + if (flags & BDRV_REQ_ZERO_WRITE) { + ret = bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, NULL); + } else { + ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); + } if (ret < 0) { return ret; } From 39af49c0d7e0a2a285f1bcbd3db0db88f15b1d8c Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:28:05 +0300 Subject: [PATCH 0047/1334] block: make BlockLimits::max_pdiscard 64bit We are going to support 64 bit discard requests. Now update the limit variable. It's absolutely safe. The variable is set in some drivers, and used in bdrv_co_pdiscard(). Update also max_pdiscard variable in bdrv_co_pdiscard(), so that bdrv_co_pdiscard() is now prepared for 64bit requests. The remaining logic including num, offset and bytes variables is already supporting 64bit requests. So the only thing that prevents 64 bit requests is limiting max_pdiscard variable to INT_MAX in bdrv_co_pdiscard(). We'll drop this limitation after updating all block drivers. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Message-Id: <20210903102807.27127-10-vsementsov@virtuozzo.com> Signed-off-by: Eric Blake --- block/io.c | 3 ++- include/block/block_int.h | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/block/io.c b/block/io.c index e40462742e..3846e2ed96 100644 --- a/block/io.c +++ b/block/io.c @@ -3056,7 +3056,8 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes) { BdrvTrackedRequest req; - int max_pdiscard, ret; + int ret; + int64_t max_pdiscard; int head, tail, align; BlockDriverState *bs = child->bs; diff --git a/include/block/block_int.h b/include/block/block_int.h index d518703e3e..9b4e0748bc 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -674,11 +674,12 @@ typedef struct BlockLimits { * otherwise. */ uint32_t request_alignment; - /* Maximum number of bytes that can be discarded at once (since it - * is signed, it must be < 2G, if set). Must be multiple of - * pdiscard_alignment, but need not be power of 2. May be 0 if no - * inherent 32-bit limit */ - int32_t max_pdiscard; + /* + * Maximum number of bytes that can be discarded at once. Must be multiple + * of pdiscard_alignment, but need not be power of 2. May be 0 if no + * inherent 64-bit limit. + */ + int64_t max_pdiscard; /* Optimal alignment for discard requests in bytes. A power of 2 * is best but not mandatory. Must be a multiple of From 0c8022876f2183f93e23a7314862140c94ee62e7 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:28:06 +0300 Subject: [PATCH 0048/1334] block: use int64_t instead of int in driver discard handlers We are generally moving to int64_t for both offset and bytes parameters on all io paths. Main motivation is realization of 64-bit write_zeroes operation for fast zeroing large disk chunks, up to the whole disk. We chose signed type, to be consistent with off_t (which is signed) and with possibility for signed return type (where negative value means error). So, convert driver discard handlers bytes parameter to int64_t. The only caller of all updated function is bdrv_co_pdiscard in block/io.c. It is already prepared to work with 64bit requests, but pass at most max(bs->bl.max_pdiscard, INT_MAX) to the driver. Let's look at all updated functions: blkdebug: all calculations are still OK, thanks to bdrv_check_qiov_request(). both rule_check and bdrv_co_pdiscard are 64bit blklogwrites: pass to blk_loc_writes_co_log which is 64bit blkreplay, copy-on-read, filter-compress: pass to bdrv_co_pdiscard, OK copy-before-write: pass to bdrv_co_pdiscard which is 64bit and to cbw_do_copy_before_write which is 64bit file-posix: one handler calls raw_account_discard() is 64bit and both handlers calls raw_do_pdiscard(). Update raw_do_pdiscard, which pass to RawPosixAIOData::aio_nbytes, which is 64bit (and calls raw_account_discard()) gluster: somehow, third argument of glfs_discard_async is size_t. Let's set max_pdiscard accordingly. iscsi: iscsi_allocmap_set_invalid is 64bit, !is_byte_request_lun_aligned is 64bit. list.num is uint32_t. Let's clarify max_pdiscard and pdiscard_alignment. mirror_top: pass to bdrv_mirror_top_do_write() which is 64bit nbd: protocol limitation. max_pdiscard is alredy set strict enough, keep it as is for now. nvme: buf.nlb is uint32_t and we do shift. So, add corresponding limits to nvme_refresh_limits(). preallocate: pass to bdrv_co_pdiscard() which is 64bit. rbd: pass to qemu_rbd_start_co() which is 64bit. qcow2: calculations are still OK, thanks to bdrv_check_qiov_request(), qcow2_cluster_discard() is 64bit. raw-format: raw_adjust_offset() is 64bit, bdrv_co_pdiscard too. throttle: pass to bdrv_co_pdiscard() which is 64bit and to throttle_group_co_io_limits_intercept() which is 64bit as well. test-block-iothread: bytes argument is unused Great! Now all drivers are prepared to handle 64bit discard requests, or else have explicit max_pdiscard limits. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210903102807.27127-11-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/blkdebug.c | 2 +- block/blklogwrites.c | 4 ++-- block/blkreplay.c | 2 +- block/copy-before-write.c | 2 +- block/copy-on-read.c | 2 +- block/file-posix.c | 7 ++++--- block/filter-compress.c | 2 +- block/gluster.c | 7 +++++-- block/iscsi.c | 16 +++++++++++----- block/mirror.c | 2 +- block/nbd.c | 6 ++++-- block/nvme.c | 14 +++++++++++++- block/preallocate.c | 2 +- block/qcow2.c | 2 +- block/raw-format.c | 2 +- block/rbd.c | 4 ++-- block/throttle.c | 2 +- block/trace-events | 4 ++-- include/block/block_int.h | 2 +- tests/unit/test-block-iothread.c | 2 +- 20 files changed, 55 insertions(+), 31 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 742b4a3834..bbf2948703 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -717,7 +717,7 @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs, } static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { uint32_t align = bs->bl.pdiscard_alignment; int err; diff --git a/block/blklogwrites.c b/block/blklogwrites.c index d7ae64c22d..f7a251e91f 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -484,9 +484,9 @@ static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs) } static int coroutine_fn -blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) +blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { - return blk_log_writes_co_log(bs, offset, count, NULL, 0, + return blk_log_writes_co_log(bs, offset, bytes, NULL, 0, blk_log_writes_co_do_file_pdiscard, LOG_DISCARD_FLAG, false); } diff --git a/block/blkreplay.c b/block/blkreplay.c index 89d74a3cca..dcbe780ddb 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -105,7 +105,7 @@ static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs, } static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_pdiscard(bs->file, offset, bytes); diff --git a/block/copy-before-write.c b/block/copy-before-write.c index d210e87a45..c30a5ff8de 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -64,7 +64,7 @@ static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, } static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { int ret = cbw_do_copy_before_write(bs, offset, bytes, 0); if (ret < 0) { diff --git a/block/copy-on-read.c b/block/copy-on-read.c index f83dd83f14..1fc7fb3333 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -201,7 +201,7 @@ static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { return bdrv_co_pdiscard(bs->file, offset, bytes); } diff --git a/block/file-posix.c b/block/file-posix.c index f375070f25..c62e42743d 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2942,7 +2942,8 @@ static void raw_account_discard(BDRVRawState *s, uint64_t nbytes, int ret) } static coroutine_fn int -raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev) +raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes, + bool blkdev) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; @@ -2966,7 +2967,7 @@ raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev) } static coroutine_fn int -raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) +raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { return raw_do_pdiscard(bs, offset, bytes, false); } @@ -3591,7 +3592,7 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) #endif /* linux */ static coroutine_fn int -hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) +hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { BDRVRawState *s = bs->opaque; int ret; diff --git a/block/filter-compress.c b/block/filter-compress.c index fb85686b69..d5be538619 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -94,7 +94,7 @@ static int coroutine_fn compress_co_pwrite_zeroes(BlockDriverState *bs, static int coroutine_fn compress_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { return bdrv_co_pdiscard(bs->file, offset, bytes); } diff --git a/block/gluster.c b/block/gluster.c index 4e3c9cd14f..398976bc66 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -891,6 +891,7 @@ out: static void qemu_gluster_refresh_limits(BlockDriverState *bs, Error **errp) { bs->bl.max_transfer = GLUSTER_MAX_TRANSFER; + bs->bl.max_pdiscard = SIZE_MAX; } static int qemu_gluster_reopen_prepare(BDRVReopenState *state, @@ -1297,18 +1298,20 @@ error: #ifdef CONFIG_GLUSTERFS_DISCARD static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs, - int64_t offset, int size) + int64_t offset, int64_t bytes) { int ret; GlusterAIOCB acb; BDRVGlusterState *s = bs->opaque; + assert(bytes <= SIZE_MAX); /* rely on max_pdiscard */ + acb.size = 0; acb.ret = 0; acb.coroutine = qemu_coroutine_self(); acb.aio_context = bdrv_get_aio_context(bs); - ret = glfs_discard_async(s->fd, offset, size, gluster_finish_aiocb, &acb); + ret = glfs_discard_async(s->fd, offset, bytes, gluster_finish_aiocb, &acb); if (ret < 0) { return -errno; } diff --git a/block/iscsi.c b/block/iscsi.c index 74ff7e307e..57aa07a40d 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1138,7 +1138,8 @@ iscsi_getlength(BlockDriverState *bs) } static int -coroutine_fn iscsi_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) +coroutine_fn iscsi_co_pdiscard(BlockDriverState *bs, int64_t offset, + int64_t bytes) { IscsiLun *iscsilun = bs->opaque; struct IscsiTask iTask; @@ -1154,6 +1155,12 @@ coroutine_fn iscsi_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) return 0; } + /* + * We don't want to overflow list.num which is uint32_t. + * We rely on our max_pdiscard. + */ + assert(bytes / iscsilun->block_size <= UINT32_MAX); + list.lba = offset / iscsilun->block_size; list.num = bytes / iscsilun->block_size; @@ -2071,10 +2078,9 @@ static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) } if (iscsilun->lbp.lbpu) { - if (iscsilun->bl.max_unmap < 0xffffffff / block_size) { - bs->bl.max_pdiscard = - iscsilun->bl.max_unmap * iscsilun->block_size; - } + bs->bl.max_pdiscard = + MIN_NON_ZERO(iscsilun->bl.max_unmap * iscsilun->block_size, + (uint64_t)UINT32_MAX * iscsilun->block_size); bs->bl.pdiscard_alignment = iscsilun->bl.opt_unmap_gran * iscsilun->block_size; } else { diff --git a/block/mirror.c b/block/mirror.c index fab75087b0..c962e8b471 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1508,7 +1508,7 @@ static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, } static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, offset, bytes, NULL, 0); diff --git a/block/nbd.c b/block/nbd.c index c0c479abe9..a66b2c282d 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1457,15 +1457,17 @@ static int nbd_client_co_flush(BlockDriverState *bs) } static int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, - int bytes) + int64_t bytes) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { .type = NBD_CMD_TRIM, .from = offset, - .len = bytes, + .len = bytes, /* len is uint32_t */ }; + assert(bytes <= UINT32_MAX); /* rely on max_pdiscard */ + assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); if (!(s->info.flags & NBD_FLAG_SEND_TRIM) || !bytes) { return 0; diff --git a/block/nvme.c b/block/nvme.c index 2e0fd9e76a..1cc7b62bb4 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1360,7 +1360,7 @@ static coroutine_fn int nvme_co_pwrite_zeroes(BlockDriverState *bs, static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs, int64_t offset, - int bytes) + int64_t bytes) { BDRVNVMeState *s = bs->opaque; NVMeQueuePair *ioq = s->queues[INDEX_IO(0)]; @@ -1387,6 +1387,14 @@ static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs, assert(s->queue_count > 1); + /* + * Filling the @buf requires @offset and @bytes to satisfy restrictions + * defined in nvme_refresh_limits(). + */ + assert(QEMU_IS_ALIGNED(bytes, 1UL << s->blkshift)); + assert(QEMU_IS_ALIGNED(offset, 1UL << s->blkshift)); + assert((bytes >> s->blkshift) <= UINT32_MAX); + buf = qemu_try_memalign(s->page_size, s->page_size); if (!buf) { return -ENOMEM; @@ -1490,6 +1498,10 @@ static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.max_pwrite_zeroes = 1ULL << (s->blkshift + 16); bs->bl.pwrite_zeroes_alignment = MAX(bs->bl.request_alignment, 1UL << s->blkshift); + + bs->bl.max_pdiscard = (uint64_t)UINT32_MAX << s->blkshift; + bs->bl.pdiscard_alignment = MAX(bs->bl.request_alignment, + 1UL << s->blkshift); } static void nvme_detach_aio_context(BlockDriverState *bs) diff --git a/block/preallocate.c b/block/preallocate.c index 99e28d9f08..1d4233f730 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -235,7 +235,7 @@ static coroutine_fn int preallocate_co_preadv_part( } static int coroutine_fn preallocate_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { return bdrv_co_pdiscard(bs->file, offset, bytes); } diff --git a/block/qcow2.c b/block/qcow2.c index 4b2e869495..d509016756 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3996,7 +3996,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, } static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { int ret; BDRVQcow2State *s = bs->opaque; diff --git a/block/raw-format.c b/block/raw-format.c index a2485926b8..bda757fd19 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -302,7 +302,7 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, } static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { int ret; diff --git a/block/rbd.c b/block/rbd.c index 053eb8e48f..701fbf2b0c 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1197,9 +1197,9 @@ static int coroutine_fn qemu_rbd_co_flush(BlockDriverState *bs) } static int coroutine_fn qemu_rbd_co_pdiscard(BlockDriverState *bs, - int64_t offset, int count) + int64_t offset, int64_t bytes) { - return qemu_rbd_start_co(bs, offset, count, NULL, 0, RBD_AIO_DISCARD); + return qemu_rbd_start_co(bs, offset, bytes, NULL, 0, RBD_AIO_DISCARD); } #ifdef LIBRBD_SUPPORTS_WRITE_ZEROES diff --git a/block/throttle.c b/block/throttle.c index c13fe9067f..6e8d52fa24 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -145,7 +145,7 @@ static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs, } static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { ThrottleGroupMember *tgm = bs->opaque; throttle_group_co_io_limits_intercept(tgm, bytes, true); diff --git a/block/trace-events b/block/trace-events index d8a08563f1..f2d0a9b62a 100644 --- a/block/trace-events +++ b/block/trace-events @@ -152,8 +152,8 @@ nvme_write_zeroes(void *s, uint64_t offset, uint64_t bytes, int flags) "s %p off nvme_qiov_unaligned(const void *qiov, int n, void *base, size_t size, int align) "qiov %p n %d base %p size 0x%zx align 0x%x" nvme_prw_buffered(void *s, uint64_t offset, uint64_t bytes, int niov, int is_write) "s %p offset 0x%"PRIx64" bytes %"PRId64" niov %d is_write %d" nvme_rw_done(void *s, int is_write, uint64_t offset, uint64_t bytes, int ret) "s %p is_write %d offset 0x%"PRIx64" bytes %"PRId64" ret %d" -nvme_dsm(void *s, uint64_t offset, uint64_t bytes) "s %p offset 0x%"PRIx64" bytes %"PRId64"" -nvme_dsm_done(void *s, uint64_t offset, uint64_t bytes, int ret) "s %p offset 0x%"PRIx64" bytes %"PRId64" ret %d" +nvme_dsm(void *s, int64_t offset, int64_t bytes) "s %p offset 0x%"PRIx64" bytes %"PRId64"" +nvme_dsm_done(void *s, int64_t offset, int64_t bytes, int ret) "s %p offset 0x%"PRIx64" bytes %"PRId64" ret %d" nvme_dma_map_flush(void *s) "s %p" nvme_free_req_queue_wait(void *s, unsigned q_index) "s %p q #%u" nvme_create_queue_pair(unsigned q_index, void *q, size_t size, void *aio_context, int fd) "index %u q %p size %zu aioctx %p fd %d" diff --git a/include/block/block_int.h b/include/block/block_int.h index 9b4e0748bc..ffe86068d4 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -303,7 +303,7 @@ struct BlockDriver { int coroutine_fn (*bdrv_co_pwrite_zeroes)(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestFlags flags); int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs, - int64_t offset, int bytes); + int64_t offset, int64_t bytes); /* Map [offset, offset + nbytes) range onto a child of @bs to copy from, * and invoke bdrv_co_copy_range_from(child, ...), or invoke diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index c86954c7ba..aea660aeed 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -48,7 +48,7 @@ static int coroutine_fn bdrv_test_co_pwritev(BlockDriverState *bs, } static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) + int64_t offset, int64_t bytes) { return 0; } From 6a8f3dbb1912141c9806c22db394a9ac8fe8366c Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 3 Sep 2021 13:28:07 +0300 Subject: [PATCH 0049/1334] block/io: allow 64bit discard requests Now that all drivers are updated by the previous commit, we can drop the last limiter on pdiscard path: INT_MAX in bdrv_co_pdiscard(). Now everything is prepared for implementing incredibly cool and fast big-discard requests in NBD and qcow2. And any other driver which wants it of course. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Message-Id: <20210903102807.27127-12-vsementsov@virtuozzo.com> Signed-off-by: Eric Blake --- block/io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/io.c b/block/io.c index 3846e2ed96..18d345a87a 100644 --- a/block/io.c +++ b/block/io.c @@ -3104,7 +3104,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, goto out; } - max_pdiscard = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_pdiscard, INT_MAX), + max_pdiscard = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_pdiscard, INT64_MAX), align); assert(max_pdiscard >= bs->bl.request_alignment); From da24597dd37bc31b4d2328e5542fa3b5676a3fe0 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 7 Sep 2021 12:35:05 -0500 Subject: [PATCH 0050/1334] nbd/server: Allow LIST_META_CONTEXT without STRUCTURED_REPLY The NBD protocol just relaxed the requirements on NBD_OPT_LIST_META_CONTEXT: https://github.com/NetworkBlockDevice/nbd/commit/13a4e33a87 Since listing is not stateful (unlike SET_META_CONTEXT), we don't care if a client asks for meta contexts without first requesting structured replies. Well-behaved clients will still ask for structured reply first (if for no other reason than for back-compat to older servers), but that's no reason to avoid this change. Signed-off-by: Eric Blake Message-Id: <20210907173505.1499709-1-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- nbd/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nbd/server.c b/nbd/server.c index 3927f7789d..6d03e8a4b4 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -980,7 +980,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, size_t i; size_t count = 0; - if (!client->structured_reply) { + if (client->opt == NBD_OPT_SET_META_CONTEXT && !client->structured_reply) { return nbd_opt_invalid(client, errp, "request option '%s' when structured reply " "is not negotiated", From f7ca4aadca865898ef9c52d75f142a9db622c712 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 6 Sep 2021 22:06:46 +0300 Subject: [PATCH 0051/1334] nbd/client-connection: nbd_co_establish_connection(): fix non set errp When we don't have a connection and blocking is false, we return NULL but don't set errp. That's wrong. We have two paths for calling nbd_co_establish_connection(): 1. nbd_open() -> nbd_do_establish_connection() -> ... but that will never set blocking=false 2. nbd_reconnect_attempt() -> nbd_co_do_establish_connection() -> ... but that uses errp=NULL So, we are safe with our wrong errp policy in nbd_co_establish_connection(). Still let's fix it. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210906190654.183421-2-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- nbd/client-connection.c | 1 + 1 file changed, 1 insertion(+) diff --git a/nbd/client-connection.c b/nbd/client-connection.c index 7123b1e189..695f855754 100644 --- a/nbd/client-connection.c +++ b/nbd/client-connection.c @@ -318,6 +318,7 @@ nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info, } if (!blocking) { + error_setg(errp, "No connection at the moment"); return NULL; } From cb116da7d722f1c5fbb32c818817aef0f576772d Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 2 Sep 2021 13:38:01 +0300 Subject: [PATCH 0052/1334] block/nbd: nbd_channel_error() shutdown channel unconditionally Don't rely on connection being totally broken in case of -EIO. Safer and more correct is to just shut down the channel anyway, since we change the state and plan on reconnecting. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Message-Id: <20210902103805.25686-2-vsementsov@virtuozzo.com> [eblake: grammar tweaks] Signed-off-by: Eric Blake --- block/nbd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index a66b2c282d..de59e76378 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -129,15 +129,16 @@ static bool nbd_client_connected(BDRVNBDState *s) static void nbd_channel_error(BDRVNBDState *s, int ret) { + if (nbd_client_connected(s)) { + qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + } + if (ret == -EIO) { if (nbd_client_connected(s)) { s->state = s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT : NBD_CLIENT_CONNECTING_NOWAIT; } } else { - if (nbd_client_connected(s)) { - qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); - } s->state = NBD_CLIENT_QUIT; } } From 3bc0bd1f4292758417738739d1594a55f283498b Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 2 Sep 2021 13:38:02 +0300 Subject: [PATCH 0053/1334] block/nbd: move nbd_recv_coroutines_wake_all() up We are going to use it in nbd_channel_error(), so move it up. Note, that we are going also refactor and rename nbd_recv_coroutines_wake_all() in future anyway, so keeping it where it is and making forward declaration doesn't make real sense. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Message-Id: <20210902103805.25686-3-vsementsov@virtuozzo.com> Signed-off-by: Eric Blake --- block/nbd.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index de59e76378..2842e6263f 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -127,6 +127,20 @@ static bool nbd_client_connected(BDRVNBDState *s) return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED; } +static void nbd_recv_coroutines_wake_all(BDRVNBDState *s) +{ + int i; + + for (i = 0; i < MAX_NBD_REQUESTS; i++) { + NBDClientRequest *req = &s->requests[i]; + + if (req->coroutine && req->receiving) { + req->receiving = false; + aio_co_wake(req->coroutine); + } + } +} + static void nbd_channel_error(BDRVNBDState *s, int ret) { if (nbd_client_connected(s)) { @@ -143,20 +157,6 @@ static void nbd_channel_error(BDRVNBDState *s, int ret) } } -static void nbd_recv_coroutines_wake_all(BDRVNBDState *s) -{ - int i; - - for (i = 0; i < MAX_NBD_REQUESTS; i++) { - NBDClientRequest *req = &s->requests[i]; - - if (req->coroutine && req->receiving) { - req->receiving = false; - aio_co_wake(req->coroutine); - } - } -} - static void reconnect_delay_timer_del(BDRVNBDState *s) { if (s->reconnect_delay_timer) { From 04a953b232751f4074fd3365c82c82e11cc2d241 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 2 Sep 2021 13:38:03 +0300 Subject: [PATCH 0054/1334] block/nbd: refactor nbd_recv_coroutines_wake_all() Split out nbd_recv_coroutine_wake_one(), as it will be used separately. Rename the function and add a possibility to wake only first found sleeping coroutine. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210902103805.25686-4-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweak] Signed-off-by: Eric Blake --- block/nbd.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 2842e6263f..709c2499e3 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -127,16 +127,24 @@ static bool nbd_client_connected(BDRVNBDState *s) return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED; } -static void nbd_recv_coroutines_wake_all(BDRVNBDState *s) +static bool nbd_recv_coroutine_wake_one(NBDClientRequest *req) +{ + if (req->receiving) { + req->receiving = false; + aio_co_wake(req->coroutine); + return true; + } + + return false; +} + +static void nbd_recv_coroutines_wake(BDRVNBDState *s, bool all) { int i; for (i = 0; i < MAX_NBD_REQUESTS; i++) { - NBDClientRequest *req = &s->requests[i]; - - if (req->coroutine && req->receiving) { - req->receiving = false; - aio_co_wake(req->coroutine); + if (nbd_recv_coroutine_wake_one(&s->requests[i]) && !all) { + return; } } } @@ -415,7 +423,7 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) while (s->in_flight > 0) { qemu_co_mutex_unlock(&s->send_mutex); - nbd_recv_coroutines_wake_all(s); + nbd_recv_coroutines_wake(s, true); s->wait_in_flight = true; qemu_coroutine_yield(); s->wait_in_flight = false; @@ -558,7 +566,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque) } qemu_co_queue_restart_all(&s->free_sema); - nbd_recv_coroutines_wake_all(s); + nbd_recv_coroutines_wake(s, true); bdrv_dec_in_flight(s->bs); s->connection_co = NULL; @@ -1035,7 +1043,7 @@ static coroutine_fn int nbd_co_receive_one_chunk( if (s->connection_co && !s->wait_in_flight) { /* * We must check s->wait_in_flight, because we may entered by - * nbd_recv_coroutines_wake_all(), in this case we should not + * nbd_recv_coroutines_wake(), in this case we should not * wake connection_co here, it will woken by last request. */ aio_co_wake(s->connection_co); From 4ddb5d2fde6f22b2cf65f314107e890a7ca14fcf Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 2 Sep 2021 13:38:04 +0300 Subject: [PATCH 0055/1334] block/nbd: drop connection_co OK, that's a big rewrite of the logic. Pre-patch we have an always running coroutine - connection_co. It does reply receiving and reconnecting. And it leads to a lot of difficult and unobvious code around drained sections and context switch. We also abuse bs->in_flight counter which is increased for connection_co and temporary decreased in points where we want to allow drained section to begin. One of these place is in another file: in nbd_read_eof() in nbd/client.c. We also cancel reconnect and requests waiting for reconnect on drained begin which is not correct. And this patch fixes that. Let's finally drop this always running coroutine and go another way: do both reconnect and receiving in request coroutines. The detailed list of changes below (in the sequence of diff hunks). 1. receiving coroutines are woken directly from nbd_channel_error, when we change s->state 2. nbd_co_establish_connection_cancel(): we don't have drain_begin now, and in nbd_teardown_connection() all requests should already be finished (and reconnect is done from request). So nbd_co_establish_connection_cancel() is called from nbd_cancel_in_flight() (to cancel the request that is doing nbd_co_establish_connection()) and from reconnect_delay_timer_cb() (previously we didn't need it, as reconnect delay only should cancel active requests not the reconnection itself). But now reconnection itself is done in the separate thread (we now call nbd_client_connection_enable_retry() in nbd_open()), and we need to cancel the requests that wait in nbd_co_establish_connection() now). 2A. We do receive headers in request coroutine. But we also should dispatch replies for other pending requests. So, nbd_connection_entry() is turned into nbd_receive_replies(), which does reply dispatching while it receives other request headers, and returns when it receives the requested header. 3. All old staff around drained sections and context switch is dropped. In details: - we don't need to move connection_co to new aio context, as we don't have connection_co anymore - we don't have a fake "request" of connection_co (extra increasing in_flight), so don't care with it in drain_begin/end - we don't stop reconnection during drained section anymore. This means that drain_begin may wait for a long time (up to reconnect_delay). But that's an improvement and more correct behavior see below[*] 4. In nbd_teardown_connection() we don't have to wait for connection_co, as it is dropped. And cleanup for s->ioc and nbd_yank is moved here from removed connection_co. 5. In nbd_co_do_establish_connection() we now should handle NBD_CLIENT_CONNECTING_NOWAIT: if new request comes when we are in NBD_CLIENT_CONNECTING_NOWAIT, it still should call nbd_co_establish_connection() (who knows, maybe the connection was already established by another thread in the background). But we shouldn't wait: if nbd_co_establish_connection() can't return new channel immediately the request should fail (we are in NBD_CLIENT_CONNECTING_NOWAIT state). 6. nbd_reconnect_attempt() is simplified: it's now easier to wait for other requests in the caller, so here we just assert that fact. Also delay time is now initialized here: we can easily detect first attempt and start a timer. 7. nbd_co_reconnect_loop() is dropped, we don't need it. Reconnect retries are fully handle by thread (nbd/client-connection.c), delay timer we initialize in nbd_reconnect_attempt(), we don't have to bother with s->drained and friends. nbd_reconnect_attempt() now called from nbd_co_send_request(). 8. nbd_connection_entry is dropped: reconnect is now handled by nbd_co_send_request(), receiving reply is now handled by nbd_receive_replies(): all handled from request coroutines. 9. So, welcome new nbd_receive_replies() called from request coroutine, that receives reply header instead of nbd_connection_entry(). Like with sending requests, only one coroutine may receive in a moment. So we introduce receive_mutex, which is locked around nbd_receive_reply(). It also protects some related fields. Still, full audit of thread-safety in nbd driver is a separate task. New function waits for a reply with specified handle being received and works rather simple: Under mutex: - if current handle is 0, do receive by hand. If another handle received - switch to other request coroutine, release mutex and yield. Otherwise return success - if current handle == requested handle, we are done - otherwise, release mutex and yield 10: in nbd_co_send_request() we now do nbd_reconnect_attempt() if needed. Also waiting in free_sema queue we now wait for one of two conditions: - connectED, in_flight < MAX_NBD_REQUESTS (so we can start new one) - connectING, in_flight == 0, so we can call nbd_reconnect_attempt() And this logic is protected by s->send_mutex Also, on failure we don't have to care of removed s->connection_co 11. nbd_co_do_receive_one_chunk(): now instead of yield() and wait for s->connection_co we just call new nbd_receive_replies(). 12. nbd_co_receive_one_chunk(): place where s->reply.handle becomes 0, which means that handling of the whole reply is finished. Here we need to wake one of coroutines sleeping in nbd_receive_replies(). If none are sleeping - do nothing. That's another behavior change: we don't have endless recv() in the idle time. It may be considered as a drawback. If so, it may be fixed later. 13. nbd_reply_chunk_iter_receive(): don't care about removed connection_co, just ping in_flight waiters. 14. Don't create connection_co, enable retry in the connection thread (we don't have own reconnect loop anymore) 15. We now need to add a nbd_co_establish_connection_cancel() call in nbd_cancel_in_flight(), to cancel the request that is doing a connection attempt. [*], ok, now we don't cancel reconnect on drain begin. That's correct: reconnect feature leads to possibility of long-running requests (up to reconnect delay). Still, drain begin is not a reason to kill long requests. We should wait for them. This also means, that we can again reproduce a dead-lock, described in 8c517de24a8a1dcbeb54e7e12b5b0fda42a90ace. Why we are OK with it: 1. Now this is not absolutely-dead dead-lock: the vm is unfrozen after reconnect delay. Actually 8c517de24a8a1dc fixed a bug in NBD logic, that was not described in 8c517de24a8a1dc and led to forever dead-lock. The problem was that nobody woke the free_sema queue, but drain_begin can't finish until there is a request in free_sema queue. Now we have a reconnect delay timer that works well. 2. It's not a problem of the NBD driver, but of the ide code, because it does drain_begin under the global mutex; the problem doesn't reproduce when using scsi instead of ide. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210902103805.25686-5-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar and comment tweaks] Signed-off-by: Eric Blake --- block/nbd.c | 381 ++++++++++++++------------------------------------- nbd/client.c | 2 - 2 files changed, 103 insertions(+), 280 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 709c2499e3..8ff6daf43d 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -57,7 +57,7 @@ typedef struct { Coroutine *coroutine; uint64_t offset; /* original offset of the request */ - bool receiving; /* waiting for connection_co? */ + bool receiving; /* sleeping in the yield in nbd_receive_replies */ } NBDClientRequest; typedef enum NBDClientState { @@ -73,14 +73,10 @@ typedef struct BDRVNBDState { CoMutex send_mutex; CoQueue free_sema; - Coroutine *connection_co; - Coroutine *teardown_co; - QemuCoSleep reconnect_sleep; - bool drained; - bool wait_drained_end; + + CoMutex receive_mutex; int in_flight; NBDClientState state; - bool wait_in_flight; QEMUTimer *reconnect_delay_timer; @@ -163,6 +159,8 @@ static void nbd_channel_error(BDRVNBDState *s, int ret) } else { s->state = NBD_CLIENT_QUIT; } + + nbd_recv_coroutines_wake(s, true); } static void reconnect_delay_timer_del(BDRVNBDState *s) @@ -179,6 +177,7 @@ static void reconnect_delay_timer_cb(void *opaque) if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT) { s->state = NBD_CLIENT_CONNECTING_NOWAIT; + nbd_co_establish_connection_cancel(s->conn); while (qemu_co_enter_next(&s->free_sema, NULL)) { /* Resume all queued requests */ } @@ -201,113 +200,21 @@ static void reconnect_delay_timer_init(BDRVNBDState *s, uint64_t expire_time_ns) timer_mod(s->reconnect_delay_timer, expire_time_ns); } -static void nbd_client_detach_aio_context(BlockDriverState *bs) -{ - BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - - /* Timer is deleted in nbd_client_co_drain_begin() */ - assert(!s->reconnect_delay_timer); - /* - * If reconnect is in progress we may have no ->ioc. It will be - * re-instantiated in the proper aio context once the connection is - * reestablished. - */ - if (s->ioc) { - qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); - } -} - -static void nbd_client_attach_aio_context_bh(void *opaque) -{ - BlockDriverState *bs = opaque; - BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - - if (s->connection_co) { - /* - * The node is still drained, so we know the coroutine has yielded in - * nbd_read_eof(), the only place where bs->in_flight can reach 0, or - * it is entered for the first time. Both places are safe for entering - * the coroutine. - */ - qemu_aio_coroutine_enter(bs->aio_context, s->connection_co); - } - bdrv_dec_in_flight(bs); -} - -static void nbd_client_attach_aio_context(BlockDriverState *bs, - AioContext *new_context) -{ - BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - - /* - * s->connection_co is either yielded from nbd_receive_reply or from - * nbd_co_reconnect_loop() - */ - if (nbd_client_connected(s)) { - qio_channel_attach_aio_context(QIO_CHANNEL(s->ioc), new_context); - } - - bdrv_inc_in_flight(bs); - - /* - * Need to wait here for the BH to run because the BH must run while the - * node is still drained. - */ - aio_wait_bh_oneshot(new_context, nbd_client_attach_aio_context_bh, bs); -} - -static void coroutine_fn nbd_client_co_drain_begin(BlockDriverState *bs) -{ - BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - - s->drained = true; - qemu_co_sleep_wake(&s->reconnect_sleep); - - nbd_co_establish_connection_cancel(s->conn); - - reconnect_delay_timer_del(s); - - if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT) { - s->state = NBD_CLIENT_CONNECTING_NOWAIT; - qemu_co_queue_restart_all(&s->free_sema); - } -} - -static void coroutine_fn nbd_client_co_drain_end(BlockDriverState *bs) -{ - BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - - s->drained = false; - if (s->wait_drained_end) { - s->wait_drained_end = false; - aio_co_wake(s->connection_co); - } -} - - static void nbd_teardown_connection(BlockDriverState *bs) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; + assert(!s->in_flight); + if (s->ioc) { - /* finish any pending coroutines */ qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), + nbd_yank, s->bs); + object_unref(OBJECT(s->ioc)); + s->ioc = NULL; } s->state = NBD_CLIENT_QUIT; - if (s->connection_co) { - qemu_co_sleep_wake(&s->reconnect_sleep); - nbd_co_establish_connection_cancel(s->conn); - } - if (qemu_in_coroutine()) { - s->teardown_co = qemu_coroutine_self(); - /* connection_co resumes us when it terminates */ - qemu_coroutine_yield(); - s->teardown_co = NULL; - } else { - BDRV_POLL_WHILE(bs, s->connection_co); - } - assert(!s->connection_co); } static bool nbd_client_connecting(BDRVNBDState *s) @@ -372,10 +279,11 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; int ret; + bool blocking = nbd_client_connecting_wait(s); assert(!s->ioc); - s->ioc = nbd_co_establish_connection(s->conn, &s->info, true, errp); + s->ioc = nbd_co_establish_connection(s->conn, &s->info, blocking, errp); if (!s->ioc) { return -ECONNREFUSED; } @@ -411,29 +319,22 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, return 0; } +/* called under s->send_mutex */ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) { - if (!nbd_client_connecting(s)) { - return; - } + assert(nbd_client_connecting(s)); + assert(s->in_flight == 0); - /* Wait for completion of all in-flight requests */ - - qemu_co_mutex_lock(&s->send_mutex); - - while (s->in_flight > 0) { - qemu_co_mutex_unlock(&s->send_mutex); - nbd_recv_coroutines_wake(s, true); - s->wait_in_flight = true; - qemu_coroutine_yield(); - s->wait_in_flight = false; - qemu_co_mutex_lock(&s->send_mutex); - } - - qemu_co_mutex_unlock(&s->send_mutex); - - if (!nbd_client_connecting(s)) { - return; + if (nbd_client_connecting_wait(s) && s->reconnect_delay && + !s->reconnect_delay_timer) + { + /* + * It's first reconnect attempt after switching to + * NBD_CLIENT_CONNECTING_WAIT + */ + reconnect_delay_timer_init(s, + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + + s->reconnect_delay * NANOSECONDS_PER_SECOND); } /* @@ -453,135 +354,80 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) nbd_co_do_establish_connection(s->bs, NULL); } -static coroutine_fn void nbd_co_reconnect_loop(BDRVNBDState *s) +static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) { - uint64_t timeout = 1 * NANOSECONDS_PER_SECOND; - uint64_t max_timeout = 16 * NANOSECONDS_PER_SECOND; + int ret; + uint64_t ind = HANDLE_TO_INDEX(s, handle), ind2; + QEMU_LOCK_GUARD(&s->receive_mutex); - if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT) { - reconnect_delay_timer_init(s, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + - s->reconnect_delay * NANOSECONDS_PER_SECOND); - } - - nbd_reconnect_attempt(s); - - while (nbd_client_connecting(s)) { - if (s->drained) { - bdrv_dec_in_flight(s->bs); - s->wait_drained_end = true; - while (s->drained) { - /* - * We may be entered once from nbd_client_attach_aio_context_bh - * and then from nbd_client_co_drain_end. So here is a loop. - */ - qemu_coroutine_yield(); - } - bdrv_inc_in_flight(s->bs); - } else { - qemu_co_sleep_ns_wakeable(&s->reconnect_sleep, - QEMU_CLOCK_REALTIME, timeout); - if (s->drained) { - continue; - } - if (timeout < max_timeout) { - timeout *= 2; - } - } - - nbd_reconnect_attempt(s); - } - - reconnect_delay_timer_del(s); -} - -static coroutine_fn void nbd_connection_entry(void *opaque) -{ - BDRVNBDState *s = opaque; - uint64_t i; - int ret = 0; - Error *local_err = NULL; - - while (qatomic_load_acquire(&s->state) != NBD_CLIENT_QUIT) { - /* - * The NBD client can only really be considered idle when it has - * yielded from qio_channel_readv_all_eof(), waiting for data. This is - * the point where the additional scheduled coroutine entry happens - * after nbd_client_attach_aio_context(). - * - * Therefore we keep an additional in_flight reference all the time and - * only drop it temporarily here. - */ - - if (nbd_client_connecting(s)) { - nbd_co_reconnect_loop(s); + while (true) { + if (s->reply.handle == handle) { + /* We are done */ + return 0; } if (!nbd_client_connected(s)) { + return -EIO; + } + + if (s->reply.handle != 0) { + /* + * Some other request is being handled now. It should already be + * woken by whoever set s->reply.handle (or never wait in this + * yield). So, we should not wake it here. + */ + ind2 = HANDLE_TO_INDEX(s, s->reply.handle); + assert(!s->requests[ind2].receiving); + + s->requests[ind].receiving = true; + qemu_co_mutex_unlock(&s->receive_mutex); + + qemu_coroutine_yield(); + /* + * We may be woken for 3 reasons: + * 1. From this function, executing in parallel coroutine, when our + * handle is received. + * 2. From nbd_channel_error(), when connection is lost. + * 3. From nbd_co_receive_one_chunk(), when previous request is + * finished and s->reply.handle set to 0. + * Anyway, it's OK to lock the mutex and go to the next iteration. + */ + + qemu_co_mutex_lock(&s->receive_mutex); + assert(!s->requests[ind].receiving); continue; } + /* We are under mutex and handle is 0. We have to do the dirty work. */ assert(s->reply.handle == 0); - ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, &local_err); - - if (local_err) { - trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err)); - error_free(local_err); - local_err = NULL; - } + ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, NULL); if (ret <= 0) { - nbd_channel_error(s, ret ? ret : -EIO); - continue; + ret = ret ? ret : -EIO; + nbd_channel_error(s, ret); + return ret; } - - /* - * There's no need for a mutex on the receive side, because the - * handler acts as a synchronization point and ensures that only - * one coroutine is called until the reply finishes. - */ - i = HANDLE_TO_INDEX(s, s->reply.handle); - if (i >= MAX_NBD_REQUESTS || - !s->requests[i].coroutine || - !s->requests[i].receiving || - (nbd_reply_is_structured(&s->reply) && !s->info.structured_reply)) - { + if (nbd_reply_is_structured(&s->reply) && !s->info.structured_reply) { nbd_channel_error(s, -EINVAL); - continue; + return -EINVAL; } - - /* - * We're woken up again by the request itself. Note that there - * is no race between yielding and reentering connection_co. This - * is because: - * - * - if the request runs on the same AioContext, it is only - * entered after we yield - * - * - if the request runs on a different AioContext, reentering - * connection_co happens through a bottom half, which can only - * run after we yield. - */ - s->requests[i].receiving = false; - aio_co_wake(s->requests[i].coroutine); - qemu_coroutine_yield(); + if (s->reply.handle == handle) { + /* We are done */ + return 0; + } + ind2 = HANDLE_TO_INDEX(s, s->reply.handle); + if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) { + /* + * We only check that ind2 request exists. But don't check + * whether it is now waiting for the reply header or + * not. We can't just check s->requests[ind2].receiving: + * ind2 request may wait in trying to lock + * receive_mutex. So that's a TODO. + */ + nbd_channel_error(s, -EINVAL); + return -EINVAL; + } + nbd_recv_coroutine_wake_one(&s->requests[ind2]); } - - qemu_co_queue_restart_all(&s->free_sema); - nbd_recv_coroutines_wake(s, true); - bdrv_dec_in_flight(s->bs); - - s->connection_co = NULL; - if (s->ioc) { - qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); - yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), - nbd_yank, s->bs); - object_unref(OBJECT(s->ioc)); - s->ioc = NULL; - } - - if (s->teardown_co) { - aio_co_wake(s->teardown_co); - } - aio_wait_kick(); } static int nbd_co_send_request(BlockDriverState *bs, @@ -592,10 +438,17 @@ static int nbd_co_send_request(BlockDriverState *bs, int rc, i = -1; qemu_co_mutex_lock(&s->send_mutex); - while (s->in_flight == MAX_NBD_REQUESTS || nbd_client_connecting_wait(s)) { + + while (s->in_flight == MAX_NBD_REQUESTS || + (!nbd_client_connected(s) && s->in_flight > 0)) + { qemu_co_queue_wait(&s->free_sema, &s->send_mutex); } + if (nbd_client_connecting(s)) { + nbd_reconnect_attempt(s); + } + if (!nbd_client_connected(s)) { rc = -EIO; goto err; @@ -642,10 +495,6 @@ err: if (i != -1) { s->requests[i].coroutine = NULL; s->in_flight--; - } - if (s->in_flight == 0 && s->wait_in_flight) { - aio_co_wake(s->connection_co); - } else { qemu_co_queue_next(&s->free_sema); } } @@ -944,10 +793,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( } *request_ret = 0; - /* Wait until we're woken up by nbd_connection_entry. */ - s->requests[i].receiving = true; - qemu_coroutine_yield(); - assert(!s->requests[i].receiving); + nbd_receive_replies(s, handle); if (!nbd_client_connected(s)) { error_setg(errp, "Connection closed"); return -EIO; @@ -1040,14 +886,7 @@ static coroutine_fn int nbd_co_receive_one_chunk( } s->reply.handle = 0; - if (s->connection_co && !s->wait_in_flight) { - /* - * We must check s->wait_in_flight, because we may entered by - * nbd_recv_coroutines_wake(), in this case we should not - * wake connection_co here, it will woken by last request. - */ - aio_co_wake(s->connection_co); - } + nbd_recv_coroutines_wake(s, false); return ret; } @@ -1158,11 +997,7 @@ break_loop: qemu_co_mutex_lock(&s->send_mutex); s->in_flight--; - if (s->in_flight == 0 && s->wait_in_flight) { - aio_co_wake(s->connection_co); - } else { - qemu_co_queue_next(&s->free_sema); - } + qemu_co_queue_next(&s->free_sema); qemu_co_mutex_unlock(&s->send_mutex); return false; @@ -1984,6 +1819,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, s->bs = bs; qemu_co_mutex_init(&s->send_mutex); qemu_co_queue_init(&s->free_sema); + qemu_co_mutex_init(&s->receive_mutex); if (!yank_register_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name), errp)) { return -EEXIST; @@ -1998,14 +1834,13 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, s->x_dirty_bitmap, s->tlscreds); /* TODO: Configurable retry-until-timeout behaviour. */ + s->state = NBD_CLIENT_CONNECTING_WAIT; ret = nbd_do_establish_connection(bs, errp); if (ret < 0) { goto fail; } - s->connection_co = qemu_coroutine_create(nbd_connection_entry, s); - bdrv_inc_in_flight(bs); - aio_co_schedule(bdrv_get_aio_context(bs), s->connection_co); + nbd_client_connection_enable_retry(s->conn); return 0; @@ -2159,6 +1994,8 @@ static void nbd_cancel_in_flight(BlockDriverState *bs) s->state = NBD_CLIENT_CONNECTING_NOWAIT; qemu_co_queue_restart_all(&s->free_sema); } + + nbd_co_establish_connection_cancel(s->conn); } static BlockDriver bdrv_nbd = { @@ -2179,10 +2016,6 @@ static BlockDriver bdrv_nbd = { .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, .bdrv_getlength = nbd_getlength, - .bdrv_detach_aio_context = nbd_client_detach_aio_context, - .bdrv_attach_aio_context = nbd_client_attach_aio_context, - .bdrv_co_drain_begin = nbd_client_co_drain_begin, - .bdrv_co_drain_end = nbd_client_co_drain_end, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, @@ -2208,10 +2041,6 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, .bdrv_getlength = nbd_getlength, - .bdrv_detach_aio_context = nbd_client_detach_aio_context, - .bdrv_attach_aio_context = nbd_client_attach_aio_context, - .bdrv_co_drain_begin = nbd_client_co_drain_begin, - .bdrv_co_drain_end = nbd_client_co_drain_end, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, @@ -2237,10 +2066,6 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, .bdrv_getlength = nbd_getlength, - .bdrv_detach_aio_context = nbd_client_detach_aio_context, - .bdrv_attach_aio_context = nbd_client_attach_aio_context, - .bdrv_co_drain_begin = nbd_client_co_drain_begin, - .bdrv_co_drain_end = nbd_client_co_drain_end, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, diff --git a/nbd/client.c b/nbd/client.c index 0c2db4bcba..30d5383cb1 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -1434,9 +1434,7 @@ nbd_read_eof(BlockDriverState *bs, QIOChannel *ioc, void *buffer, size_t size, len = qio_channel_readv(ioc, &iov, 1, errp); if (len == QIO_CHANNEL_ERR_BLOCK) { - bdrv_dec_in_flight(bs); qio_channel_yield(ioc, G_IO_IN); - bdrv_inc_in_flight(bs); continue; } else if (len < 0) { return -EIO; From 1af7737871fb3b66036f5e520acb0a98fc2605f7 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 2 Sep 2021 13:38:05 +0300 Subject: [PATCH 0056/1334] block/nbd: check that received handle is valid If we don't have active request, that waiting for this handle to be received, we should report an error. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210902103805.25686-6-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/nbd.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 8ff6daf43d..5ef462db1b 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -58,6 +58,7 @@ typedef struct { Coroutine *coroutine; uint64_t offset; /* original offset of the request */ bool receiving; /* sleeping in the yield in nbd_receive_replies */ + bool reply_possible; /* reply header not yet received */ } NBDClientRequest; typedef enum NBDClientState { @@ -415,14 +416,7 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) return 0; } ind2 = HANDLE_TO_INDEX(s, s->reply.handle); - if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) { - /* - * We only check that ind2 request exists. But don't check - * whether it is now waiting for the reply header or - * not. We can't just check s->requests[ind2].receiving: - * ind2 request may wait in trying to lock - * receive_mutex. So that's a TODO. - */ + if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].reply_possible) { nbd_channel_error(s, -EINVAL); return -EINVAL; } @@ -468,6 +462,7 @@ static int nbd_co_send_request(BlockDriverState *bs, s->requests[i].coroutine = qemu_coroutine_self(); s->requests[i].offset = request->from; s->requests[i].receiving = false; + s->requests[i].reply_possible = true; request->handle = INDEX_TO_HANDLE(s, i); From d43f1670c7908ee4bd5911ee562053b782c63f6e Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Sep 2021 21:47:53 -0300 Subject: [PATCH 0057/1334] qapi/qdev.json: add DEVICE_UNPLUG_GUEST_ERROR QAPI event At this moment we only provide one event to report a hotunplug error, MEM_UNPLUG_ERROR. As of Linux kernel 5.12 and QEMU 6.0.0, the pseries machine is now able to report unplug errors for other device types, such as CPUs. Instead of creating a (device_type)_UNPLUG_ERROR for each new device, create a generic DEVICE_UNPLUG_GUEST_ERROR event that can be used by all guest side unplug errors in the future. This event has a similar API as the existing DEVICE_DELETED event, always providing the QOM path of the device and dev->id if there's any. With this new generic event, MEM_UNPLUG_ERROR is now marked as deprecated. Reviewed-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Markus Armbruster Signed-off-by: Daniel Henrique Barboza Message-Id: <20210907004755.424931-6-danielhb413@gmail.com> [dwg: Correct missing ')' in stubs/qdev.c] Signed-off-by: David Gibson --- docs/about/deprecated.rst | 10 ++++++++++ qapi/machine.json | 7 ++++++- qapi/qdev.json | 27 ++++++++++++++++++++++++++- stubs/qdev.c | 7 +++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 3c2be84d80..2f7db9a98d 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -238,6 +238,16 @@ The ``I7200`` guest CPU relies on the nanoMIPS ISA, which is deprecated (the ISA has never been upstreamed to a compiler toolchain). Therefore this CPU is also deprecated. + +QEMU API (QAPI) events +---------------------- + +``MEM_UNPLUG_ERROR`` (since 6.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use the more generic event ``DEVICE_UNPLUG_GUEST_ERROR`` instead. + + System emulator machines ------------------------ diff --git a/qapi/machine.json b/qapi/machine.json index 32d47f4e35..66bc34ed8b 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1305,6 +1305,10 @@ # # @msg: Informative message # +# Features: +# @deprecated: This event is deprecated. Use @DEVICE_UNPLUG_GUEST_ERROR +# instead. +# # Since: 2.4 # # Example: @@ -1317,7 +1321,8 @@ # ## { 'event': 'MEM_UNPLUG_ERROR', - 'data': { 'device': 'str', 'msg': 'str' } } + 'data': { 'device': 'str', 'msg': 'str' }, + 'features': ['deprecated'] } ## # @SMPConfiguration: diff --git a/qapi/qdev.json b/qapi/qdev.json index 0e9cb2ae88..d75e68908b 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -84,7 +84,9 @@ # This command merely requests that the guest begin the hot removal # process. Completion of the device removal process is signaled with a # DEVICE_DELETED event. Guest reset will automatically complete removal -# for all devices. +# for all devices. If a guest-side error in the hot removal process is +# detected, the device will not be removed and a DEVICE_UNPLUG_GUEST_ERROR +# event is sent. Some errors cannot be detected. # # Since: 0.14 # @@ -124,3 +126,26 @@ ## { 'event': 'DEVICE_DELETED', 'data': { '*device': 'str', 'path': 'str' } } + +## +# @DEVICE_UNPLUG_GUEST_ERROR: +# +# Emitted when a device hot unplug fails due to a guest reported error. +# +# @device: the device's ID if it has one +# +# @path: the device's QOM path +# +# Since: 6.2 +# +# Example: +# +# <- { "event": "DEVICE_UNPLUG_GUEST_ERROR" +# "data": { "device": "core1", +# "path": "/machine/peripheral/core1" }, +# }, +# "timestamp": { "seconds": 1615570772, "microseconds": 202844 } } +# +## +{ 'event': 'DEVICE_UNPLUG_GUEST_ERROR', + 'data': { '*device': 'str', 'path': 'str' } } diff --git a/stubs/qdev.c b/stubs/qdev.c index 92e6143134..187659f707 100644 --- a/stubs/qdev.c +++ b/stubs/qdev.c @@ -21,3 +21,10 @@ void qapi_event_send_device_deleted(bool has_device, { /* Nothing to do. */ } + +void qapi_event_send_device_unplug_guest_error(bool has_device, + const char *device, + const char *path) +{ + /* Nothing to do. */ +} From 4b08cd567b54e26a0608463311418197dda83e37 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Sep 2021 21:47:54 -0300 Subject: [PATCH 0058/1334] spapr: use DEVICE_UNPLUG_GUEST_ERROR to report unplug errors Linux Kernel 5.12 is now unisolating CPU DRCs in the device_removal error path, signalling that the hotunplug process wasn't successful. This allow us to send a DEVICE_UNPLUG_GUEST_ERROR in drc_unisolate_logical() to signal this error to the management layer. We also have another error path in spapr_memory_unplug_rollback() for configured LMB DRCs. Kernels older than 5.13 will not unisolate the LMBs in the hotunplug error path, but it will reconfigure them. Let's send the DEVICE_UNPLUG_GUEST_ERROR event in that code path as well to cover the case of older kernels. Acked-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Markus Armbruster Signed-off-by: Daniel Henrique Barboza Message-Id: <20210907004755.424931-7-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr.c | 10 +++++++++- hw/ppc/spapr_drc.c | 9 +++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ac11c8a728..270106975b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -29,6 +29,7 @@ #include "qemu/datadir.h" #include "qapi/error.h" #include "qapi/qapi-events-machine.h" +#include "qapi/qapi-events-qdev.h" #include "qapi/visitor.h" #include "sysemu/sysemu.h" #include "sysemu/hostmem.h" @@ -3686,11 +3687,18 @@ void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev) /* * Tell QAPI that something happened and the memory - * hotunplug wasn't successful. + * hotunplug wasn't successful. Keep sending + * MEM_UNPLUG_ERROR even while sending + * DEVICE_UNPLUG_GUEST_ERROR until the deprecation of + * MEM_UNPLUG_ERROR is due. */ qapi_error = g_strdup_printf("Memory hotunplug rejected by the guest " "for device %s", dev->id); + qapi_event_send_mem_unplug_error(dev->id ? : "", qapi_error); + + qapi_event_send_device_unplug_guest_error(!!dev->id, dev->id, + dev->canonical_path); } /* Callback to be called during DRC release. */ diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index a4d9496f76..f8ac0a10df 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -17,6 +17,8 @@ #include "hw/ppc/spapr_drc.h" #include "qom/object.h" #include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/qapi-events-qdev.h" #include "qapi/visitor.h" #include "qemu/error-report.h" #include "hw/ppc/spapr.h" /* for RTAS return codes */ @@ -173,10 +175,9 @@ static uint32_t drc_unisolate_logical(SpaprDrc *drc) "for device %s", drc->dev->id); } - /* - * TODO: send a QAPI DEVICE_UNPLUG_ERROR event when - * it is implemented. - */ + qapi_event_send_device_unplug_guest_error(!!drc->dev->id, + drc->dev->id, + drc->dev->canonical_path); } return RTAS_OUT_SUCCESS; /* Nothing to do */ From 46f2c282c32ed0e0c9366e018b98fd58a859682e Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 6 Sep 2021 21:47:55 -0300 Subject: [PATCH 0059/1334] memory_hotplug.c: send DEVICE_UNPLUG_GUEST_ERROR in acpi_memory_hotplug_write() MEM_UNPLUG_ERROR is deprecated since the introduction of DEVICE_UNPLUG_GUEST_ERROR. Keep emitting both while the deprecation of MEM_UNPLUG_ERROR is pending. CC: Michael S. Tsirkin CC: Igor Mammedov Acked-by: Michael S. Tsirkin Reviewed-by: Greg Kurz Reviewed-by: David Gibson Reviewed-by: Igor Mammedov Reviewed-by: Markus Armbruster Signed-off-by: Daniel Henrique Barboza Message-Id: <20210907004755.424931-8-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/acpi/memory_hotplug.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 6a71de408b..d0fffcf787 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -8,6 +8,7 @@ #include "qapi/error.h" #include "qapi/qapi-events-acpi.h" #include "qapi/qapi-events-machine.h" +#include "qapi/qapi-events-qdev.h" #define MEMORY_SLOTS_NUMBER "MDNR" #define MEMORY_HOTPLUG_IO_REGION "HPMR" @@ -178,8 +179,16 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, hotplug_handler_unplug(hotplug_ctrl, dev, &local_err); if (local_err) { trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector); + + /* + * Send both MEM_UNPLUG_ERROR and DEVICE_UNPLUG_GUEST_ERROR + * while the deprecation of MEM_UNPLUG_ERROR is + * pending. + */ qapi_event_send_mem_unplug_error(dev->id ? : "", error_get_pretty(local_err)); + qapi_event_send_device_unplug_guest_error(!!dev->id, dev->id, + dev->canonical_path); error_free(local_err); break; } From 2eb1ef73b6e661bb80f6ab7d863e2528d12c434a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 20 Sep 2021 08:12:00 +0200 Subject: [PATCH 0060/1334] target/ppc: Convert debug to trace events (exceptions) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Message-Id: <20210920061203.989563-2-clg@kaod.org> Signed-off-by: David Gibson --- target/ppc/excp_helper.c | 38 ++++++++++---------------------------- target/ppc/trace-events | 8 ++++++++ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index d7e32ee107..b7d1767920 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -23,20 +23,14 @@ #include "internal.h" #include "helper_regs.h" +#include "trace.h" + #ifdef CONFIG_TCG #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #endif -/* #define DEBUG_OP */ /* #define DEBUG_SOFTWARE_TLB */ -/* #define DEBUG_EXCEPTIONS */ - -#ifdef DEBUG_EXCEPTIONS -# define LOG_EXCP(...) qemu_log(__VA_ARGS__) -#else -# define LOG_EXCP(...) do { } while (0) -#endif /*****************************************************************************/ /* Exception processing */ @@ -414,12 +408,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } break; case POWERPC_EXCP_DSI: /* Data storage exception */ - LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx - "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); + trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); break; case POWERPC_EXCP_ISI: /* Instruction storage exception */ - LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx - "\n", msr, env->nip); + trace_ppc_excp_isi(msr, env->nip); msr |= env->error_code; break; case POWERPC_EXCP_EXTERNAL: /* External input */ @@ -474,7 +466,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) switch (env->error_code & ~0xF) { case POWERPC_EXCP_FP: if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { - LOG_EXCP("Ignore floating point exception\n"); + trace_ppc_excp_fp_ignore(); cs->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; return; @@ -489,7 +481,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) env->spr[SPR_BOOKE_ESR] = ESR_FP; break; case POWERPC_EXCP_INVAL: - LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); + trace_ppc_excp_inval(env->nip); msr |= 0x00080000; env->spr[SPR_BOOKE_ESR] = ESR_PIL; break; @@ -547,10 +539,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) break; case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ /* FIT on 4xx */ - LOG_EXCP("FIT exception\n"); + trace_ppc_excp_print("FIT"); break; case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - LOG_EXCP("WDT exception\n"); + trace_ppc_excp_print("WDT"); switch (excp_model) { case POWERPC_EXCP_BOOKE: srr0 = SPR_BOOKE_CSRR0; @@ -657,7 +649,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) #endif break; case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ - LOG_EXCP("PIT exception\n"); + trace_ppc_excp_print("PIT"); break; case POWERPC_EXCP_IO: /* IO error exception */ /* XXX: TODO */ @@ -1115,14 +1107,6 @@ bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) #endif /* !CONFIG_USER_ONLY */ -#if defined(DEBUG_OP) -static void cpu_dump_rfi(target_ulong RA, target_ulong msr) -{ - qemu_log("Return from exception at " TARGET_FMT_lx " with flags " - TARGET_FMT_lx "\n", RA, msr); -} -#endif - /*****************************************************************************/ /* Exceptions processing helpers */ @@ -1221,9 +1205,7 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) /* XXX: beware: this is false if VLE is supported */ env->nip = nip & ~((target_ulong)0x00000003); hreg_store_msr(env, msr, 1); -#if defined(DEBUG_OP) - cpu_dump_rfi(env->nip, env->msr); -#endif + trace_ppc_excp_rfi(env->nip, env->msr); /* * No need to raise an exception here, as rfi is always the last * insn of a TB diff --git a/target/ppc/trace-events b/target/ppc/trace-events index c88cfccf8d..53b107f56e 100644 --- a/target/ppc/trace-events +++ b/target/ppc/trace-events @@ -28,3 +28,11 @@ kvm_handle_epr(void) "handle epr" kvm_handle_watchdog_expiry(void) "handle watchdog expiry" kvm_handle_debug_exception(void) "handle debug exception" kvm_handle_nmi_exception(void) "handle NMI exception" + +# excp_helper.c +ppc_excp_rfi(uint64_t nip, uint64_t msr) "Return from exception at 0x%" PRIx64 " with flags 0x%016" PRIx64 +ppc_excp_dsi(uint64_t dsisr, uint64_t dar) "DSI exception: DSISR=0x%" PRIx64 " DAR=0x%" PRIx64 +ppc_excp_isi(uint64_t msr, uint64_t nip) "ISI exception: msr=0x%016" PRIx64 " nip=0x%" PRIx64 +ppc_excp_fp_ignore(void) "Ignore floating point exception" +ppc_excp_inval(uint64_t nip) "Invalid instruction at 0x%" PRIx64 +ppc_excp_print(const char *excp) "%s exception" From 7279810b67b6ea16f9b6060693a286dc6deb3036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 20 Sep 2021 08:12:01 +0200 Subject: [PATCH 0061/1334] target/ppc: Replace debug messages by asserts for unknown IRQ pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an unknown pin of the IRQ controller is raised, something is very wrong in the QEMU model. It is better to abort. Signed-off-by: Cédric Le Goater Message-Id: <20210920061203.989563-3-clg@kaod.org> Signed-off-by: David Gibson --- hw/ppc/ppc.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 7375bf4fa9..a327206a0a 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -165,9 +165,7 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); break; default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; + g_assert_not_reached(); } if (level) env->irq_input_state |= 1 << pin; @@ -252,9 +250,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level) /* XXX: TODO */ break; default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; + g_assert_not_reached(); } if (level) env->irq_input_state |= 1 << pin; @@ -287,9 +283,7 @@ static void power7_set_irq(void *opaque, int pin, int level) ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; + g_assert_not_reached(); } } @@ -323,9 +317,7 @@ static void power9_set_irq(void *opaque, int pin, int level) ppc_set_irq(cpu, PPC_INTERRUPT_HVIRT, level); break; default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; + g_assert_not_reached(); } } @@ -459,9 +451,7 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); break; default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; + g_assert_not_reached(); } if (level) env->irq_input_state |= 1 << pin; @@ -523,9 +513,7 @@ static void ppce500_set_irq(void *opaque, int pin, int level) ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); break; default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; + g_assert_not_reached(); } if (level) env->irq_input_state |= 1 << pin; From 1db3632a14f44e243068bcf89bcf0739b657972b Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Fri, 17 Sep 2021 08:47:50 -0300 Subject: [PATCH 0062/1334] target/ppc: add LPCR[HR] to DisasContext and hflags Add a Host Radix field (hr) in DisasContext with LPCR[HR] value to allow us to decide between Radix and HPT while validating instructions arguments. Note that PowerISA v3.1 does not require LPCR[HR] and PATE.HR to match if the thread is in ultravisor/hypervisor real addressing mode, so ctx->hr may be invalid if ctx->hv and ctx->dr are set. Signed-off-by: Matheus Ferst Reviewed-by: Daniel Henrique Barboza Message-Id: <20210917114751.206845-2-matheus.ferst@eldorado.org.br> Reviewed-by: Daniel Henrique Barboza Signed-off-by: David Gibson --- target/ppc/cpu.h | 1 + target/ppc/helper_regs.c | 3 +++ target/ppc/translate.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 01d3773bc7..baa4e7c34d 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -600,6 +600,7 @@ enum { HFLAGS_64 = 2, /* computed from MSR_CE and MSR_SF */ HFLAGS_GTSE = 3, /* computed from SPR_LPCR[GTSE] */ HFLAGS_DR = 4, /* MSR_DR */ + HFLAGS_HR = 5, /* computed from SPR_LPCR[HR] */ HFLAGS_SPE = 6, /* from MSR_SPE if cpu has SPE; avoid overlap w/ MSR_VR */ HFLAGS_TM = 8, /* computed from MSR_TM */ HFLAGS_BE = 9, /* MSR_BE -- from elsewhere on embedded ppc */ diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 405450d863..1bfb480ecf 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -106,6 +106,9 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (env->spr[SPR_LPCR] & LPCR_GTSE) { hflags |= 1 << HFLAGS_GTSE; } + if (env->spr[SPR_LPCR] & LPCR_HR) { + hflags |= 1 << HFLAGS_HR; + } #ifndef CONFIG_USER_ONLY if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 5d8b06bd80..9af1624ad2 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -175,6 +175,7 @@ struct DisasContext { bool spe_enabled; bool tm_enabled; bool gtse; + bool hr; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint32_t flags; @@ -8539,6 +8540,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->vsx_enabled = (hflags >> HFLAGS_VSX) & 1; ctx->tm_enabled = (hflags >> HFLAGS_TM) & 1; ctx->gtse = (hflags >> HFLAGS_GTSE) & 1; + ctx->hr = (hflags >> HFLAGS_HR) & 1; ctx->singlestep_enabled = 0; if ((hflags >> HFLAGS_SE) & 1) { From 92fb92d3e9c67f83cbbca0e2b9ca2d88fefd6643 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Fri, 17 Sep 2021 08:47:51 -0300 Subject: [PATCH 0063/1334] target/ppc: Check privilege level based on PSR and LPCR[HR] in tlbie[l] PowerISA v3.0B made tlbie[l] hypervisor privileged when PSR=0 and HR=1. To allow the check at translation time, we'll use the HR bit of LPCR to check the MMU mode instead of the PATE.HR. Signed-off-by: Matheus Ferst Message-Id: <20210917114751.206845-3-matheus.ferst@eldorado.org.br> Reviewed-by: Daniel Henrique Barboza Signed-off-by: David Gibson --- target/ppc/translate.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 9af1624ad2..b985e9e55b 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -5517,7 +5517,15 @@ static void gen_tlbiel(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) GEN_PRIV; #else - CHK_SV; + bool psr = (ctx->opcode >> 17) & 0x1; + + if (ctx->pr || (!ctx->hv && !psr && ctx->hr)) { + /* + * tlbiel is privileged except when PSR=0 and HR=1, making it + * hypervisor privileged. + */ + GEN_PRIV; + } gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ @@ -5529,12 +5537,20 @@ static void gen_tlbie(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) GEN_PRIV; #else + bool psr = (ctx->opcode >> 17) & 0x1; TCGv_i32 t1; - if (ctx->gtse) { - CHK_SV; /* If gtse is set then tlbie is supervisor privileged */ - } else { - CHK_HV; /* Else hypervisor privileged */ + if (ctx->pr) { + /* tlbie is privileged... */ + GEN_PRIV; + } else if (!ctx->hv) { + if (!ctx->gtse || (!psr && ctx->hr)) { + /* + * ... except when GTSE=0 or when PSR=0 and HR=1, making it + * hypervisor privileged. + */ + GEN_PRIV; + } } if (NARROW_MODE(ctx)) { From d98dbe2a2bec249847e789d0cb42eb5e7e93343d Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 20 Sep 2021 14:49:41 -0300 Subject: [PATCH 0064/1334] spapr_numa.c: split FORM1 code into helpers The upcoming FORM2 NUMA affinity will support asymmetric NUMA topologies and doesn't need be concerned with all the legacy support for older pseries FORM1 guests. We're also not going to calculate associativity domains based on numa distance (via spapr_numa_define_associativity_domains) since the distances will be written directly into new DT properties. Let's split FORM1 code into its own functions to allow for easier insertion of FORM2 logic later on. Reviewed-by: Greg Kurz Signed-off-by: Daniel Henrique Barboza Message-Id: <20210920174947.556324-2-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr_numa.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index 779f18b994..786def7c73 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -92,7 +92,7 @@ static uint8_t spapr_numa_get_numa_level(uint8_t distance) return 0; } -static void spapr_numa_define_associativity_domains(SpaprMachineState *spapr) +static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) { MachineState *ms = MACHINE(spapr); NodeInfo *numa_info = ms->numa_state->nodes; @@ -155,8 +155,11 @@ static void spapr_numa_define_associativity_domains(SpaprMachineState *spapr) } -void spapr_numa_associativity_init(SpaprMachineState *spapr, - MachineState *machine) +/* + * Set NUMA machine state data based on FORM1 affinity semantics. + */ +static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, + MachineState *machine) { SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); int nb_numa_nodes = machine->numa_state->num_nodes; @@ -225,7 +228,13 @@ void spapr_numa_associativity_init(SpaprMachineState *spapr, exit(EXIT_FAILURE); } - spapr_numa_define_associativity_domains(spapr); + spapr_numa_define_FORM1_domains(spapr); +} + +void spapr_numa_associativity_init(SpaprMachineState *spapr, + MachineState *machine) +{ + spapr_numa_FORM1_affinity_init(spapr, machine); } void spapr_numa_write_associativity_dt(SpaprMachineState *spapr, void *fdt, @@ -302,12 +311,8 @@ int spapr_numa_write_assoc_lookup_arrays(SpaprMachineState *spapr, void *fdt, return ret; } -/* - * Helper that writes ibm,associativity-reference-points and - * max-associativity-domains in the RTAS pointed by @rtas - * in the DT @fdt. - */ -void spapr_numa_write_rtas_dt(SpaprMachineState *spapr, void *fdt, int rtas) +static void spapr_numa_FORM1_write_rtas_dt(SpaprMachineState *spapr, + void *fdt, int rtas) { MachineState *ms = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); @@ -365,6 +370,16 @@ void spapr_numa_write_rtas_dt(SpaprMachineState *spapr, void *fdt, int rtas) maxdomains, sizeof(maxdomains))); } +/* + * Helper that writes ibm,associativity-reference-points and + * max-associativity-domains in the RTAS pointed by @rtas + * in the DT @fdt. + */ +void spapr_numa_write_rtas_dt(SpaprMachineState *spapr, void *fdt, int rtas) +{ + spapr_numa_FORM1_write_rtas_dt(spapr, fdt, rtas); +} + static target_ulong h_home_node_associativity(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, From afa3b3c9ee8ae2c7c25c93f2d6eebe09e962cd3a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 20 Sep 2021 14:49:42 -0300 Subject: [PATCH 0065/1334] spapr_numa.c: scrap 'legacy_numa' concept When first introduced, 'legacy_numa' was a way to refer to guests that either wouldn't be affected by associativity domain calculations, namely the ones with only 1 NUMA node, and pre 5.2 guests that shouldn't be affected by it because it would be an userspace change. Calling these cases 'legacy_numa' was a convenient way to label these cases. We're about to introduce a new NUMA affinity, FORM2, and this concept of 'legacy_numa' is now a bit misleading because, although it is called 'legacy' it is in fact a FORM1 exclusive contraint. This patch removes spapr_machine_using_legacy_numa() and open code the conditions in each caller. While we're at it, move the chunk inside spapr_numa_FORM1_affinity_init() that sets all numa_assoc_array domains with 'node_id' to spapr_numa_define_FORM1_domains(). This chunk was being executed if !pre_5_2_numa_associativity and num_nodes => 1, the same conditions in which spapr_numa_define_FORM1_domains() is called shortly after. Reviewed-by: Greg Kurz Signed-off-by: Daniel Henrique Barboza Message-Id: <20210920174947.556324-3-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr_numa.c | 47 +++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index 786def7c73..bf520d42b2 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -19,15 +19,6 @@ /* Moved from hw/ppc/spapr_pci_nvlink2.c */ #define SPAPR_GPU_NUMA_ID (cpu_to_be32(1)) -static bool spapr_machine_using_legacy_numa(SpaprMachineState *spapr) -{ - MachineState *machine = MACHINE(spapr); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); - - return smc->pre_5_2_numa_associativity || - machine->numa_state->num_nodes <= 1; -} - static bool spapr_numa_is_symmetrical(MachineState *ms) { int src, dst; @@ -97,7 +88,18 @@ static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) MachineState *ms = MACHINE(spapr); NodeInfo *numa_info = ms->numa_state->nodes; int nb_numa_nodes = ms->numa_state->num_nodes; - int src, dst, i; + int src, dst, i, j; + + /* + * Fill all associativity domains of non-zero NUMA nodes with + * node_id. This is required because the default value (0) is + * considered a match with associativity domains of node 0. + */ + for (i = 1; i < nb_numa_nodes; i++) { + for (j = 1; j < MAX_DISTANCE_REF_POINTS; j++) { + spapr->numa_assoc_array[i][j] = cpu_to_be32(i); + } + } for (src = 0; src < nb_numa_nodes; src++) { for (dst = src; dst < nb_numa_nodes; dst++) { @@ -164,7 +166,6 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); int nb_numa_nodes = machine->numa_state->num_nodes; int i, j, max_nodes_with_gpus; - bool using_legacy_numa = spapr_machine_using_legacy_numa(spapr); /* * For all associativity arrays: first position is the size, @@ -178,17 +179,6 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, for (i = 0; i < nb_numa_nodes; i++) { spapr->numa_assoc_array[i][0] = cpu_to_be32(MAX_DISTANCE_REF_POINTS); spapr->numa_assoc_array[i][MAX_DISTANCE_REF_POINTS] = cpu_to_be32(i); - - /* - * Fill all associativity domains of non-zero NUMA nodes with - * node_id. This is required because the default value (0) is - * considered a match with associativity domains of node 0. - */ - if (!using_legacy_numa && i != 0) { - for (j = 1; j < MAX_DISTANCE_REF_POINTS; j++) { - spapr->numa_assoc_array[i][j] = cpu_to_be32(i); - } - } } /* @@ -214,11 +204,13 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, } /* - * Legacy NUMA guests (pseries-5.1 and older, or guests with only - * 1 NUMA node) will not benefit from anything we're going to do - * after this point. + * Guests pseries-5.1 and older uses zeroed associativity domains, + * i.e. no domain definition based on NUMA distance input. + * + * Same thing with guests that have only one NUMA node. */ - if (using_legacy_numa) { + if (smc->pre_5_2_numa_associativity || + machine->numa_state->num_nodes <= 1) { return; } @@ -334,7 +326,8 @@ static void spapr_numa_FORM1_write_rtas_dt(SpaprMachineState *spapr, cpu_to_be32(maxdomain) }; - if (spapr_machine_using_legacy_numa(spapr)) { + if (smc->pre_5_2_numa_associativity || + ms->numa_state->num_nodes <= 1) { uint32_t legacy_refpoints[] = { cpu_to_be32(0x4), cpu_to_be32(0x4), From 3a6e4ce684e48988f9736aecc6b365609e83f8d1 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 20 Sep 2021 14:49:43 -0300 Subject: [PATCH 0066/1334] spapr_numa.c: parametrize FORM1 macros The next preliminary step to introduce NUMA FORM2 affinity is to make the existing code independent of FORM1 macros and values, i.e. MAX_DISTANCE_REF_POINTS, NUMA_ASSOC_SIZE and VCPU_ASSOC_SIZE. This patch accomplishes that by doing the following: - move the NUMA related macros from spapr.h to spapr_numa.c where they are used. spapr.h gets instead a 'NUMA_NODES_MAX_NUM' macro that is used to refer to the maximum number of NUMA nodes, including GPU nodes, that the machine can support; - MAX_DISTANCE_REF_POINTS and NUMA_ASSOC_SIZE are renamed to FORM1_DIST_REF_POINTS and FORM1_NUMA_ASSOC_SIZE. These FORM1 specific macros are used in FORM1 init functions; - code that uses MAX_DISTANCE_REF_POINTS now retrieves the max_dist_ref_points value using get_max_dist_ref_points(). NUMA_ASSOC_SIZE is replaced by get_numa_assoc_size() and VCPU_ASSOC_SIZE is replaced by get_vcpu_assoc_size(). These functions are used by the generic device tree functions and h_home_node_associativity() and will allow them to switch between FORM1 and FORM2 without changing their core logic. Reviewed-by: Greg Kurz Signed-off-by: Daniel Henrique Barboza Message-Id: <20210920174947.556324-4-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr_numa.c | 74 ++++++++++++++++++++++++++++++------------ include/hw/ppc/spapr.h | 34 +++++++++---------- 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index bf520d42b2..08e2d6aed8 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -19,6 +19,33 @@ /* Moved from hw/ppc/spapr_pci_nvlink2.c */ #define SPAPR_GPU_NUMA_ID (cpu_to_be32(1)) +/* + * Retrieves max_dist_ref_points of the current NUMA affinity. + */ +static int get_max_dist_ref_points(SpaprMachineState *spapr) +{ + return FORM1_DIST_REF_POINTS; +} + +/* + * Retrieves numa_assoc_size of the current NUMA affinity. + */ +static int get_numa_assoc_size(SpaprMachineState *spapr) +{ + return FORM1_NUMA_ASSOC_SIZE; +} + +/* + * Retrieves vcpu_assoc_size of the current NUMA affinity. + * + * vcpu_assoc_size is the size of ibm,associativity array + * for CPUs, which has an extra element (vcpu_id) in the end. + */ +static int get_vcpu_assoc_size(SpaprMachineState *spapr) +{ + return get_numa_assoc_size(spapr) + 1; +} + static bool spapr_numa_is_symmetrical(MachineState *ms) { int src, dst; @@ -96,7 +123,7 @@ static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) * considered a match with associativity domains of node 0. */ for (i = 1; i < nb_numa_nodes; i++) { - for (j = 1; j < MAX_DISTANCE_REF_POINTS; j++) { + for (j = 1; j < FORM1_DIST_REF_POINTS; j++) { spapr->numa_assoc_array[i][j] = cpu_to_be32(i); } } @@ -134,7 +161,7 @@ static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) * * The Linux kernel will assume that the distance between src and * dst, in this case of no match, is 10 (local distance) doubled - * for each NUMA it didn't match. We have MAX_DISTANCE_REF_POINTS + * for each NUMA it didn't match. We have FORM1_DIST_REF_POINTS * levels (4), so this gives us 10*2*2*2*2 = 160. * * This logic can be seen in the Linux kernel source code, as of @@ -169,7 +196,7 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, /* * For all associativity arrays: first position is the size, - * position MAX_DISTANCE_REF_POINTS is always the numa_id, + * position FORM1_DIST_REF_POINTS is always the numa_id, * represented by the index 'i'. * * This will break on sparse NUMA setups, when/if QEMU starts @@ -177,8 +204,8 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, * 'i' will be a valid node_id set by the user. */ for (i = 0; i < nb_numa_nodes; i++) { - spapr->numa_assoc_array[i][0] = cpu_to_be32(MAX_DISTANCE_REF_POINTS); - spapr->numa_assoc_array[i][MAX_DISTANCE_REF_POINTS] = cpu_to_be32(i); + spapr->numa_assoc_array[i][0] = cpu_to_be32(FORM1_DIST_REF_POINTS); + spapr->numa_assoc_array[i][FORM1_DIST_REF_POINTS] = cpu_to_be32(i); } /* @@ -192,15 +219,15 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, max_nodes_with_gpus = nb_numa_nodes + NVGPU_MAX_NUM; for (i = nb_numa_nodes; i < max_nodes_with_gpus; i++) { - spapr->numa_assoc_array[i][0] = cpu_to_be32(MAX_DISTANCE_REF_POINTS); + spapr->numa_assoc_array[i][0] = cpu_to_be32(FORM1_DIST_REF_POINTS); - for (j = 1; j < MAX_DISTANCE_REF_POINTS; j++) { + for (j = 1; j < FORM1_DIST_REF_POINTS; j++) { uint32_t gpu_assoc = smc->pre_5_1_assoc_refpoints ? SPAPR_GPU_NUMA_ID : cpu_to_be32(i); spapr->numa_assoc_array[i][j] = gpu_assoc; } - spapr->numa_assoc_array[i][MAX_DISTANCE_REF_POINTS] = cpu_to_be32(i); + spapr->numa_assoc_array[i][FORM1_DIST_REF_POINTS] = cpu_to_be32(i); } /* @@ -234,13 +261,15 @@ void spapr_numa_write_associativity_dt(SpaprMachineState *spapr, void *fdt, { _FDT((fdt_setprop(fdt, offset, "ibm,associativity", spapr->numa_assoc_array[nodeid], - sizeof(spapr->numa_assoc_array[nodeid])))); + get_numa_assoc_size(spapr) * sizeof(uint32_t)))); } static uint32_t *spapr_numa_get_vcpu_assoc(SpaprMachineState *spapr, PowerPCCPU *cpu) { - uint32_t *vcpu_assoc = g_new(uint32_t, VCPU_ASSOC_SIZE); + int max_distance_ref_points = get_max_dist_ref_points(spapr); + int vcpu_assoc_size = get_vcpu_assoc_size(spapr); + uint32_t *vcpu_assoc = g_new(uint32_t, vcpu_assoc_size); int index = spapr_get_vcpu_id(cpu); /* @@ -249,10 +278,10 @@ static uint32_t *spapr_numa_get_vcpu_assoc(SpaprMachineState *spapr, * 0, put cpu_id last, then copy the remaining associativity * domains. */ - vcpu_assoc[0] = cpu_to_be32(MAX_DISTANCE_REF_POINTS + 1); - vcpu_assoc[VCPU_ASSOC_SIZE - 1] = cpu_to_be32(index); + vcpu_assoc[0] = cpu_to_be32(max_distance_ref_points + 1); + vcpu_assoc[vcpu_assoc_size - 1] = cpu_to_be32(index); memcpy(vcpu_assoc + 1, spapr->numa_assoc_array[cpu->node_id] + 1, - (VCPU_ASSOC_SIZE - 2) * sizeof(uint32_t)); + (vcpu_assoc_size - 2) * sizeof(uint32_t)); return vcpu_assoc; } @@ -261,12 +290,13 @@ int spapr_numa_fixup_cpu_dt(SpaprMachineState *spapr, void *fdt, int offset, PowerPCCPU *cpu) { g_autofree uint32_t *vcpu_assoc = NULL; + int vcpu_assoc_size = get_vcpu_assoc_size(spapr); vcpu_assoc = spapr_numa_get_vcpu_assoc(spapr, cpu); /* Advertise NUMA via ibm,associativity */ return fdt_setprop(fdt, offset, "ibm,associativity", vcpu_assoc, - VCPU_ASSOC_SIZE * sizeof(uint32_t)); + vcpu_assoc_size * sizeof(uint32_t)); } @@ -274,17 +304,18 @@ int spapr_numa_write_assoc_lookup_arrays(SpaprMachineState *spapr, void *fdt, int offset) { MachineState *machine = MACHINE(spapr); + int max_distance_ref_points = get_max_dist_ref_points(spapr); int nb_numa_nodes = machine->numa_state->num_nodes; int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; uint32_t *int_buf, *cur_index, buf_len; int ret, i; /* ibm,associativity-lookup-arrays */ - buf_len = (nr_nodes * MAX_DISTANCE_REF_POINTS + 2) * sizeof(uint32_t); + buf_len = (nr_nodes * max_distance_ref_points + 2) * sizeof(uint32_t); cur_index = int_buf = g_malloc0(buf_len); int_buf[0] = cpu_to_be32(nr_nodes); /* Number of entries per associativity list */ - int_buf[1] = cpu_to_be32(MAX_DISTANCE_REF_POINTS); + int_buf[1] = cpu_to_be32(max_distance_ref_points); cur_index += 2; for (i = 0; i < nr_nodes; i++) { /* @@ -293,8 +324,8 @@ int spapr_numa_write_assoc_lookup_arrays(SpaprMachineState *spapr, void *fdt, */ uint32_t *associativity = spapr->numa_assoc_array[i]; memcpy(cur_index, ++associativity, - sizeof(uint32_t) * MAX_DISTANCE_REF_POINTS); - cur_index += MAX_DISTANCE_REF_POINTS; + sizeof(uint32_t) * max_distance_ref_points); + cur_index += max_distance_ref_points; } ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf, (cur_index - int_buf) * sizeof(uint32_t)); @@ -383,6 +414,7 @@ static target_ulong h_home_node_associativity(PowerPCCPU *cpu, target_ulong procno = args[1]; PowerPCCPU *tcpu; int idx, assoc_idx; + int vcpu_assoc_size = get_vcpu_assoc_size(spapr); /* only support procno from H_REGISTER_VPA */ if (flags != 0x1) { @@ -401,7 +433,7 @@ static target_ulong h_home_node_associativity(PowerPCCPU *cpu, * 12 associativity domains for vcpus. Assert and bail if that's * not the case. */ - G_STATIC_ASSERT((VCPU_ASSOC_SIZE - 1) <= 12); + g_assert((vcpu_assoc_size - 1) <= 12); vcpu_assoc = spapr_numa_get_vcpu_assoc(spapr, tcpu); /* assoc_idx starts at 1 to skip associativity size */ @@ -422,9 +454,9 @@ static target_ulong h_home_node_associativity(PowerPCCPU *cpu, * macro. The ternary will fill the remaining registers with -1 * after we went through vcpu_assoc[]. */ - a = assoc_idx < VCPU_ASSOC_SIZE ? + a = assoc_idx < vcpu_assoc_size ? be32_to_cpu(vcpu_assoc[assoc_idx++]) : -1; - b = assoc_idx < VCPU_ASSOC_SIZE ? + b = assoc_idx < vcpu_assoc_size ? be32_to_cpu(vcpu_assoc[assoc_idx++]) : -1; args[idx] = ASSOCIATIVITY(a, b); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 637652ad16..814e087e98 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -100,23 +100,23 @@ typedef enum { #define FDT_MAX_SIZE 0x200000 -/* - * NUMA related macros. MAX_DISTANCE_REF_POINTS was taken - * from Linux kernel arch/powerpc/mm/numa.h. It represents the - * amount of associativity domains for non-CPU resources. - * - * NUMA_ASSOC_SIZE is the base array size of an ibm,associativity - * array for any non-CPU resource. - * - * VCPU_ASSOC_SIZE represents the size of ibm,associativity array - * for CPUs, which has an extra element (vcpu_id) in the end. - */ -#define MAX_DISTANCE_REF_POINTS 4 -#define NUMA_ASSOC_SIZE (MAX_DISTANCE_REF_POINTS + 1) -#define VCPU_ASSOC_SIZE (NUMA_ASSOC_SIZE + 1) +/* Max number of GPUs per system */ +#define NVGPU_MAX_NUM 6 -/* Max number of these GPUsper a physical box */ -#define NVGPU_MAX_NUM 6 +/* Max number of NUMA nodes */ +#define NUMA_NODES_MAX_NUM (MAX_NODES + NVGPU_MAX_NUM) + +/* + * NUMA FORM1 macros. FORM1_DIST_REF_POINTS was taken from + * MAX_DISTANCE_REF_POINTS in arch/powerpc/mm/numa.h from Linux + * kernel source. It represents the amount of associativity domains + * for non-CPU resources. + * + * FORM1_NUMA_ASSOC_SIZE is the base array size of an ibm,associativity + * array for any non-CPU resource. + */ +#define FORM1_DIST_REF_POINTS 4 +#define FORM1_NUMA_ASSOC_SIZE (FORM1_DIST_REF_POINTS + 1) typedef struct SpaprCapabilities SpaprCapabilities; struct SpaprCapabilities { @@ -249,7 +249,7 @@ struct SpaprMachineState { unsigned gpu_numa_id; SpaprTpmProxy *tpm_proxy; - uint32_t numa_assoc_array[MAX_NODES + NVGPU_MAX_NUM][NUMA_ASSOC_SIZE]; + uint32_t numa_assoc_array[NUMA_NODES_MAX_NUM][FORM1_NUMA_ASSOC_SIZE]; Error *fwnmi_migration_blocker; }; From a165ac67c3ff8dc7065aa827de36da3785ec83fd Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 20 Sep 2021 14:49:44 -0300 Subject: [PATCH 0067/1334] spapr_numa.c: rename numa_assoc_array to FORM1_assoc_array Introducing a new NUMA affinity, FORM2, requires a new mechanism to switch between affinity modes after CAS. Also, we want FORM2 data structures and functions to be completely separated from the existing FORM1 code, allowing us to avoid adding new code that inherits the existing complexity of FORM1. The idea of switching values used by the write_dt() functions in spapr_numa.c was already introduced in the previous patch, and the same approach will be used when dealing with the FORM1 and FORM2 arrays. We can accomplish that by that by renaming the existing numa_assoc_array to FORM1_assoc_array, which now is used exclusively to handle FORM1 affinity data. A new helper get_associativity() is then introduced to be used by the write_dt() functions to retrieve the current ibm,associativity array of a given node, after considering affinity selection that might have been done during CAS. All code that was using numa_assoc_array now needs to retrieve the array by calling this function. This will allow for an easier plug of FORM2 data later on. Signed-off-by: Daniel Henrique Barboza Message-Id: <20210920174947.556324-5-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr_hcall.c | 1 + hw/ppc/spapr_numa.c | 38 +++++++++++++++++++++++++------------- include/hw/ppc/spapr.h | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 0e9a5b2e40..9056644890 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -17,6 +17,7 @@ #include "kvm_ppc.h" #include "hw/ppc/fdt.h" #include "hw/ppc/spapr_ovec.h" +#include "hw/ppc/spapr_numa.h" #include "mmu-book3s-v3.h" #include "hw/mem/memory-device.h" diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index 08e2d6aed8..dce9ce987a 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -46,6 +46,15 @@ static int get_vcpu_assoc_size(SpaprMachineState *spapr) return get_numa_assoc_size(spapr) + 1; } +/* + * Retrieves the ibm,associativity array of NUMA node 'node_id' + * for the current NUMA affinity. + */ +static const uint32_t *get_associativity(SpaprMachineState *spapr, int node_id) +{ + return spapr->FORM1_assoc_array[node_id]; +} + static bool spapr_numa_is_symmetrical(MachineState *ms) { int src, dst; @@ -124,7 +133,7 @@ static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) */ for (i = 1; i < nb_numa_nodes; i++) { for (j = 1; j < FORM1_DIST_REF_POINTS; j++) { - spapr->numa_assoc_array[i][j] = cpu_to_be32(i); + spapr->FORM1_assoc_array[i][j] = cpu_to_be32(i); } } @@ -176,8 +185,8 @@ static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) * and going up to 0x1. */ for (i = n_level; i > 0; i--) { - assoc_src = spapr->numa_assoc_array[src][i]; - spapr->numa_assoc_array[dst][i] = assoc_src; + assoc_src = spapr->FORM1_assoc_array[src][i]; + spapr->FORM1_assoc_array[dst][i] = assoc_src; } } } @@ -204,8 +213,8 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, * 'i' will be a valid node_id set by the user. */ for (i = 0; i < nb_numa_nodes; i++) { - spapr->numa_assoc_array[i][0] = cpu_to_be32(FORM1_DIST_REF_POINTS); - spapr->numa_assoc_array[i][FORM1_DIST_REF_POINTS] = cpu_to_be32(i); + spapr->FORM1_assoc_array[i][0] = cpu_to_be32(FORM1_DIST_REF_POINTS); + spapr->FORM1_assoc_array[i][FORM1_DIST_REF_POINTS] = cpu_to_be32(i); } /* @@ -219,15 +228,15 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, max_nodes_with_gpus = nb_numa_nodes + NVGPU_MAX_NUM; for (i = nb_numa_nodes; i < max_nodes_with_gpus; i++) { - spapr->numa_assoc_array[i][0] = cpu_to_be32(FORM1_DIST_REF_POINTS); + spapr->FORM1_assoc_array[i][0] = cpu_to_be32(FORM1_DIST_REF_POINTS); for (j = 1; j < FORM1_DIST_REF_POINTS; j++) { uint32_t gpu_assoc = smc->pre_5_1_assoc_refpoints ? SPAPR_GPU_NUMA_ID : cpu_to_be32(i); - spapr->numa_assoc_array[i][j] = gpu_assoc; + spapr->FORM1_assoc_array[i][j] = gpu_assoc; } - spapr->numa_assoc_array[i][FORM1_DIST_REF_POINTS] = cpu_to_be32(i); + spapr->FORM1_assoc_array[i][FORM1_DIST_REF_POINTS] = cpu_to_be32(i); } /* @@ -259,14 +268,17 @@ void spapr_numa_associativity_init(SpaprMachineState *spapr, void spapr_numa_write_associativity_dt(SpaprMachineState *spapr, void *fdt, int offset, int nodeid) { + const uint32_t *associativity = get_associativity(spapr, nodeid); + _FDT((fdt_setprop(fdt, offset, "ibm,associativity", - spapr->numa_assoc_array[nodeid], + associativity, get_numa_assoc_size(spapr) * sizeof(uint32_t)))); } static uint32_t *spapr_numa_get_vcpu_assoc(SpaprMachineState *spapr, PowerPCCPU *cpu) { + const uint32_t *associativity = get_associativity(spapr, cpu->node_id); int max_distance_ref_points = get_max_dist_ref_points(spapr); int vcpu_assoc_size = get_vcpu_assoc_size(spapr); uint32_t *vcpu_assoc = g_new(uint32_t, vcpu_assoc_size); @@ -280,7 +292,7 @@ static uint32_t *spapr_numa_get_vcpu_assoc(SpaprMachineState *spapr, */ vcpu_assoc[0] = cpu_to_be32(max_distance_ref_points + 1); vcpu_assoc[vcpu_assoc_size - 1] = cpu_to_be32(index); - memcpy(vcpu_assoc + 1, spapr->numa_assoc_array[cpu->node_id] + 1, + memcpy(vcpu_assoc + 1, associativity + 1, (vcpu_assoc_size - 2) * sizeof(uint32_t)); return vcpu_assoc; @@ -319,10 +331,10 @@ int spapr_numa_write_assoc_lookup_arrays(SpaprMachineState *spapr, void *fdt, cur_index += 2; for (i = 0; i < nr_nodes; i++) { /* - * For the lookup-array we use the ibm,associativity array, - * from numa_assoc_array. without the first element (size). + * For the lookup-array we use the ibm,associativity array of the + * current NUMA affinity, without the first element (size). */ - uint32_t *associativity = spapr->numa_assoc_array[i]; + const uint32_t *associativity = get_associativity(spapr, i); memcpy(cur_index, ++associativity, sizeof(uint32_t) * max_distance_ref_points); cur_index += max_distance_ref_points; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 814e087e98..6b3dfc5dc2 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -249,7 +249,7 @@ struct SpaprMachineState { unsigned gpu_numa_id; SpaprTpmProxy *tpm_proxy; - uint32_t numa_assoc_array[NUMA_NODES_MAX_NUM][FORM1_NUMA_ASSOC_SIZE]; + uint32_t FORM1_assoc_array[NUMA_NODES_MAX_NUM][FORM1_NUMA_ASSOC_SIZE]; Error *fwnmi_migration_blocker; }; From 5dab5abe623d97929382158b43b3fd545e8edd62 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 20 Sep 2021 14:49:45 -0300 Subject: [PATCH 0068/1334] spapr: move FORM1 verifications to post CAS FORM2 NUMA affinity is prepared to deal with empty (memory/cpu less) NUMA nodes. This is used by the DAX KMEM driver to locate a PAPR SCM device that has a different latency than the original NUMA node from the regular memory. FORM2 is also able to deal with asymmetric NUMA distances gracefully, something that our FORM1 implementation doesn't do. Move these FORM1 verifications to a new function and wait until after CAS, when we're sure that we're sticking with FORM1, to enforce them. Reviewed-by: Greg Kurz Signed-off-by: Daniel Henrique Barboza Message-Id: <20210920174947.556324-6-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr.c | 33 ----------------------- hw/ppc/spapr_hcall.c | 6 +++++ hw/ppc/spapr_numa.c | 53 ++++++++++++++++++++++++++++++++----- include/hw/ppc/spapr_numa.h | 1 + 4 files changed, 54 insertions(+), 39 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 270106975b..524951def1 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2774,39 +2774,6 @@ static void spapr_machine_init(MachineState *machine) /* init CPUs */ spapr_init_cpus(spapr); - /* - * check we don't have a memory-less/cpu-less NUMA node - * Firmware relies on the existing memory/cpu topology to provide the - * NUMA topology to the kernel. - * And the linux kernel needs to know the NUMA topology at start - * to be able to hotplug CPUs later. - */ - if (machine->numa_state->num_nodes) { - for (i = 0; i < machine->numa_state->num_nodes; ++i) { - /* check for memory-less node */ - if (machine->numa_state->nodes[i].node_mem == 0) { - CPUState *cs; - int found = 0; - /* check for cpu-less node */ - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - if (cpu->node_id == i) { - found = 1; - break; - } - } - /* memory-less and cpu-less node */ - if (!found) { - error_report( - "Memory-less/cpu-less nodes are not supported (node %d)", - i); - exit(1); - } - } - } - - } - spapr->gpu_numa_id = spapr_numa_initial_nvgpu_numa_id(machine); /* Init numa_assoc_array */ diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 9056644890..222c1b6bbd 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1198,6 +1198,12 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, spapr->cas_pre_isa3_guest = !spapr_ovec_test(ov1_guest, OV1_PPC_3_00); spapr_ovec_cleanup(ov1_guest); + /* + * Check for NUMA affinity conditions now that we know which NUMA + * affinity the guest will use. + */ + spapr_numa_associativity_check(spapr); + /* * Ensure the guest asks for an interrupt mode we support; * otherwise terminate the boot. diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index dce9ce987a..6718c0fdd1 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -193,6 +193,48 @@ static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) } +static void spapr_numa_FORM1_affinity_check(MachineState *machine) +{ + int i; + + /* + * Check we don't have a memory-less/cpu-less NUMA node + * Firmware relies on the existing memory/cpu topology to provide the + * NUMA topology to the kernel. + * And the linux kernel needs to know the NUMA topology at start + * to be able to hotplug CPUs later. + */ + if (machine->numa_state->num_nodes) { + for (i = 0; i < machine->numa_state->num_nodes; ++i) { + /* check for memory-less node */ + if (machine->numa_state->nodes[i].node_mem == 0) { + CPUState *cs; + int found = 0; + /* check for cpu-less node */ + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + if (cpu->node_id == i) { + found = 1; + break; + } + } + /* memory-less and cpu-less node */ + if (!found) { + error_report( +"Memory-less/cpu-less nodes are not supported with FORM1 NUMA (node %d)", i); + exit(EXIT_FAILURE); + } + } + } + } + + if (!spapr_numa_is_symmetrical(machine)) { + error_report( +"Asymmetrical NUMA topologies aren't supported in the pSeries machine using FORM1 NUMA"); + exit(EXIT_FAILURE); + } +} + /* * Set NUMA machine state data based on FORM1 affinity semantics. */ @@ -250,12 +292,6 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, return; } - if (!spapr_numa_is_symmetrical(machine)) { - error_report("Asymmetrical NUMA topologies aren't supported " - "in the pSeries machine"); - exit(EXIT_FAILURE); - } - spapr_numa_define_FORM1_domains(spapr); } @@ -265,6 +301,11 @@ void spapr_numa_associativity_init(SpaprMachineState *spapr, spapr_numa_FORM1_affinity_init(spapr, machine); } +void spapr_numa_associativity_check(SpaprMachineState *spapr) +{ + spapr_numa_FORM1_affinity_check(MACHINE(spapr)); +} + void spapr_numa_write_associativity_dt(SpaprMachineState *spapr, void *fdt, int offset, int nodeid) { diff --git a/include/hw/ppc/spapr_numa.h b/include/hw/ppc/spapr_numa.h index 6f9f02d3de..7cb3367400 100644 --- a/include/hw/ppc/spapr_numa.h +++ b/include/hw/ppc/spapr_numa.h @@ -24,6 +24,7 @@ */ void spapr_numa_associativity_init(SpaprMachineState *spapr, MachineState *machine); +void spapr_numa_associativity_check(SpaprMachineState *spapr); void spapr_numa_write_rtas_dt(SpaprMachineState *spapr, void *fdt, int rtas); void spapr_numa_write_associativity_dt(SpaprMachineState *spapr, void *fdt, int offset, int nodeid); From e0eb84d4f51f26aff6210407ec658c4b9ecbc68f Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 20 Sep 2021 14:49:46 -0300 Subject: [PATCH 0069/1334] spapr_numa.c: FORM2 NUMA affinity support The main feature of FORM2 affinity support is the separation of NUMA distances from ibm,associativity information. This allows for a more flexible and straightforward NUMA distance assignment without relying on complex associations between several levels of NUMA via ibm,associativity matches. Another feature is its extensibility. This base support contains the facilities for NUMA distance assignment, but in the future more facilities will be added for latency, performance, bandwidth and so on. This patch implements the base FORM2 affinity support as follows: - the use of FORM2 associativity is indicated by using bit 2 of byte 5 of ibm,architecture-vec-5. A FORM2 aware guest can choose to use FORM1 or FORM2 affinity. Setting both forms will default to FORM2. We're not advertising FORM2 for pseries-6.1 and older machine versions to prevent guest visible changes in those; - ibm,associativity-reference-points has a new semantic. Instead of being used to calculate distances via NUMA levels, it's now used to indicate the primary domain index in the ibm,associativity domain of each resource. In our case it's set to {0x4}, matching the position where we already place logical_domain_id; - two new RTAS DT artifacts are introduced: ibm,numa-lookup-index-table and ibm,numa-distance-table. The index table is used to list all the NUMA logical domains of the platform, in ascending order, and allows for spartial NUMA configurations (although QEMU ATM doesn't support that). ibm,numa-distance-table is an array that contains all the distances from the first NUMA node to all other nodes, then the second NUMA node distances to all other nodes and so on; - get_max_dist_ref_points(), get_numa_assoc_size() and get_associativity() now checks for OV5_FORM2_AFFINITY and returns FORM2 values if the guest selected FORM2 affinity during CAS. Reviewed-by: Greg Kurz Signed-off-by: Daniel Henrique Barboza Message-Id: <20210920174947.556324-7-danielhb413@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr.c | 8 ++ hw/ppc/spapr_numa.c | 146 ++++++++++++++++++++++++++++++++++++ include/hw/ppc/spapr.h | 9 +++ include/hw/ppc/spapr_ovec.h | 1 + 4 files changed, 164 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 524951def1..b7bee5f4ff 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2753,6 +2753,11 @@ static void spapr_machine_init(MachineState *machine) spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); + /* Do not advertise FORM2 NUMA support for pseries-6.1 and older */ + if (!smc->pre_6_2_numa_affinity) { + spapr_ovec_set(spapr->ov5, OV5_FORM2_AFFINITY); + } + /* advertise support for dedicated HP event source to guests */ if (spapr->use_hotplug_event_source) { spapr_ovec_set(spapr->ov5, OV5_HP_EVT); @@ -4675,8 +4680,11 @@ DEFINE_SPAPR_MACHINE(6_2, "6.2", true); */ static void spapr_machine_6_1_class_options(MachineClass *mc) { + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + spapr_machine_6_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); + smc->pre_6_2_numa_affinity = true; } DEFINE_SPAPR_MACHINE(6_1, "6.1", false); diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index 6718c0fdd1..13db321997 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -24,6 +24,10 @@ */ static int get_max_dist_ref_points(SpaprMachineState *spapr) { + if (spapr_ovec_test(spapr->ov5_cas, OV5_FORM2_AFFINITY)) { + return FORM2_DIST_REF_POINTS; + } + return FORM1_DIST_REF_POINTS; } @@ -32,6 +36,10 @@ static int get_max_dist_ref_points(SpaprMachineState *spapr) */ static int get_numa_assoc_size(SpaprMachineState *spapr) { + if (spapr_ovec_test(spapr->ov5_cas, OV5_FORM2_AFFINITY)) { + return FORM2_NUMA_ASSOC_SIZE; + } + return FORM1_NUMA_ASSOC_SIZE; } @@ -52,6 +60,9 @@ static int get_vcpu_assoc_size(SpaprMachineState *spapr) */ static const uint32_t *get_associativity(SpaprMachineState *spapr, int node_id) { + if (spapr_ovec_test(spapr->ov5_cas, OV5_FORM2_AFFINITY)) { + return spapr->FORM2_assoc_array[node_id]; + } return spapr->FORM1_assoc_array[node_id]; } @@ -295,14 +306,50 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, spapr_numa_define_FORM1_domains(spapr); } +/* + * Init NUMA FORM2 machine state data + */ +static void spapr_numa_FORM2_affinity_init(SpaprMachineState *spapr) +{ + int i; + + /* + * For all resources but CPUs, FORM2 associativity arrays will + * be a size 2 array with the following format: + * + * ibm,associativity = {1, numa_id} + * + * CPUs will write an additional 'vcpu_id' on top of the arrays + * being initialized here. 'numa_id' is represented by the + * index 'i' of the loop. + * + * Given that this initialization is also valid for GPU associativity + * arrays, handle everything in one single step by populating the + * arrays up to NUMA_NODES_MAX_NUM. + */ + for (i = 0; i < NUMA_NODES_MAX_NUM; i++) { + spapr->FORM2_assoc_array[i][0] = cpu_to_be32(1); + spapr->FORM2_assoc_array[i][1] = cpu_to_be32(i); + } +} + void spapr_numa_associativity_init(SpaprMachineState *spapr, MachineState *machine) { spapr_numa_FORM1_affinity_init(spapr, machine); + spapr_numa_FORM2_affinity_init(spapr); } void spapr_numa_associativity_check(SpaprMachineState *spapr) { + /* + * FORM2 does not have any restrictions we need to handle + * at CAS time, for now. + */ + if (spapr_ovec_test(spapr->ov5_cas, OV5_FORM2_AFFINITY)) { + return; + } + spapr_numa_FORM1_affinity_check(MACHINE(spapr)); } @@ -447,6 +494,100 @@ static void spapr_numa_FORM1_write_rtas_dt(SpaprMachineState *spapr, maxdomains, sizeof(maxdomains))); } +static void spapr_numa_FORM2_write_rtas_tables(SpaprMachineState *spapr, + void *fdt, int rtas) +{ + MachineState *ms = MACHINE(spapr); + NodeInfo *numa_info = ms->numa_state->nodes; + int nb_numa_nodes = ms->numa_state->num_nodes; + int distance_table_entries = nb_numa_nodes * nb_numa_nodes; + g_autofree uint32_t *lookup_index_table = NULL; + g_autofree uint32_t *distance_table = NULL; + int src, dst, i, distance_table_size; + uint8_t *node_distances; + + /* + * ibm,numa-lookup-index-table: array with length and a + * list of NUMA ids present in the guest. + */ + lookup_index_table = g_new0(uint32_t, nb_numa_nodes + 1); + lookup_index_table[0] = cpu_to_be32(nb_numa_nodes); + + for (i = 0; i < nb_numa_nodes; i++) { + lookup_index_table[i + 1] = cpu_to_be32(i); + } + + _FDT(fdt_setprop(fdt, rtas, "ibm,numa-lookup-index-table", + lookup_index_table, + (nb_numa_nodes + 1) * sizeof(uint32_t))); + + /* + * ibm,numa-distance-table: contains all node distances. First + * element is the size of the table as uint32, followed up + * by all the uint8 distances from the first NUMA node, then all + * distances from the second NUMA node and so on. + * + * ibm,numa-lookup-index-table is used by guest to navigate this + * array because NUMA ids can be sparse (node 0 is the first, + * node 8 is the second ...). + */ + distance_table = g_new0(uint32_t, distance_table_entries + 1); + distance_table[0] = cpu_to_be32(distance_table_entries); + + node_distances = (uint8_t *)&distance_table[1]; + i = 0; + + for (src = 0; src < nb_numa_nodes; src++) { + for (dst = 0; dst < nb_numa_nodes; dst++) { + node_distances[i++] = numa_info[src].distance[dst]; + } + } + + distance_table_size = distance_table_entries * sizeof(uint8_t) + + sizeof(uint32_t); + _FDT(fdt_setprop(fdt, rtas, "ibm,numa-distance-table", + distance_table, distance_table_size)); +} + +/* + * This helper could be compressed in a single function with + * FORM1 logic since we're setting the same DT values, with the + * difference being a call to spapr_numa_FORM2_write_rtas_tables() + * in the end. The separation was made to avoid clogging FORM1 code + * which already has to deal with compat modes from previous + * QEMU machine types. + */ +static void spapr_numa_FORM2_write_rtas_dt(SpaprMachineState *spapr, + void *fdt, int rtas) +{ + MachineState *ms = MACHINE(spapr); + uint32_t number_nvgpus_nodes = spapr->gpu_numa_id - + spapr_numa_initial_nvgpu_numa_id(ms); + + /* + * In FORM2, ibm,associativity-reference-points will point to + * the element in the ibm,associativity array that contains the + * primary domain index (for FORM2, the first element). + * + * This value (in our case, the numa-id) is then used as an index + * to retrieve all other attributes of the node (distance, + * bandwidth, latency) via ibm,numa-lookup-index-table and other + * ibm,numa-*-table properties. + */ + uint32_t refpoints[] = { cpu_to_be32(1) }; + + uint32_t maxdomain = ms->numa_state->num_nodes + number_nvgpus_nodes; + uint32_t maxdomains[] = { cpu_to_be32(1), cpu_to_be32(maxdomain) }; + + _FDT(fdt_setprop(fdt, rtas, "ibm,associativity-reference-points", + refpoints, sizeof(refpoints))); + + _FDT(fdt_setprop(fdt, rtas, "ibm,max-associativity-domains", + maxdomains, sizeof(maxdomains))); + + spapr_numa_FORM2_write_rtas_tables(spapr, fdt, rtas); +} + /* * Helper that writes ibm,associativity-reference-points and * max-associativity-domains in the RTAS pointed by @rtas @@ -454,6 +595,11 @@ static void spapr_numa_FORM1_write_rtas_dt(SpaprMachineState *spapr, */ void spapr_numa_write_rtas_dt(SpaprMachineState *spapr, void *fdt, int rtas) { + if (spapr_ovec_test(spapr->ov5_cas, OV5_FORM2_AFFINITY)) { + spapr_numa_FORM2_write_rtas_dt(spapr, fdt, rtas); + return; + } + spapr_numa_FORM1_write_rtas_dt(spapr, fdt, rtas); } diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 6b3dfc5dc2..ee7504b976 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -118,6 +118,13 @@ typedef enum { #define FORM1_DIST_REF_POINTS 4 #define FORM1_NUMA_ASSOC_SIZE (FORM1_DIST_REF_POINTS + 1) +/* + * FORM2 NUMA affinity has a single associativity domain, giving + * us a assoc size of 2. + */ +#define FORM2_DIST_REF_POINTS 1 +#define FORM2_NUMA_ASSOC_SIZE (FORM2_DIST_REF_POINTS + 1) + typedef struct SpaprCapabilities SpaprCapabilities; struct SpaprCapabilities { uint8_t caps[SPAPR_CAP_NUM]; @@ -145,6 +152,7 @@ struct SpaprMachineClass { hwaddr rma_limit; /* clamp the RMA to this size */ bool pre_5_1_assoc_refpoints; bool pre_5_2_numa_associativity; + bool pre_6_2_numa_affinity; bool (*phb_placement)(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, @@ -250,6 +258,7 @@ struct SpaprMachineState { SpaprTpmProxy *tpm_proxy; uint32_t FORM1_assoc_array[NUMA_NODES_MAX_NUM][FORM1_NUMA_ASSOC_SIZE]; + uint32_t FORM2_assoc_array[NUMA_NODES_MAX_NUM][FORM2_NUMA_ASSOC_SIZE]; Error *fwnmi_migration_blocker; }; diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index 48b716a060..c3e8b98e7e 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -49,6 +49,7 @@ typedef struct SpaprOptionVector SpaprOptionVector; /* option vector 5 */ #define OV5_DRCONF_MEMORY OV_BIT(2, 2) #define OV5_FORM1_AFFINITY OV_BIT(5, 0) +#define OV5_FORM2_AFFINITY OV_BIT(5, 2) #define OV5_HP_EVT OV_BIT(6, 5) #define OV5_HPT_RESIZE OV_BIT(6, 7) #define OV5_DRMEM_V2 OV_BIT(22, 0) From 0d5ba48112daf820daddb5c952265fa23e092e69 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 20 Sep 2021 14:49:47 -0300 Subject: [PATCH 0070/1334] spapr_numa.c: handle auto NUMA node with no distance info numa_complete_configuration() in hw/core/numa.c always adds a NUMA node for the pSeries machine if none was specified, but without node distance information for the single node created. NUMA FORM1 affinity code didn't rely on numa_state information to do its job, but FORM2 does. As is now, this is the result of a pSeries guest with NUMA FORM2 affinity when no NUMA nodes is specified: $ numactl -H available: 1 nodes (0) node 0 cpus: 0 node 0 size: 16222 MB node 0 free: 15681 MB No distance information available. This can be amended in spapr_numa_FORM2_write_rtas_tables(). We're enforcing that the local distance (the distance to the node to itself) is always 10. This allows for the proper creation of the NUMA distance tables, fixing the output of 'numactl -H' in the guest: $ numactl -H available: 1 nodes (0) node 0 cpus: 0 node 0 size: 16222 MB node 0 free: 15685 MB node distances: node 0 0: 10 CC: Igor Mammedov Reviewed-by: Greg Kurz Signed-off-by: Daniel Henrique Barboza Message-Id: <20210920174947.556324-8-danielhb413@gmail.com> Acked-by: Igor Mammedov Signed-off-by: David Gibson --- hw/ppc/spapr_numa.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index 13db321997..58d5dc7084 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -539,6 +539,17 @@ static void spapr_numa_FORM2_write_rtas_tables(SpaprMachineState *spapr, for (src = 0; src < nb_numa_nodes; src++) { for (dst = 0; dst < nb_numa_nodes; dst++) { + /* + * We need to be explicit with the local distance + * value to cover the case where the user didn't added any + * NUMA nodes, but QEMU adds the default NUMA node without + * adding the numa_info to retrieve distance info from. + */ + if (src == dst) { + node_distances[i++] = 10; + continue; + } + node_distances[i++] = numa_info[src].distance[dst]; } } From af96d2e69227fc25f663d840527dbe2a0c592400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 20 Sep 2021 08:12:02 +0200 Subject: [PATCH 0071/1334] target/ppc: Convert debug to trace events (decrementer and IRQ) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Message-Id: <20210920061203.989563-4-clg@kaod.org> Signed-off-by: David Gibson --- hw/ppc/ppc.c | 169 ++++++++++++++++---------------------------- hw/ppc/trace-events | 20 ++++++ 2 files changed, 81 insertions(+), 108 deletions(-) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index a327206a0a..b813ef732e 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -37,22 +37,6 @@ #include "migration/vmstate.h" #include "trace.h" -//#define PPC_DEBUG_IRQ -//#define PPC_DEBUG_TB - -#ifdef PPC_DEBUG_IRQ -# define LOG_IRQ(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) -#else -# define LOG_IRQ(...) do { } while (0) -#endif - - -#ifdef PPC_DEBUG_TB -# define LOG_TB(...) qemu_log(__VA_ARGS__) -#else -# define LOG_TB(...) do { } while (0) -#endif - static void cpu_ppc_tb_stop (CPUPPCState *env); static void cpu_ppc_tb_start (CPUPPCState *env); @@ -86,9 +70,8 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level) } - LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32 - "req %08x\n", __func__, env, n_IRQ, level, - env->pending_interrupts, CPU(cpu)->interrupt_request); + trace_ppc_irq_set_exit(env, n_IRQ, level, env->pending_interrupts, + CPU(cpu)->interrupt_request); if (locked) { qemu_mutex_unlock_iothread(); @@ -102,8 +85,8 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) CPUPPCState *env = &cpu->env; int cur_level; - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); + trace_ppc_irq_set(env, pin, level); + cur_level = (env->irq_input_state >> pin) & 1; /* Don't generate spurious events */ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { @@ -112,8 +95,7 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) switch (pin) { case PPC6xx_INPUT_TBEN: /* Level sensitive - active high */ - LOG_IRQ("%s: %s the time base\n", - __func__, level ? "start" : "stop"); + trace_ppc_irq_set_state("time base", level); if (level) { cpu_ppc_tb_start(env); } else { @@ -122,14 +104,12 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) break; case PPC6xx_INPUT_INT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("external IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case PPC6xx_INPUT_SMI: /* Level sensitive - active high */ - LOG_IRQ("%s: set the SMI IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("SMI IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_SMI, level); break; case PPC6xx_INPUT_MCP: @@ -138,8 +118,7 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) * 603/604/740/750: check HID0[EMCP] */ if (cur_level == 1 && level == 0) { - LOG_IRQ("%s: raise machine check state\n", - __func__); + trace_ppc_irq_set_state("machine check", 1); ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); } break; @@ -148,20 +127,19 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) /* XXX: TODO: relay the signal to CKSTP_OUT pin */ /* XXX: Note that the only way to restart the CPU is to reset it */ if (level) { - LOG_IRQ("%s: stop the CPU\n", __func__); + trace_ppc_irq_cpu("stop"); cs->halted = 1; } break; case PPC6xx_INPUT_HRESET: /* Level sensitive - active low */ if (level) { - LOG_IRQ("%s: reset the CPU\n", __func__); + trace_ppc_irq_reset("CPU"); cpu_interrupt(cs, CPU_INTERRUPT_RESET); } break; case PPC6xx_INPUT_SRESET: - LOG_IRQ("%s: set the RESET IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("RESET IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); break; default: @@ -190,8 +168,8 @@ static void ppc970_set_irq(void *opaque, int pin, int level) CPUPPCState *env = &cpu->env; int cur_level; - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); + trace_ppc_irq_set(env, pin, level); + cur_level = (env->irq_input_state >> pin) & 1; /* Don't generate spurious events */ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { @@ -200,14 +178,12 @@ static void ppc970_set_irq(void *opaque, int pin, int level) switch (pin) { case PPC970_INPUT_INT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("external IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case PPC970_INPUT_THINT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__, - level); + trace_ppc_irq_set_state("SMI IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_THERM, level); break; case PPC970_INPUT_MCP: @@ -216,8 +192,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level) * 603/604/740/750: check HID0[EMCP] */ if (cur_level == 1 && level == 0) { - LOG_IRQ("%s: raise machine check state\n", - __func__); + trace_ppc_irq_set_state("machine check", 1); ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); } break; @@ -225,10 +200,10 @@ static void ppc970_set_irq(void *opaque, int pin, int level) /* Level sensitive - active low */ /* XXX: TODO: relay the signal to CKSTP_OUT pin */ if (level) { - LOG_IRQ("%s: stop the CPU\n", __func__); + trace_ppc_irq_cpu("stop"); cs->halted = 1; } else { - LOG_IRQ("%s: restart the CPU\n", __func__); + trace_ppc_irq_cpu("restart"); cs->halted = 0; qemu_cpu_kick(cs); } @@ -240,13 +215,11 @@ static void ppc970_set_irq(void *opaque, int pin, int level) } break; case PPC970_INPUT_SRESET: - LOG_IRQ("%s: set the RESET IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("RESET IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); break; case PPC970_INPUT_TBEN: - LOG_IRQ("%s: set the TBEN state to %d\n", __func__, - level); + trace_ppc_irq_set_state("TBEN IRQ", level); /* XXX: TODO */ break; default: @@ -272,14 +245,12 @@ static void power7_set_irq(void *opaque, int pin, int level) { PowerPCCPU *cpu = opaque; - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - &cpu->env, pin, level); + trace_ppc_irq_set(&cpu->env, pin, level); switch (pin) { case POWER7_INPUT_INT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("external IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; default: @@ -300,24 +271,22 @@ static void power9_set_irq(void *opaque, int pin, int level) { PowerPCCPU *cpu = opaque; - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - &cpu->env, pin, level); + trace_ppc_irq_set(&cpu->env, pin, level); switch (pin) { case POWER9_INPUT_INT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("external IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case POWER9_INPUT_HINT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("HV external IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_HVIRT, level); break; default: g_assert_not_reached(); + return; } } @@ -393,8 +362,8 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) CPUPPCState *env = &cpu->env; int cur_level; - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); + trace_ppc_irq_set(env, pin, level); + cur_level = (env->irq_input_state >> pin) & 1; /* Don't generate spurious events */ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { @@ -403,51 +372,47 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) switch (pin) { case PPC40x_INPUT_RESET_SYS: if (level) { - LOG_IRQ("%s: reset the PowerPC system\n", - __func__); + trace_ppc_irq_reset("system"); ppc40x_system_reset(cpu); } break; case PPC40x_INPUT_RESET_CHIP: if (level) { - LOG_IRQ("%s: reset the PowerPC chip\n", __func__); + trace_ppc_irq_reset("chip"); ppc40x_chip_reset(cpu); } break; case PPC40x_INPUT_RESET_CORE: /* XXX: TODO: update DBSR[MRR] */ if (level) { - LOG_IRQ("%s: reset the PowerPC core\n", __func__); + trace_ppc_irq_reset("core"); ppc40x_core_reset(cpu); } break; case PPC40x_INPUT_CINT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the critical IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("critical IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); break; case PPC40x_INPUT_INT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("external IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case PPC40x_INPUT_HALT: /* Level sensitive - active low */ if (level) { - LOG_IRQ("%s: stop the CPU\n", __func__); + trace_ppc_irq_cpu("stop"); cs->halted = 1; } else { - LOG_IRQ("%s: restart the CPU\n", __func__); + trace_ppc_irq_cpu("restart"); cs->halted = 0; qemu_cpu_kick(cs); } break; case PPC40x_INPUT_DEBUG: /* Level sensitive - active high */ - LOG_IRQ("%s: set the debug pin state to %d\n", - __func__, level); + trace_ppc_irq_set_state("debug pin", level); ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); break; default: @@ -475,41 +440,37 @@ static void ppce500_set_irq(void *opaque, int pin, int level) CPUPPCState *env = &cpu->env; int cur_level; - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); + trace_ppc_irq_set(env, pin, level); + cur_level = (env->irq_input_state >> pin) & 1; /* Don't generate spurious events */ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { switch (pin) { case PPCE500_INPUT_MCK: if (level) { - LOG_IRQ("%s: reset the PowerPC system\n", - __func__); + trace_ppc_irq_reset("system"); qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; case PPCE500_INPUT_RESET_CORE: if (level) { - LOG_IRQ("%s: reset the PowerPC core\n", __func__); + trace_ppc_irq_reset("core"); ppc_set_irq(cpu, PPC_INTERRUPT_MCK, level); } break; case PPCE500_INPUT_CINT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the critical IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("critical IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); break; case PPCE500_INPUT_INT: /* Level sensitive - active high */ - LOG_IRQ("%s: set the core IRQ state to %d\n", - __func__, level); + trace_ppc_irq_set_state("core IRQ", level); ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case PPCE500_INPUT_DEBUG: /* Level sensitive - active high */ - LOG_IRQ("%s: set the debug pin state to %d\n", - __func__, level); + trace_ppc_irq_set_state("debug pin", level); ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); break; default: @@ -564,7 +525,7 @@ uint64_t cpu_ppc_load_tbl (CPUPPCState *env) } tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); - LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); + trace_ppc_tb_load(tb); return tb; } @@ -575,7 +536,7 @@ static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env) uint64_t tb; tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); - LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); + trace_ppc_tb_load(tb); return tb >> 32; } @@ -595,8 +556,7 @@ static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk, *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND); - LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n", - __func__, value, *tb_offsetp); + trace_ppc_tb_store(value, *tb_offsetp); } void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value) @@ -632,7 +592,7 @@ uint64_t cpu_ppc_load_atbl (CPUPPCState *env) uint64_t tb; tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); - LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); + trace_ppc_tb_load(tb); return tb; } @@ -643,7 +603,7 @@ uint32_t cpu_ppc_load_atbu (CPUPPCState *env) uint64_t tb; tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); - LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); + trace_ppc_tb_load(tb); return tb >> 32; } @@ -762,7 +722,7 @@ static inline int64_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) } else { decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); } - LOG_TB("%s: %016" PRIx64 "\n", __func__, decr); + trace_ppc_decr_load(decr); return decr; } @@ -821,7 +781,7 @@ uint64_t cpu_ppc_load_purr (CPUPPCState *env) static inline void cpu_ppc_decr_excp(PowerPCCPU *cpu) { /* Raise it */ - LOG_TB("raise decrementer exception\n"); + trace_ppc_decr_excp("raise"); ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 1); } @@ -835,7 +795,7 @@ static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) CPUPPCState *env = &cpu->env; /* Raise it */ - LOG_TB("raise hv decrementer exception\n"); + trace_ppc_decr_excp("raise HV"); /* The architecture specifies that we don't deliver HDEC * interrupts in a PM state. Not only they don't cause a @@ -870,8 +830,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, value |= (0xFFFFFFFFULL << nr_bits); } - LOG_TB("%s: " TARGET_FMT_lx " => " TARGET_FMT_lx "\n", __func__, - decr, value); + trace_ppc_decr_store(nr_bits, decr, value); if (kvm_enabled()) { /* KVM handles decrementer exceptions, we don't need our own timer */ @@ -1199,9 +1158,8 @@ static void cpu_4xx_fit_cb (void *opaque) if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) { ppc_set_irq(cpu, PPC_INTERRUPT_FIT, 1); } - LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, - (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1), - env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); + trace_ppc4xx_fit((int)((env->spr[SPR_40x_TCR] >> 23) & 0x1), + env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); } /* Programmable interval timer */ @@ -1215,11 +1173,10 @@ static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) !((env->spr[SPR_40x_TCR] >> 26) & 0x1) || (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { /* Stop PIT */ - LOG_TB("%s: stop PIT\n", __func__); + trace_ppc4xx_pit_stop(); timer_del(tb_env->decr_timer); } else { - LOG_TB("%s: start PIT %016" PRIx64 "\n", - __func__, ppc40x_timer->pit_reload); + trace_ppc4xx_pit_start(ppc40x_timer->pit_reload); now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); next = now + muldiv64(ppc40x_timer->pit_reload, NANOSECONDS_PER_SECOND, tb_env->decr_freq); @@ -1248,9 +1205,7 @@ static void cpu_4xx_pit_cb (void *opaque) ppc_set_irq(cpu, ppc40x_timer->decr_excp, 1); } start_stop_pit(env, tb_env, 1); - LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " - "%016" PRIx64 "\n", __func__, - (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), + trace_ppc4xx_pit((int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1), env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], ppc40x_timer->pit_reload); @@ -1290,8 +1245,7 @@ static void cpu_4xx_wdt_cb (void *opaque) next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->decr_freq); if (next == now) next++; - LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, - env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); + trace_ppc4xx_wdt(env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { case 0x0: case 0x1: @@ -1334,7 +1288,7 @@ void store_40x_pit (CPUPPCState *env, target_ulong val) tb_env = env->tb_env; ppc40x_timer = tb_env->opaque; - LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val); + trace_ppc40x_store_pit(val); ppc40x_timer->pit_reload = val; start_stop_pit(env, tb_env, 0); } @@ -1349,8 +1303,7 @@ static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) CPUPPCState *env = opaque; ppc_tb_t *tb_env = env->tb_env; - LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__, - freq); + trace_ppc40x_set_tb_clk(freq); tb_env->tb_freq = freq; tb_env->decr_freq = freq; /* XXX: we should also update all timers */ @@ -1369,7 +1322,7 @@ clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, tb_env->tb_freq = freq; tb_env->decr_freq = freq; tb_env->opaque = ppc40x_timer; - LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); + trace_ppc40x_timers_init(freq); if (ppc40x_timer != NULL) { /* We use decr timer for PIT */ tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_pit_cb, env); diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index da6e74b80d..3bf43fa340 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -97,7 +97,27 @@ vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx6 # ppc.c ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "adjusted from 0x%"PRIx64" to 0x%"PRIx64", diff %"PRId64" (%"PRId64"s)" +ppc_tb_load(uint64_t tb) "tb 0x%016" PRIx64 +ppc_tb_store(uint64_t tb, uint64_t offset) "tb 0x%016" PRIx64 " offset 0x%08" PRIx64 +ppc_decr_load(uint64_t tb) "decr 0x%016" PRIx64 +ppc_decr_excp(const char *action) "%s decrementer" +ppc_decr_store(uint32_t nr_bits, uint64_t decr, uint64_t value) "%d-bit 0x%016" PRIx64 " => 0x%016" PRIx64 + +ppc4xx_fit(uint32_t ir, uint64_t tcr, uint64_t tsr) "ir %d TCR 0x%" PRIx64 " TSR 0x%" PRIx64 +ppc4xx_pit_stop(void) "" +ppc4xx_pit_start(uint64_t reload) "PIT 0x%016" PRIx64 +ppc4xx_pit(uint32_t ar, uint32_t ir, uint64_t tcr, uint64_t tsr, uint64_t reload) "ar %d ir %d TCR 0x%" PRIx64 " TSR 0x%" PRIx64 " PIT 0x%016" PRIx64 +ppc4xx_wdt(uint64_t tcr, uint64_t tsr) "TCR 0x%" PRIx64 " TSR 0x%" PRIx64 +ppc40x_store_pit(uint64_t value) "val 0x%" PRIx64 +ppc40x_set_tb_clk(uint32_t value) "new frequency %" PRIu32 +ppc40x_timers_init(uint32_t value) "frequency %" PRIu32 + +ppc_irq_set(void *env, uint32_t pin, uint32_t level) "env [%p] pin %d level %d" +ppc_irq_set_exit(void *env, uint32_t n_IRQ, uint32_t level, uint32_t pending, uint32_t request) "env [%p] n_IRQ %d level %d => pending 0x%08" PRIx32 " req 0x%08" PRIx32 +ppc_irq_set_state(const char *name, uint32_t level) "\"%s\" level %d" +ppc_irq_reset(const char *name) "%s" +ppc_irq_cpu(const char *action) "%s" # prep_systemio.c prep_systemio_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" From 4d9b8ef9b5ab880d491b0c41d222b42ed83bdbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 20 Sep 2021 08:12:03 +0200 Subject: [PATCH 0072/1334] target/ppc: Fix 64-bit decrementer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current way the mask is built can overflow with a 64-bit decrementer. Use sextract64() to extract the signed values and remove the logic to handle negative values which has become useless. Cc: Luis Fernando Fujita Pires Fixes: a8dafa525181 ("target/ppc: Implement large decrementer support for TCG") Signed-off-by: Cédric Le Goater Message-Id: <20210920061203.989563-5-clg@kaod.org> Reviewed-by: Luis Pires Signed-off-by: David Gibson --- hw/ppc/ppc.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index b813ef732e..f5d012f860 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -821,14 +821,12 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, CPUPPCState *env = &cpu->env; ppc_tb_t *tb_env = env->tb_env; uint64_t now, next; - bool negative; + int64_t signed_value; + int64_t signed_decr; /* Truncate value to decr_width and sign extend for simplicity */ - value &= ((1ULL << nr_bits) - 1); - negative = !!(value & (1ULL << (nr_bits - 1))); - if (negative) { - value |= (0xFFFFFFFFULL << nr_bits); - } + signed_value = sextract64(value, 0, nr_bits); + signed_decr = sextract64(decr, 0, nr_bits); trace_ppc_decr_store(nr_bits, decr, value); @@ -850,16 +848,16 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers * an edge interrupt, so raise it here too. */ - if ((value < 3) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && negative) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && negative - && !(decr & (1ULL << (nr_bits - 1))))) { + if ((signed_value < 3) || + ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) || + ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0 + && signed_decr >= 0)) { (*raise_excp)(cpu); return; } /* On MSB level based systems a 0 for the MSB stops interrupt delivery */ - if (!negative && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { + if (signed_value >= 0 && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { (*lower_excp)(cpu); } From 457279cb49d343b2c39a9efd2b28fe0fbde71f78 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sat, 18 Sep 2021 11:26:51 +0800 Subject: [PATCH 0073/1334] hw/intc: openpic: Correct the reset value of IPIDR for FSL chipset The reset value of IPIDR should be zero for Freescale chipset, per the following 2 manuals I checked: - P2020RM (https://www.nxp.com/webapp/Download?colCode=P2020RM) - P4080RM (https://www.nxp.com/webapp/Download?colCode=P4080RM) Currently it is set to 1, which leaves the IPI enabled on core 0 after power-on reset. Such may cause unexpected interrupt to be delivered to core 0 if the IPI is triggered from core 0 to other cores later. Fixes: ffd5e9fe0276 ("openpic: Reset IRQ source private members") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/584 Signed-off-by: Bin Meng Message-Id: <20210918032653.646370-1-bin.meng@windriver.com> Signed-off-by: David Gibson --- hw/intc/openpic.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 9b4c17854d..2790c6710a 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -1276,6 +1276,15 @@ static void openpic_reset(DeviceState *d) break; } + /* Mask all IPI interrupts for Freescale OpenPIC */ + if ((opp->model == OPENPIC_MODEL_FSL_MPIC_20) || + (opp->model == OPENPIC_MODEL_FSL_MPIC_42)) { + if (i >= opp->irq_ipi0 && i < opp->irq_tim0) { + write_IRQreg_idr(opp, i, 0); + continue; + } + } + write_IRQreg_idr(opp, i, opp->idr_reset); } /* Initialise IRQ destinations */ From 86229b68a28a8414797dd5548f3c5284f009fb53 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sat, 18 Sep 2021 11:26:52 +0800 Subject: [PATCH 0074/1334] hw/intc: openpic: Drop Raven related codes There is no machine that uses Motorola MCP750 (aka Raven) model. Drop the related codes. While we are here, drop the mentioning of Intel GW80314 I/O companion chip in the comments as it has been obsolete for years, and correct a typo too. Signed-off-by: Bin Meng Message-Id: <20210918032653.646370-2-bin.meng@windriver.com> Signed-off-by: David Gibson --- hw/intc/openpic.c | 28 +--------------------------- include/hw/ppc/openpic.h | 16 ---------------- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 2790c6710a..23eafb32bd 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -25,12 +25,8 @@ /* * * Based on OpenPic implementations: - * - Intel GW80314 I/O companion chip developer's manual * - Motorola MPC8245 & MPC8540 user manuals. - * - Motorola MCP750 (aka Raven) programmer manual. - * - Motorola Harrier programmer manuel - * - * Serial interrupts, as implemented in Raven chipset are not supported yet. + * - Motorola Harrier programmer manual * */ @@ -1564,28 +1560,6 @@ static void openpic_realize(DeviceState *dev, Error **errp) break; - case OPENPIC_MODEL_RAVEN: - opp->nb_irqs = RAVEN_MAX_EXT; - opp->vid = VID_REVISION_1_3; - opp->vir = VIR_GENERIC; - opp->vector_mask = 0xFF; - opp->tfrr_reset = 4160000; - opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK; - opp->idr_reset = 0; - opp->max_irq = RAVEN_MAX_IRQ; - opp->irq_ipi0 = RAVEN_IPI_IRQ; - opp->irq_tim0 = RAVEN_TMR_IRQ; - opp->brr1 = -1; - opp->mpic_mode_mask = GCR_MODE_MIXED; - - if (opp->nb_cpus != 1) { - error_setg(errp, "Only UP supported today"); - return; - } - - map_list(opp, list_le, &list_count); - break; - case OPENPIC_MODEL_KEYLARGO: opp->nb_irqs = KEYLARGO_MAX_EXT; opp->vid = VID_REVISION_1_2; diff --git a/include/hw/ppc/openpic.h b/include/hw/ppc/openpic.h index 74ff44bff0..f89802a15c 100644 --- a/include/hw/ppc/openpic.h +++ b/include/hw/ppc/openpic.h @@ -21,7 +21,6 @@ enum { typedef struct IrqLines { qemu_irq irq[OPENPIC_OUTPUT_NB]; } IrqLines; -#define OPENPIC_MODEL_RAVEN 0 #define OPENPIC_MODEL_FSL_MPIC_20 1 #define OPENPIC_MODEL_FSL_MPIC_42 2 #define OPENPIC_MODEL_KEYLARGO 3 @@ -32,13 +31,6 @@ typedef struct IrqLines { qemu_irq irq[OPENPIC_OUTPUT_NB]; } IrqLines; #define OPENPIC_MAX_IRQ (OPENPIC_MAX_SRC + OPENPIC_MAX_IPI + \ OPENPIC_MAX_TMR) -/* Raven */ -#define RAVEN_MAX_CPU 2 -#define RAVEN_MAX_EXT 48 -#define RAVEN_MAX_IRQ 64 -#define RAVEN_MAX_TMR OPENPIC_MAX_TMR -#define RAVEN_MAX_IPI OPENPIC_MAX_IPI - /* KeyLargo */ #define KEYLARGO_MAX_CPU 4 #define KEYLARGO_MAX_EXT 64 @@ -49,14 +41,6 @@ typedef struct IrqLines { qemu_irq irq[OPENPIC_OUTPUT_NB]; } IrqLines; /* Timers don't exist but this makes the code happy... */ #define KEYLARGO_TMR_IRQ (KEYLARGO_IPI_IRQ + KEYLARGO_MAX_IPI) -/* Interrupt definitions */ -#define RAVEN_FE_IRQ (RAVEN_MAX_EXT) /* Internal functional IRQ */ -#define RAVEN_ERR_IRQ (RAVEN_MAX_EXT + 1) /* Error IRQ */ -#define RAVEN_TMR_IRQ (RAVEN_MAX_EXT + 2) /* First timer IRQ */ -#define RAVEN_IPI_IRQ (RAVEN_TMR_IRQ + RAVEN_MAX_TMR) /* First IPI IRQ */ -/* First doorbell IRQ */ -#define RAVEN_DBL_IRQ (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI)) - typedef struct FslMpicInfo { int max_ext; } FslMpicInfo; From 06caae8af0926077338e9133dd95913aead28745 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sat, 18 Sep 2021 11:26:53 +0800 Subject: [PATCH 0075/1334] hw/intc: openpic: Clean up the styles Correct the multi-line comment format. No functional changes. Signed-off-by: Bin Meng Message-Id: <20210918032653.646370-3-bin.meng@windriver.com> Signed-off-by: David Gibson --- hw/intc/openpic.c | 55 +++++++++++++++++++++++++--------------- include/hw/ppc/openpic.h | 9 ++++--- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 23eafb32bd..49504e740f 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -47,7 +47,7 @@ #include "qemu/timer.h" #include "qemu/error-report.h" -//#define DEBUG_OPENPIC +/* #define DEBUG_OPENPIC */ #ifdef DEBUG_OPENPIC static const int debug_openpic = 1; @@ -118,7 +118,8 @@ static FslMpicInfo fsl_mpic_42 = { #define ILR_INTTGT_CINT 0x01 /* critical */ #define ILR_INTTGT_MCP 0x02 /* machine check */ -/* The currently supported INTTGT values happen to be the same as QEMU's +/* + * The currently supported INTTGT values happen to be the same as QEMU's * openpic output codes, but don't depend on this. The output codes * could change (unlikely, but...) or support could be added for * more INTTGT values. @@ -177,10 +178,11 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, uint32_t val, int idx); static void openpic_reset(DeviceState *d); -/* Convert between openpic clock ticks and nanosecs. In the hardware the clock - frequency is driven by board inputs to the PIC which the PIC would then - divide by 4 or 8. For now hard code to 25MZ. -*/ +/* + * Convert between openpic clock ticks and nanosecs. In the hardware the clock + * frequency is driven by board inputs to the PIC which the PIC would then + * divide by 4 or 8. For now hard code to 25MZ. + */ #define OPENPIC_TIMER_FREQ_MHZ 25 #define OPENPIC_TIMER_NS_PER_TICK (1000 / OPENPIC_TIMER_FREQ_MHZ) static inline uint64_t ns_to_ticks(uint64_t ns) @@ -253,7 +255,8 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, __func__, src->output, n_IRQ, active, was_active, dst->outputs_active[src->output]); - /* On Freescale MPIC, critical interrupts ignore priority, + /* + * On Freescale MPIC, critical interrupts ignore priority, * IACK, EOI, etc. Before MPIC v4.1 they also ignore * masking. */ @@ -276,7 +279,8 @@ static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, priority = IVPR_PRIORITY(src->ivpr); - /* Even if the interrupt doesn't have enough priority, + /* + * Even if the interrupt doesn't have enough priority, * it is still raised, in case ctpr is lowered later. */ if (active) { @@ -408,7 +412,8 @@ static void openpic_set_irq(void *opaque, int n_IRQ, int level) } if (src->output != OPENPIC_OUTPUT_INT) { - /* Edge-triggered interrupts shouldn't be used + /* + * Edge-triggered interrupts shouldn't be used * with non-INT delivery, but just in case, * try to make it do something sane rather than * cause an interrupt storm. This is close to @@ -501,7 +506,8 @@ static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val) { uint32_t mask; - /* NOTE when implementing newer FSL MPIC models: starting with v4.0, + /* + * NOTE when implementing newer FSL MPIC models: starting with v4.0, * the polarity bit is read-only on internal interrupts. */ mask = IVPR_MASK_MASK | IVPR_PRIORITY_MASK | IVPR_SENSE_MASK | @@ -511,7 +517,8 @@ static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val) opp->src[n_IRQ].ivpr = (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) | (val & mask); - /* For FSL internal interrupts, The sense bit is reserved and zero, + /* + * For FSL internal interrupts, The sense bit is reserved and zero, * and the interrupt is always level-triggered. Timers and IPIs * have no sense or polarity bits, and are edge-triggered. */ @@ -695,16 +702,20 @@ static void qemu_timer_cb(void *opaque) openpic_set_irq(opp, n_IRQ, 0); } -/* If enabled is true, arranges for an interrupt to be raised val clocks into - the future, if enabled is false cancels the timer. */ +/* + * If enabled is true, arranges for an interrupt to be raised val clocks into + * the future, if enabled is false cancels the timer. + */ static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled) { uint64_t ns = ticks_to_ns(val & ~TCCR_TOG); - /* A count of zero causes a timer to be set to expire immediately. This - effectively stops the simulation since the timer is constantly expiring - which prevents guest code execution, so we don't honor that - configuration. On real hardware, this situation would generate an - interrupt on every clock cycle if the interrupt was unmasked. */ + /* + * A count of zero causes a timer to be set to expire immediately. This + * effectively stops the simulation since the timer is constantly expiring + * which prevents guest code execution, so we don't honor that + * configuration. On real hardware, this situation would generate an + * interrupt on every clock cycle if the interrupt was unmasked. + */ if ((ns == 0) || !enabled) { tmr->qemu_timer_active = false; tmr->tccr = tmr->tccr & TCCR_TOG; @@ -717,8 +728,10 @@ static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled) } } -/* Returns the currrent tccr value, i.e., timer value (in clocks) with - appropriate TOG. */ +/* + * Returns the currrent tccr value, i.e., timer value (in clocks) with + * appropriate TOG. + */ static uint64_t openpic_tmr_get_timer(OpenPICTimer *tmr) { uint64_t retval; @@ -1309,7 +1322,7 @@ static void openpic_reset(DeviceState *d) typedef struct MemReg { const char *name; MemoryRegionOps const *ops; - hwaddr start_addr; + hwaddr start_addr; ram_addr_t size; } MemReg; diff --git a/include/hw/ppc/openpic.h b/include/hw/ppc/openpic.h index f89802a15c..ebdaf8a493 100644 --- a/include/hw/ppc/openpic.h +++ b/include/hw/ppc/openpic.h @@ -51,7 +51,8 @@ typedef enum IRQType { IRQ_TYPE_FSLSPECIAL, /* FSL timer/IPI interrupt, edge, no polarity */ } IRQType; -/* Round up to the nearest 64 IRQs so that the queue length +/* + * Round up to the nearest 64 IRQs so that the queue length * won't change when moving between 32 and 64 bit hosts. */ #define IRQQUEUE_SIZE_BITS ((OPENPIC_MAX_IRQ + 63) & ~63) @@ -101,8 +102,10 @@ typedef struct OpenPICTimer { bool qemu_timer_active; /* Is the qemu_timer is running? */ struct QEMUTimer *qemu_timer; struct OpenPICState *opp; /* Device timer is part of. */ - /* The QEMU_CLOCK_VIRTUAL time (in ns) corresponding to the last - current_count written or read, only defined if qemu_timer_active. */ + /* + * The QEMU_CLOCK_VIRTUAL time (in ns) corresponding to the last + * current_count written or read, only defined if qemu_timer_active. + */ uint64_t origin_time; } OpenPICTimer; From 28d86252fc6d7ff0b48a0298014e4761d8580c70 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 22 Sep 2021 09:28:52 -0300 Subject: [PATCH 0076/1334] spapr_numa.c: fixes in spapr_numa_FORM2_write_rtas_tables() This patch has a handful of modifications for the recent added FORM2 support: - to not allocate more than the necessary size in 'distance_table'. At this moment the array is oversized due to allocating uint32_t for all elements, when most of them fits in an uint8_t. Fix it by changing the array to uint8_t and allocating the exact size; - use stl_be_p() to store the uint32_t at the start of 'distance_table'; - use sizeof(uint32_t) to skip the uint32_t length when populating the distances; - use the NUMA_DISTANCE_MIN macro from sysemu/numa.h to avoid hardcoding the local distance value. Signed-off-by: Daniel Henrique Barboza Message-Id: <20210922122852.130054-2-danielhb413@gmail.com> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_numa.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index 58d5dc7084..5822938448 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -502,9 +502,8 @@ static void spapr_numa_FORM2_write_rtas_tables(SpaprMachineState *spapr, int nb_numa_nodes = ms->numa_state->num_nodes; int distance_table_entries = nb_numa_nodes * nb_numa_nodes; g_autofree uint32_t *lookup_index_table = NULL; - g_autofree uint32_t *distance_table = NULL; + g_autofree uint8_t *distance_table = NULL; int src, dst, i, distance_table_size; - uint8_t *node_distances; /* * ibm,numa-lookup-index-table: array with length and a @@ -531,11 +530,13 @@ static void spapr_numa_FORM2_write_rtas_tables(SpaprMachineState *spapr, * array because NUMA ids can be sparse (node 0 is the first, * node 8 is the second ...). */ - distance_table = g_new0(uint32_t, distance_table_entries + 1); - distance_table[0] = cpu_to_be32(distance_table_entries); + distance_table_size = distance_table_entries * sizeof(uint8_t) + + sizeof(uint32_t); + distance_table = g_new0(uint8_t, distance_table_size); + stl_be_p(distance_table, distance_table_entries); - node_distances = (uint8_t *)&distance_table[1]; - i = 0; + /* Skip the uint32_t array length at the start */ + i = sizeof(uint32_t); for (src = 0; src < nb_numa_nodes; src++) { for (dst = 0; dst < nb_numa_nodes; dst++) { @@ -546,16 +547,14 @@ static void spapr_numa_FORM2_write_rtas_tables(SpaprMachineState *spapr, * adding the numa_info to retrieve distance info from. */ if (src == dst) { - node_distances[i++] = 10; + distance_table[i++] = NUMA_DISTANCE_MIN; continue; } - node_distances[i++] = numa_info[src].distance[dst]; + distance_table[i++] = numa_info[src].distance[dst]; } } - distance_table_size = distance_table_entries * sizeof(uint8_t) + - sizeof(uint32_t); _FDT(fdt_setprop(fdt, rtas, "ibm,numa-distance-table", distance_table, distance_table_size)); } From 179abc1fcf6f74e21be02c1e4ec54776354c7136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 Sep 2021 09:02:05 +0200 Subject: [PATCH 0077/1334] spapr/xive: Fix kvm_xive_source_reset trace event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The trace event was placed in the wrong routine. Move it under kvmppc_xive_source_reset_one(). Fixes: 4e960974d4ee ("xive: Add trace events") Signed-off-by: Cédric Le Goater Message-Id: <20210922070205.1235943-1-clg@kaod.org> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- hw/intc/spapr_xive_kvm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index 3e534b9685..6d4909d0a8 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -236,6 +236,8 @@ int kvmppc_xive_source_reset_one(XiveSource *xsrc, int srcno, Error **errp) SpaprXive *xive = SPAPR_XIVE(xsrc->xive); uint64_t state = 0; + trace_kvm_xive_source_reset(srcno); + assert(xive->fd != -1); if (xive_source_irq_is_lsi(xsrc, srcno)) { @@ -311,8 +313,6 @@ uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset, return xive_esb_rw(xsrc, srcno, offset, data, 1); } - trace_kvm_xive_source_reset(srcno); - /* * Special Load EOI handling for LSI sources. Q bit is never set * and the interrupt should be re-triggered if the level is still From 7ddb120dbc1d03b7ad2e22449ba52b83b037dbd0 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 27 Sep 2021 14:17:45 +1000 Subject: [PATCH 0078/1334] MAINTAINERS: Remove machine specific files from ppc TCG CPUs entry Currently the PowerPC TCG CPUs entry in MAINTAINERS lists all of hw/ppc/ and include/hw/ppc. Nearly all the files in those places are related to specific ppc machine types, rather than to the actual CPUs however. Those machine types list their own files separately, often overlapping with this. For greater clarity, remove these misleading entries from the TCG CPUs stanza, leaving just hw/ppc/ppc.c and hw/ppc/ppc_booke.c which are the only ones common to a wide range of PPC TCG cpus each. Signed-off-by: David Gibson --- MAINTAINERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index bf1fc5b21e..b57a4c733b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,8 +271,9 @@ M: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: target/ppc/ -F: hw/ppc/ -F: include/hw/ppc/ +F: hw/ppc/ppc.c +F: hw/ppc/ppc_booke.c +F: include/hw/ppc/ppc.h F: disas/ppc.c RISC-V TCG CPUs From 225060a4883f3da482905d2cb94874dafacae613 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 21 Sep 2021 13:13:45 +1000 Subject: [PATCH 0079/1334] MAINTAINERS: Remove David & Greg as reviewers for a number of boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Greg and I are moving towards other areas and no longer have capacity to act as regular reviewers for several of the secondary ppc machine types. So, remove ourselves as reviewers for Macintosh, PReP, sam460ex and pegasos2 in MAINTAINERS. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Acked-by: BALATON Zoltan Reviewed-by: Cédric Le Goater --- MAINTAINERS | 9 --------- 1 file changed, 9 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index b57a4c733b..4d547e5604 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1274,8 +1274,6 @@ F: tests/acceptance/ppc_mpc8544ds.py New World (mac99) M: Mark Cave-Ayland -R: David Gibson -R: Greg Kurz L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/mac_newworld.c @@ -1294,8 +1292,6 @@ F: pc-bios/qemu_vga.ndrv Old World (g3beige) M: Mark Cave-Ayland -R: David Gibson -R: Greg Kurz L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/mac_oldworld.c @@ -1309,8 +1305,6 @@ F: pc-bios/qemu_vga.ndrv PReP M: Hervé Poussineau -R: David Gibson -R: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/prep.c @@ -1367,8 +1361,6 @@ F: tests/acceptance/ppc_virtex_ml507.py sam460ex M: BALATON Zoltan -R: David Gibson -R: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/sam460ex.c @@ -1382,7 +1374,6 @@ F: roms/u-boot-sam460ex pegasos2 M: BALATON Zoltan -R: David Gibson L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/pegasos2.c From 0f514eea217beeb89593fff0c222a30fa99008ca Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 21 Sep 2021 13:02:17 +1000 Subject: [PATCH 0080/1334] MAINTAINERS: Orphan obscure ppc platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are a nunber of old embedded ppc machine types which have been little changed and in "Odd Fixes" state for a long time. With both myself and Greg Kurz moving toward other areas, we no longer have the capacity to keep reviewing and maintaining even the rare patches that come in for those platforms. Therefore, remove our names as reviewers and mark these platforms as orphaned. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater --- MAINTAINERS | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 4d547e5604..a79543a877 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1236,24 +1236,18 @@ F: hw/openrisc/openrisc_sim.c PowerPC Machines ---------------- 405 -M: David Gibson -M: Greg Kurz L: qemu-ppc@nongnu.org -S: Odd Fixes +S: Orphan F: hw/ppc/ppc405_boards.c Bamboo -M: David Gibson -M: Greg Kurz L: qemu-ppc@nongnu.org -S: Odd Fixes +S: Orphan F: hw/ppc/ppc440_bamboo.c e500 -M: David Gibson -M: Greg Kurz L: qemu-ppc@nongnu.org -S: Odd Fixes +S: Orphan F: hw/ppc/e500* F: hw/gpio/mpc8xxx.c F: hw/i2c/mpc_i2c.c @@ -1264,10 +1258,8 @@ F: include/hw/pci-host/ppce500.h F: pc-bios/u-boot.e500 mpc8544ds -M: David Gibson -M: Greg Kurz L: qemu-ppc@nongnu.org -S: Odd Fixes +S: Orphan F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544_guts.c F: tests/acceptance/ppc_mpc8544ds.py @@ -1777,9 +1769,8 @@ F: include/hw/acpi/ghes.h F: docs/specs/acpi_hest_ghes.rst ppc4xx -M: David Gibson L: qemu-ppc@nongnu.org -S: Odd Fixes +S: Orphan F: hw/ppc/ppc4*.c F: hw/i2c/ppc4xx_i2c.c F: include/hw/ppc/ppc4xx.h From 689d24938c9a12c0fc904f8d5e2955bb4a0717c6 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 21 Sep 2021 13:20:24 +1000 Subject: [PATCH 0081/1334] MAINTAINERS: Remove David & Greg as reviewers/co-maintainers of powernv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With our interests moving to other areas, Greg and myself no longer have capacity to be regular reviewers of code for the powernv machine type, let alone co-maintainers. Additionally, not being IBM employees, we don't have easy access to the hardware information we'd need for good review. Therefore, remove our names as reviewers and/or co-maintainers of the powernv machine type, and the related XIVE interrupt controller. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Acked-by: Cédric Le Goater --- MAINTAINERS | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index a79543a877..ed9691d65c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1331,8 +1331,6 @@ F: tests/acceptance/ppc_pseries.py PowerNV (Non-Virtualized) M: Cédric Le Goater -M: David Gibson -M: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/pnv* @@ -2225,8 +2223,6 @@ T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE M: Cédric Le Goater -R: David Gibson -R: Greg Kurz L: qemu-ppc@nongnu.org S: Supported F: hw/*/*xive* From ff8cdbbd7e4bb7a7422c080c004b899214a08b3a Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 21 Sep 2021 13:34:35 +1000 Subject: [PATCH 0082/1334] MAINTAINERS: Add information for OpenPIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OpenPIC interrupt controller was once the de facto standard on ppc machines. In qemu it's now only used on some Macintosh and the Freescale e500 machine. It has no listed maintainer, and as far as I know, no-one who's really familiar with it any more. Since I'm moving away from the area, I no longer have capacity to do even minimal maintenance of it under the auspices of the ppc targets in general. Therefore, this patch lists the main part of openpic, and marks it as "Odd Fixes" to be looked after by Mark Cave-Ayland who handles the Macintosh targets. The openpic_kvm variant is only used on e500, so add it to the files for that machine type (itself already Orphaned). Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz Reviewed-by: Mark Cave-Ayland --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ed9691d65c..688233b44a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1256,6 +1256,8 @@ F: hw/pci-host/ppce500.c F: include/hw/ppc/ppc_e500.h F: include/hw/pci-host/ppce500.h F: pc-bios/u-boot.e500 +F: hw/intc/openpic_kvm.h +F: include/hw/ppc/openpic_kvm.h mpc8544ds L: qemu-ppc@nongnu.org @@ -2258,6 +2260,12 @@ F: net/can/* F: hw/net/can/* F: include/net/can_*.h +OpenPIC interrupt controller +M: Mark Cave-Ayland +S: Odd Fixes +F: hw/intc/openpic.c +F: include/hw/ppc/openpic.h + Subsystems ---------- Overall Audio backends From 85d887be82905aa81b5d3d6c483ff0fa9958382b Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 21 Sep 2021 14:01:12 +1000 Subject: [PATCH 0083/1334] MAINTAINERS: Demote sPAPR from "Supported" to "Maintained" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu/KVM on Power is no longer my primary job responsibility, nor Greg Kurz'. I still have some time for upstream maintenance, but it's no longer accurate to say that I'm paid to do so. Therefore, reduce sPAPR (the "pseries" machine type) from Supported to Maintained. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 688233b44a..50435b8d2f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1317,7 +1317,7 @@ sPAPR M: David Gibson M: Greg Kurz L: qemu-ppc@nongnu.org -S: Supported +S: Maintained F: hw/*/spapr* F: include/hw/*/spapr* F: hw/*/xics* From 01e75d87834a5188f98defa2d1f88b69dca7e9a0 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 20 Sep 2021 22:39:31 +0200 Subject: [PATCH 0084/1334] allwinner-h3: Switch to SMC as PSCI conduit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Allwinner H3 SoC uses Cortex-A7 cores which support virtualization. However, today we are configuring QEMU to use HVC as PSCI conduit. That means HVC calls get trapped into QEMU instead of the guest's own emulated CPU and thus break the guest's ability to execute virtualization. Fix this by moving to SMC as conduit, freeing up HYP completely to the VM. Signed-off-by: Alexander Graf Message-id: 20210920203931.66527-1-agraf@csgraf.de Fixes: 740dafc0ba0 ("hw/arm: add Allwinner H3 System-on-Chip") Reviewed-by: Niek Linnenbank Tested-by: Niek Linnenbank Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/arm/allwinner-h3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 27f1070145..f9b7ed1871 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -237,7 +237,7 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp) /* Provide Power State Coordination Interface */ qdev_prop_set_int32(DEVICE(&s->cpus[i]), "psci-conduit", - QEMU_PSCI_CONDUIT_HVC); + QEMU_PSCI_CONDUIT_SMC); /* Disable secondary CPUs */ qdev_prop_set_bit(DEVICE(&s->cpus[i]), "start-powered-off", From 9fcd15b9193e819b6cc2fd0a45e3506148812bb4 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Sep 2021 17:54:04 +0200 Subject: [PATCH 0085/1334] arm: tcg: Adhere to SMCCC 1.3 section 5.2 The SMCCC 1.3 spec section 5.2 says The Unknown SMC Function Identifier is a sign-extended value of (-1) that is returned in the R0, W0 or X0 registers. An implementation must return this error code when it receives: * An SMC or HVC call with an unknown Function Identifier * An SMC or HVC call for a removed Function Identifier * An SMC64/HVC64 call from AArch32 state To comply with these statements, let's always return -1 when we encounter an unknown HVC or SMC call. Signed-off-by: Alexander Graf Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/psci.c | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/target/arm/psci.c b/target/arm/psci.c index 6709e28013..b279c0b9a4 100644 --- a/target/arm/psci.c +++ b/target/arm/psci.c @@ -27,15 +27,13 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type) { - /* Return true if the r0/x0 value indicates a PSCI call and - * the exception type matches the configured PSCI conduit. This is - * called before the SMC/HVC instruction is executed, to decide whether - * we should treat it as a PSCI call or with the architecturally + /* + * Return true if the exception type matches the configured PSCI conduit. + * This is called before the SMC/HVC instruction is executed, to decide + * whether we should treat it as a PSCI call or with the architecturally * defined behaviour for an SMC or HVC (which might be UNDEF or trap * to EL2 or to EL3). */ - CPUARMState *env = &cpu->env; - uint64_t param = is_a64(env) ? env->xregs[0] : env->regs[0]; switch (excp_type) { case EXCP_HVC: @@ -52,27 +50,7 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type) return false; } - switch (param) { - case QEMU_PSCI_0_2_FN_PSCI_VERSION: - case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: - case QEMU_PSCI_0_2_FN_AFFINITY_INFO: - case QEMU_PSCI_0_2_FN64_AFFINITY_INFO: - case QEMU_PSCI_0_2_FN_SYSTEM_RESET: - case QEMU_PSCI_0_2_FN_SYSTEM_OFF: - case QEMU_PSCI_0_1_FN_CPU_ON: - case QEMU_PSCI_0_2_FN_CPU_ON: - case QEMU_PSCI_0_2_FN64_CPU_ON: - case QEMU_PSCI_0_1_FN_CPU_OFF: - case QEMU_PSCI_0_2_FN_CPU_OFF: - case QEMU_PSCI_0_1_FN_CPU_SUSPEND: - case QEMU_PSCI_0_2_FN_CPU_SUSPEND: - case QEMU_PSCI_0_2_FN64_CPU_SUSPEND: - case QEMU_PSCI_0_1_FN_MIGRATE: - case QEMU_PSCI_0_2_FN_MIGRATE: - return true; - default: - return false; - } + return true; } void arm_handle_psci_call(ARMCPU *cpu) @@ -194,10 +172,9 @@ void arm_handle_psci_call(ARMCPU *cpu) break; case QEMU_PSCI_0_1_FN_MIGRATE: case QEMU_PSCI_0_2_FN_MIGRATE: + default: ret = QEMU_PSCI_RET_NOT_SUPPORTED; break; - default: - g_assert_not_reached(); } err: From 68fbcc344ef6fb2dff0eb4cac0319ea7af010a7f Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:23:52 -0700 Subject: [PATCH 0086/1334] hw/nvram: Introduce Xilinx eFuse QOM This introduces the QOM for Xilinx eFuse, an one-time field-programmable storage bit array. The actual mmio interface to the array varies by device families and will be provided in different change-sets. Co-authored-by: Edgar E. Iglesias Co-authored-by: Sai Pavan Boddu Signed-off-by: Edgar E. Iglesias Signed-off-by: Sai Pavan Boddu Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-2-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/nvram/Kconfig | 7 + hw/nvram/meson.build | 2 + hw/nvram/xlnx-efuse-crc.c | 119 +++++++++++++++ hw/nvram/xlnx-efuse.c | 280 ++++++++++++++++++++++++++++++++++ include/hw/nvram/xlnx-efuse.h | 132 ++++++++++++++++ 5 files changed, 540 insertions(+) create mode 100644 hw/nvram/xlnx-efuse-crc.c create mode 100644 hw/nvram/xlnx-efuse.c create mode 100644 include/hw/nvram/xlnx-efuse.h diff --git a/hw/nvram/Kconfig b/hw/nvram/Kconfig index e872fcb194..252251f366 100644 --- a/hw/nvram/Kconfig +++ b/hw/nvram/Kconfig @@ -15,3 +15,10 @@ config NMC93XX_EEPROM config CHRP_NVRAM bool + +config XLNX_EFUSE_CRC + bool + +config XLNX_EFUSE + bool + select XLNX_EFUSE_CRC diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index fd2951a860..623c94efff 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -9,5 +9,7 @@ softmmu_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c')) softmmu_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c')) softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c')) softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_CRC', if_true: files('xlnx-efuse-crc.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE', if_true: files('xlnx-efuse.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c')) diff --git a/hw/nvram/xlnx-efuse-crc.c b/hw/nvram/xlnx-efuse-crc.c new file mode 100644 index 0000000000..5a5cc13f39 --- /dev/null +++ b/hw/nvram/xlnx-efuse-crc.c @@ -0,0 +1,119 @@ +/* + * Xilinx eFuse/bbram CRC calculator + * + * Copyright (c) 2021 Xilinx Inc. + * + * 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 "hw/nvram/xlnx-efuse.h" + +static uint32_t xlnx_efuse_u37_crc(uint32_t prev_crc, uint32_t data, + uint32_t addr) +{ + /* A table for 7-bit slicing */ + static const uint32_t crc_tab[128] = { + 0x00000000, 0xe13b70f7, 0xc79a971f, 0x26a1e7e8, + 0x8ad958cf, 0x6be22838, 0x4d43cfd0, 0xac78bf27, + 0x105ec76f, 0xf165b798, 0xd7c45070, 0x36ff2087, + 0x9a879fa0, 0x7bbcef57, 0x5d1d08bf, 0xbc267848, + 0x20bd8ede, 0xc186fe29, 0xe72719c1, 0x061c6936, + 0xaa64d611, 0x4b5fa6e6, 0x6dfe410e, 0x8cc531f9, + 0x30e349b1, 0xd1d83946, 0xf779deae, 0x1642ae59, + 0xba3a117e, 0x5b016189, 0x7da08661, 0x9c9bf696, + 0x417b1dbc, 0xa0406d4b, 0x86e18aa3, 0x67dafa54, + 0xcba24573, 0x2a993584, 0x0c38d26c, 0xed03a29b, + 0x5125dad3, 0xb01eaa24, 0x96bf4dcc, 0x77843d3b, + 0xdbfc821c, 0x3ac7f2eb, 0x1c661503, 0xfd5d65f4, + 0x61c69362, 0x80fde395, 0xa65c047d, 0x4767748a, + 0xeb1fcbad, 0x0a24bb5a, 0x2c855cb2, 0xcdbe2c45, + 0x7198540d, 0x90a324fa, 0xb602c312, 0x5739b3e5, + 0xfb410cc2, 0x1a7a7c35, 0x3cdb9bdd, 0xdde0eb2a, + 0x82f63b78, 0x63cd4b8f, 0x456cac67, 0xa457dc90, + 0x082f63b7, 0xe9141340, 0xcfb5f4a8, 0x2e8e845f, + 0x92a8fc17, 0x73938ce0, 0x55326b08, 0xb4091bff, + 0x1871a4d8, 0xf94ad42f, 0xdfeb33c7, 0x3ed04330, + 0xa24bb5a6, 0x4370c551, 0x65d122b9, 0x84ea524e, + 0x2892ed69, 0xc9a99d9e, 0xef087a76, 0x0e330a81, + 0xb21572c9, 0x532e023e, 0x758fe5d6, 0x94b49521, + 0x38cc2a06, 0xd9f75af1, 0xff56bd19, 0x1e6dcdee, + 0xc38d26c4, 0x22b65633, 0x0417b1db, 0xe52cc12c, + 0x49547e0b, 0xa86f0efc, 0x8ecee914, 0x6ff599e3, + 0xd3d3e1ab, 0x32e8915c, 0x144976b4, 0xf5720643, + 0x590ab964, 0xb831c993, 0x9e902e7b, 0x7fab5e8c, + 0xe330a81a, 0x020bd8ed, 0x24aa3f05, 0xc5914ff2, + 0x69e9f0d5, 0x88d28022, 0xae7367ca, 0x4f48173d, + 0xf36e6f75, 0x12551f82, 0x34f4f86a, 0xd5cf889d, + 0x79b737ba, 0x988c474d, 0xbe2da0a5, 0x5f16d052 + }; + + /* + * eFuse calculation is shown here: + * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_utils.c#L1496 + * + * Each u32 word is appended a 5-bit value, for a total of 37 bits; see: + * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_utils.c#L1356 + */ + uint32_t crc = prev_crc; + const unsigned rshf = 7; + const uint32_t im = (1 << rshf) - 1; + const uint32_t rm = (1 << (32 - rshf)) - 1; + const uint32_t i2 = (1 << 2) - 1; + const uint32_t r2 = (1 << 30) - 1; + + unsigned j; + uint32_t i, r; + uint64_t w; + + w = (uint64_t)(addr) << 32; + w |= data; + + /* Feed 35 bits, in 5 rounds, each a slice of 7 bits */ + for (j = 0; j < 5; j++) { + r = rm & (crc >> rshf); + i = im & (crc ^ w); + crc = crc_tab[i] ^ r; + + w >>= rshf; + } + + /* Feed the remaining 2 bits */ + r = r2 & (crc >> 2); + i = i2 & (crc ^ w); + crc = crc_tab[i << (rshf - 2)] ^ r; + + return crc; +} + +uint32_t xlnx_efuse_calc_crc(const uint32_t *data, unsigned u32_cnt, + unsigned zpads) +{ + uint32_t crc = 0; + unsigned index; + + for (index = zpads; index; index--) { + crc = xlnx_efuse_u37_crc(crc, 0, (index + u32_cnt)); + } + + for (index = u32_cnt; index; index--) { + crc = xlnx_efuse_u37_crc(crc, data[index - 1], index); + } + + return crc; +} diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c new file mode 100644 index 0000000000..ee1caab54c --- /dev/null +++ b/hw/nvram/xlnx-efuse.c @@ -0,0 +1,280 @@ +/* + * QEMU model of the EFUSE eFuse + * + * Copyright (c) 2015 Xilinx Inc. + * + * Written by Edgar E. Iglesias + * + * 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 "hw/nvram/xlnx-efuse.h" + +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "sysemu/blockdev.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" + +#define TBIT0_OFFSET 28 +#define TBIT1_OFFSET 29 +#define TBIT2_OFFSET 30 +#define TBIT3_OFFSET 31 +#define TBITS_PATTERN (0x0AU << TBIT0_OFFSET) +#define TBITS_MASK (0x0FU << TBIT0_OFFSET) + +bool xlnx_efuse_get_bit(XlnxEFuse *s, unsigned int bit) +{ + bool b = s->fuse32[bit / 32] & (1 << (bit % 32)); + return b; +} + +static int efuse_bytes(XlnxEFuse *s) +{ + return ROUND_UP((s->efuse_nr * s->efuse_size) / 8, 4); +} + +static int efuse_bdrv_read(XlnxEFuse *s, Error **errp) +{ + uint32_t *ram = s->fuse32; + int nr = efuse_bytes(s); + + if (!s->blk) { + return 0; + } + + s->blk_ro = !blk_supports_write_perm(s->blk); + if (!s->blk_ro) { + int rc; + + rc = blk_set_perm(s->blk, + (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), + BLK_PERM_ALL, NULL); + if (rc) { + s->blk_ro = true; + } + } + if (s->blk_ro) { + warn_report("%s: Skip saving updates to read-only eFUSE backstore.", + blk_name(s->blk)); + } + + if (blk_pread(s->blk, 0, ram, nr) < 0) { + error_setg(errp, "%s: Failed to read %u bytes from eFUSE backstore.", + blk_name(s->blk), nr); + return -1; + } + + /* Convert from little-endian backstore for each 32-bit row */ + nr /= 4; + while (nr--) { + ram[nr] = le32_to_cpu(ram[nr]); + } + + return 0; +} + +static void efuse_bdrv_sync(XlnxEFuse *s, unsigned int bit) +{ + unsigned int row_offset; + uint32_t le32; + + if (!s->blk || s->blk_ro) { + return; /* Silent on read-only backend to avoid message flood */ + } + + /* Backstore is always in little-endian */ + le32 = cpu_to_le32(xlnx_efuse_get_row(s, bit)); + + row_offset = (bit / 32) * 4; + if (blk_pwrite(s->blk, row_offset, &le32, 4, 0) < 0) { + error_report("%s: Failed to write offset %u of eFUSE backstore.", + blk_name(s->blk), row_offset); + } +} + +static int efuse_ro_bits_cmp(const void *a, const void *b) +{ + uint32_t i = *(const uint32_t *)a; + uint32_t j = *(const uint32_t *)b; + + return (i > j) - (i < j); +} + +static void efuse_ro_bits_sort(XlnxEFuse *s) +{ + uint32_t *ary = s->ro_bits; + const uint32_t cnt = s->ro_bits_cnt; + + if (ary && cnt > 1) { + qsort(ary, cnt, sizeof(ary[0]), efuse_ro_bits_cmp); + } +} + +static bool efuse_ro_bits_find(XlnxEFuse *s, uint32_t k) +{ + const uint32_t *ary = s->ro_bits; + const uint32_t cnt = s->ro_bits_cnt; + + if (!ary || !cnt) { + return false; + } + + return bsearch(&k, ary, cnt, sizeof(ary[0]), efuse_ro_bits_cmp) != NULL; +} + +bool xlnx_efuse_set_bit(XlnxEFuse *s, unsigned int bit) +{ + if (efuse_ro_bits_find(s, bit)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: WARN: " + "Ignored setting of readonly efuse bit<%u,%u>!\n", + object_get_canonical_path(OBJECT(s)), + (bit / 32), (bit % 32)); + return false; + } + + s->fuse32[bit / 32] |= 1 << (bit % 32); + efuse_bdrv_sync(s, bit); + return true; +} + +bool xlnx_efuse_k256_check(XlnxEFuse *s, uint32_t crc, unsigned start) +{ + uint32_t calc; + + /* A key always occupies multiple of whole rows */ + assert((start % 32) == 0); + + calc = xlnx_efuse_calc_crc(&s->fuse32[start / 32], (256 / 32), 0); + return calc == crc; +} + +uint32_t xlnx_efuse_tbits_check(XlnxEFuse *s) +{ + int nr; + uint32_t check = 0; + + for (nr = s->efuse_nr; nr-- > 0; ) { + int efuse_start_row_num = (s->efuse_size * nr) / 32; + uint32_t data = s->fuse32[efuse_start_row_num]; + + /* + * If the option is on, auto-init blank T-bits. + * (non-blank will still be reported as '0' in the check, e.g., + * for error-injection tests) + */ + if ((data & TBITS_MASK) == 0 && s->init_tbits) { + data |= TBITS_PATTERN; + + s->fuse32[efuse_start_row_num] = data; + efuse_bdrv_sync(s, (efuse_start_row_num * 32 + TBIT0_OFFSET)); + } + + check = (check << 1) | ((data & TBITS_MASK) == TBITS_PATTERN); + } + + return check; +} + +static void efuse_realize(DeviceState *dev, Error **errp) +{ + XlnxEFuse *s = XLNX_EFUSE(dev); + + /* Sort readonly-list for bsearch lookup */ + efuse_ro_bits_sort(s); + + if ((s->efuse_size % 32) != 0) { + error_setg(errp, + "%s.efuse-size: %u: property value not multiple of 32.", + object_get_canonical_path(OBJECT(dev)), s->efuse_size); + return; + } + + s->fuse32 = g_malloc0(efuse_bytes(s)); + if (efuse_bdrv_read(s, errp)) { + g_free(s->fuse32); + } +} + +static void efuse_prop_set_drive(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + + qdev_prop_drive.set(obj, v, name, opaque, errp); + + /* Fill initial data if backend is attached after realized */ + if (dev->realized) { + efuse_bdrv_read(XLNX_EFUSE(obj), errp); + } +} + +static void efuse_prop_get_drive(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + qdev_prop_drive.get(obj, v, name, opaque, errp); +} + +static void efuse_prop_release_drive(Object *obj, const char *name, + void *opaque) +{ + qdev_prop_drive.release(obj, name, opaque); +} + +static const PropertyInfo efuse_prop_drive = { + .name = "str", + .description = "Node name or ID of a block device to use as eFUSE backend", + .realized_set_allowed = true, + .get = efuse_prop_get_drive, + .set = efuse_prop_set_drive, + .release = efuse_prop_release_drive, +}; + +static Property efuse_properties[] = { + DEFINE_PROP("drive", XlnxEFuse, blk, efuse_prop_drive, BlockBackend *), + DEFINE_PROP_UINT8("efuse-nr", XlnxEFuse, efuse_nr, 3), + DEFINE_PROP_UINT32("efuse-size", XlnxEFuse, efuse_size, 64 * 32), + DEFINE_PROP_BOOL("init-factory-tbits", XlnxEFuse, init_tbits, true), + DEFINE_PROP_ARRAY("read-only", XlnxEFuse, ro_bits_cnt, ro_bits, + qdev_prop_uint32, uint32_t), + DEFINE_PROP_END_OF_LIST(), +}; + +static void efuse_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = efuse_realize; + device_class_set_props(dc, efuse_properties); +} + +static const TypeInfo efuse_info = { + .name = TYPE_XLNX_EFUSE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(XlnxEFuse), + .class_init = efuse_class_init, +}; + +static void efuse_register_types(void) +{ + type_register_static(&efuse_info); +} +type_init(efuse_register_types) diff --git a/include/hw/nvram/xlnx-efuse.h b/include/hw/nvram/xlnx-efuse.h new file mode 100644 index 0000000000..58414e468b --- /dev/null +++ b/include/hw/nvram/xlnx-efuse.h @@ -0,0 +1,132 @@ +/* + * QEMU model of the Xilinx eFuse core + * + * Copyright (c) 2015 Xilinx Inc. + * + * Written by Edgar E. Iglesias + * + * 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. + */ + +#ifndef XLNX_EFUSE_H +#define XLNX_EFUSE_H + +#include "sysemu/block-backend.h" +#include "hw/qdev-core.h" + +#define TYPE_XLNX_EFUSE "xlnx,efuse" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxEFuse, XLNX_EFUSE); + +struct XlnxEFuse { + DeviceState parent_obj; + BlockBackend *blk; + bool blk_ro; + uint32_t *fuse32; + + DeviceState *dev; + + bool init_tbits; + + uint8_t efuse_nr; + uint32_t efuse_size; + + uint32_t *ro_bits; + uint32_t ro_bits_cnt; +}; + +/** + * xlnx_efuse_calc_crc: + * @data: an array of 32-bit words for which the CRC should be computed + * @u32_cnt: the array size in number of 32-bit words + * @zpads: the number of 32-bit zeros prepended to @data before computation + * + * This function is used to compute the CRC for an array of 32-bit words, + * using a Xilinx-specific data padding. + * + * Returns: the computed 32-bit CRC + */ +uint32_t xlnx_efuse_calc_crc(const uint32_t *data, unsigned u32_cnt, + unsigned zpads); + +/** + * xlnx_efuse_get_bit: + * @s: the efuse object + * @bit: the efuse bit-address to read the data + * + * Returns: the bit, 0 or 1, at @bit of object @s + */ +bool xlnx_efuse_get_bit(XlnxEFuse *s, unsigned int bit); + +/** + * xlnx_efuse_set_bit: + * @s: the efuse object + * @bit: the efuse bit-address to be written a value of 1 + * + * Returns: true on success, false on failure + */ +bool xlnx_efuse_set_bit(XlnxEFuse *s, unsigned int bit); + +/** + * xlnx_efuse_k256_check: + * @s: the efuse object + * @crc: the 32-bit CRC to be compared with + * @start: the efuse bit-address (which must be multiple of 32) of the + * start of a 256-bit array + * + * This function computes the CRC of a 256-bit array starting at @start + * then compares to the given @crc + * + * Returns: true of @crc == computed, false otherwise + */ +bool xlnx_efuse_k256_check(XlnxEFuse *s, uint32_t crc, unsigned start); + +/** + * xlnx_efuse_tbits_check: + * @s: the efuse object + * + * This function inspects a number of efuse bits at specific addresses + * to see if they match a validation pattern. Each pattern is a group + * of 4 bits, and there are 3 groups. + * + * Returns: a 3-bit mask, where a bit of '1' means the corresponding + * group has a valid pattern. + */ +uint32_t xlnx_efuse_tbits_check(XlnxEFuse *s); + +/** + * xlnx_efuse_get_row: + * @s: the efuse object + * @bit: the efuse bit address for which a 32-bit value is read + * + * Returns: the entire 32 bits of the efuse, starting at a bit + * address that is multiple of 32 and contains the bit at @bit + */ +static inline uint32_t xlnx_efuse_get_row(XlnxEFuse *s, unsigned int bit) +{ + if (!(s->fuse32)) { + return 0; + } else { + unsigned int row_idx = bit / 32; + + assert(row_idx < (s->efuse_size * s->efuse_nr / 32)); + return s->fuse32[row_idx]; + } +} + +#endif From 9e4aa1fafef624ee4ae6006497bed0cc112135d3 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:23:53 -0700 Subject: [PATCH 0087/1334] hw/nvram: Introduce Xilinx Versal eFuse device This implements the Xilinx Versal eFuse, an one-time field-programmable non-volatile storage device. There is only one such device in the Xilinx Versal product family. This device has two separate mmio interfaces, a controller and a flatten readback. The controller provides interfaces for field-programming, configuration, control, and status. The flatten readback is a cache to provide a byte-accessible read-only interface to efficiently read efuse array. Co-authored-by: Edgar E. Iglesias Co-authored-by: Sai Pavan Boddu Signed-off-by: Edgar E. Iglesias Signed-off-by: Sai Pavan Boddu Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-3-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/nvram/Kconfig | 4 + hw/nvram/meson.build | 3 + hw/nvram/xlnx-versal-efuse-cache.c | 114 ++++ hw/nvram/xlnx-versal-efuse-ctrl.c | 783 +++++++++++++++++++++++++++ include/hw/nvram/xlnx-versal-efuse.h | 68 +++ 5 files changed, 972 insertions(+) create mode 100644 hw/nvram/xlnx-versal-efuse-cache.c create mode 100644 hw/nvram/xlnx-versal-efuse-ctrl.c create mode 100644 include/hw/nvram/xlnx-versal-efuse.h diff --git a/hw/nvram/Kconfig b/hw/nvram/Kconfig index 252251f366..6c2bb5afe3 100644 --- a/hw/nvram/Kconfig +++ b/hw/nvram/Kconfig @@ -22,3 +22,7 @@ config XLNX_EFUSE_CRC config XLNX_EFUSE bool select XLNX_EFUSE_CRC + +config XLNX_EFUSE_VERSAL + bool + select XLNX_EFUSE diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index 623c94efff..62352ad8ec 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -11,5 +11,8 @@ softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c')) softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c')) softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_CRC', if_true: files('xlnx-efuse-crc.c')) softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE', if_true: files('xlnx-efuse.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_VERSAL', if_true: files( + 'xlnx-versal-efuse-cache.c', + 'xlnx-versal-efuse-ctrl.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c')) diff --git a/hw/nvram/xlnx-versal-efuse-cache.c b/hw/nvram/xlnx-versal-efuse-cache.c new file mode 100644 index 0000000000..eaec64d785 --- /dev/null +++ b/hw/nvram/xlnx-versal-efuse-cache.c @@ -0,0 +1,114 @@ +/* + * QEMU model of the EFuse_Cache + * + * Copyright (c) 2017 Xilinx Inc. + * + * 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 "hw/nvram/xlnx-versal-efuse.h" + +#include "qemu/log.h" +#include "hw/qdev-properties.h" + +#define MR_SIZE 0xC00 + +static uint64_t efuse_cache_read(void *opaque, hwaddr addr, unsigned size) +{ + XlnxVersalEFuseCache *s = XLNX_VERSAL_EFUSE_CACHE(opaque); + unsigned int w0 = QEMU_ALIGN_DOWN(addr * 8, 32); + unsigned int w1 = QEMU_ALIGN_DOWN((addr + size - 1) * 8, 32); + + uint64_t ret; + + assert(w0 == w1 || (w0 + 32) == w1); + + ret = xlnx_versal_efuse_read_row(s->efuse, w1, NULL); + if (w0 < w1) { + ret <<= 32; + ret |= xlnx_versal_efuse_read_row(s->efuse, w0, NULL); + } + + /* If 'addr' unaligned, the guest is always assumed to be little-endian. */ + addr &= 3; + if (addr) { + ret >>= 8 * addr; + } + + return ret; +} + +static void efuse_cache_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + /* No Register Writes allowed */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: efuse cache registers are read-only", + __func__); +} + +static const MemoryRegionOps efuse_cache_ops = { + .read = efuse_cache_read, + .write = efuse_cache_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void efuse_cache_init(Object *obj) +{ + XlnxVersalEFuseCache *s = XLNX_VERSAL_EFUSE_CACHE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &efuse_cache_ops, s, + TYPE_XLNX_VERSAL_EFUSE_CACHE, MR_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static Property efuse_cache_props[] = { + DEFINE_PROP_LINK("efuse", + XlnxVersalEFuseCache, efuse, + TYPE_XLNX_EFUSE, XlnxEFuse *), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void efuse_cache_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, efuse_cache_props); +} + +static const TypeInfo efuse_cache_info = { + .name = TYPE_XLNX_VERSAL_EFUSE_CACHE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalEFuseCache), + .class_init = efuse_cache_class_init, + .instance_init = efuse_cache_init, +}; + +static void efuse_cache_register_types(void) +{ + type_register_static(&efuse_cache_info); +} + +type_init(efuse_cache_register_types) diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c new file mode 100644 index 0000000000..d362376703 --- /dev/null +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -0,0 +1,783 @@ +/* + * QEMU model of the Versal eFuse controller + * + * Copyright (c) 2020 Xilinx Inc. + * + * 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 "hw/nvram/xlnx-versal-efuse.h" + +#include "qemu/log.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" + +#ifndef XLNX_VERSAL_EFUSE_CTRL_ERR_DEBUG +#define XLNX_VERSAL_EFUSE_CTRL_ERR_DEBUG 0 +#endif + +REG32(WR_LOCK, 0x0) + FIELD(WR_LOCK, LOCK, 0, 16) +REG32(CFG, 0x4) + FIELD(CFG, SLVERR_ENABLE, 5, 1) + FIELD(CFG, MARGIN_RD, 2, 1) + FIELD(CFG, PGM_EN, 1, 1) +REG32(STATUS, 0x8) + FIELD(STATUS, AES_USER_KEY_1_CRC_PASS, 11, 1) + FIELD(STATUS, AES_USER_KEY_1_CRC_DONE, 10, 1) + FIELD(STATUS, AES_USER_KEY_0_CRC_PASS, 9, 1) + FIELD(STATUS, AES_USER_KEY_0_CRC_DONE, 8, 1) + FIELD(STATUS, AES_CRC_PASS, 7, 1) + FIELD(STATUS, AES_CRC_DONE, 6, 1) + FIELD(STATUS, CACHE_DONE, 5, 1) + FIELD(STATUS, CACHE_LOAD, 4, 1) + FIELD(STATUS, EFUSE_2_TBIT, 2, 1) + FIELD(STATUS, EFUSE_1_TBIT, 1, 1) + FIELD(STATUS, EFUSE_0_TBIT, 0, 1) +REG32(EFUSE_PGM_ADDR, 0xc) + FIELD(EFUSE_PGM_ADDR, PAGE, 13, 4) + FIELD(EFUSE_PGM_ADDR, ROW, 5, 8) + FIELD(EFUSE_PGM_ADDR, COLUMN, 0, 5) +REG32(EFUSE_RD_ADDR, 0x10) + FIELD(EFUSE_RD_ADDR, PAGE, 13, 4) + FIELD(EFUSE_RD_ADDR, ROW, 5, 8) +REG32(EFUSE_RD_DATA, 0x14) +REG32(TPGM, 0x18) + FIELD(TPGM, VALUE, 0, 16) +REG32(TRD, 0x1c) + FIELD(TRD, VALUE, 0, 8) +REG32(TSU_H_PS, 0x20) + FIELD(TSU_H_PS, VALUE, 0, 8) +REG32(TSU_H_PS_CS, 0x24) + FIELD(TSU_H_PS_CS, VALUE, 0, 8) +REG32(TRDM, 0x28) + FIELD(TRDM, VALUE, 0, 8) +REG32(TSU_H_CS, 0x2c) + FIELD(TSU_H_CS, VALUE, 0, 8) +REG32(EFUSE_ISR, 0x30) + FIELD(EFUSE_ISR, APB_SLVERR, 31, 1) + FIELD(EFUSE_ISR, CACHE_PARITY_E2, 14, 1) + FIELD(EFUSE_ISR, CACHE_PARITY_E1, 13, 1) + FIELD(EFUSE_ISR, CACHE_PARITY_E0S, 12, 1) + FIELD(EFUSE_ISR, CACHE_PARITY_E0R, 11, 1) + FIELD(EFUSE_ISR, CACHE_APB_SLVERR, 10, 1) + FIELD(EFUSE_ISR, CACHE_REQ_ERROR, 9, 1) + FIELD(EFUSE_ISR, MAIN_REQ_ERROR, 8, 1) + FIELD(EFUSE_ISR, READ_ON_CACHE_LD, 7, 1) + FIELD(EFUSE_ISR, CACHE_FSM_ERROR, 6, 1) + FIELD(EFUSE_ISR, MAIN_FSM_ERROR, 5, 1) + FIELD(EFUSE_ISR, CACHE_ERROR, 4, 1) + FIELD(EFUSE_ISR, RD_ERROR, 3, 1) + FIELD(EFUSE_ISR, RD_DONE, 2, 1) + FIELD(EFUSE_ISR, PGM_ERROR, 1, 1) + FIELD(EFUSE_ISR, PGM_DONE, 0, 1) +REG32(EFUSE_IMR, 0x34) + FIELD(EFUSE_IMR, APB_SLVERR, 31, 1) + FIELD(EFUSE_IMR, CACHE_PARITY_E2, 14, 1) + FIELD(EFUSE_IMR, CACHE_PARITY_E1, 13, 1) + FIELD(EFUSE_IMR, CACHE_PARITY_E0S, 12, 1) + FIELD(EFUSE_IMR, CACHE_PARITY_E0R, 11, 1) + FIELD(EFUSE_IMR, CACHE_APB_SLVERR, 10, 1) + FIELD(EFUSE_IMR, CACHE_REQ_ERROR, 9, 1) + FIELD(EFUSE_IMR, MAIN_REQ_ERROR, 8, 1) + FIELD(EFUSE_IMR, READ_ON_CACHE_LD, 7, 1) + FIELD(EFUSE_IMR, CACHE_FSM_ERROR, 6, 1) + FIELD(EFUSE_IMR, MAIN_FSM_ERROR, 5, 1) + FIELD(EFUSE_IMR, CACHE_ERROR, 4, 1) + FIELD(EFUSE_IMR, RD_ERROR, 3, 1) + FIELD(EFUSE_IMR, RD_DONE, 2, 1) + FIELD(EFUSE_IMR, PGM_ERROR, 1, 1) + FIELD(EFUSE_IMR, PGM_DONE, 0, 1) +REG32(EFUSE_IER, 0x38) + FIELD(EFUSE_IER, APB_SLVERR, 31, 1) + FIELD(EFUSE_IER, CACHE_PARITY_E2, 14, 1) + FIELD(EFUSE_IER, CACHE_PARITY_E1, 13, 1) + FIELD(EFUSE_IER, CACHE_PARITY_E0S, 12, 1) + FIELD(EFUSE_IER, CACHE_PARITY_E0R, 11, 1) + FIELD(EFUSE_IER, CACHE_APB_SLVERR, 10, 1) + FIELD(EFUSE_IER, CACHE_REQ_ERROR, 9, 1) + FIELD(EFUSE_IER, MAIN_REQ_ERROR, 8, 1) + FIELD(EFUSE_IER, READ_ON_CACHE_LD, 7, 1) + FIELD(EFUSE_IER, CACHE_FSM_ERROR, 6, 1) + FIELD(EFUSE_IER, MAIN_FSM_ERROR, 5, 1) + FIELD(EFUSE_IER, CACHE_ERROR, 4, 1) + FIELD(EFUSE_IER, RD_ERROR, 3, 1) + FIELD(EFUSE_IER, RD_DONE, 2, 1) + FIELD(EFUSE_IER, PGM_ERROR, 1, 1) + FIELD(EFUSE_IER, PGM_DONE, 0, 1) +REG32(EFUSE_IDR, 0x3c) + FIELD(EFUSE_IDR, APB_SLVERR, 31, 1) + FIELD(EFUSE_IDR, CACHE_PARITY_E2, 14, 1) + FIELD(EFUSE_IDR, CACHE_PARITY_E1, 13, 1) + FIELD(EFUSE_IDR, CACHE_PARITY_E0S, 12, 1) + FIELD(EFUSE_IDR, CACHE_PARITY_E0R, 11, 1) + FIELD(EFUSE_IDR, CACHE_APB_SLVERR, 10, 1) + FIELD(EFUSE_IDR, CACHE_REQ_ERROR, 9, 1) + FIELD(EFUSE_IDR, MAIN_REQ_ERROR, 8, 1) + FIELD(EFUSE_IDR, READ_ON_CACHE_LD, 7, 1) + FIELD(EFUSE_IDR, CACHE_FSM_ERROR, 6, 1) + FIELD(EFUSE_IDR, MAIN_FSM_ERROR, 5, 1) + FIELD(EFUSE_IDR, CACHE_ERROR, 4, 1) + FIELD(EFUSE_IDR, RD_ERROR, 3, 1) + FIELD(EFUSE_IDR, RD_DONE, 2, 1) + FIELD(EFUSE_IDR, PGM_ERROR, 1, 1) + FIELD(EFUSE_IDR, PGM_DONE, 0, 1) +REG32(EFUSE_CACHE_LOAD, 0x40) + FIELD(EFUSE_CACHE_LOAD, LOAD, 0, 1) +REG32(EFUSE_PGM_LOCK, 0x44) + FIELD(EFUSE_PGM_LOCK, SPK_ID_LOCK, 0, 1) +REG32(EFUSE_AES_CRC, 0x48) +REG32(EFUSE_AES_USR_KEY0_CRC, 0x4c) +REG32(EFUSE_AES_USR_KEY1_CRC, 0x50) +REG32(EFUSE_PD, 0x54) +REG32(EFUSE_ANLG_OSC_SW_1LP, 0x60) +REG32(EFUSE_TEST_CTRL, 0x100) + +#define R_MAX (R_EFUSE_TEST_CTRL + 1) + +#define R_WR_LOCK_UNLOCK_PASSCODE (0xDF0D) + +/* + * eFuse layout references: + * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilnvm/src/xnvm_efuse_hw.h + */ +#define BIT_POS_OF(A_) \ + ((uint32_t)((A_) & (R_EFUSE_PGM_ADDR_ROW_MASK | \ + R_EFUSE_PGM_ADDR_COLUMN_MASK))) + +#define BIT_POS(R_, C_) \ + ((uint32_t)((R_EFUSE_PGM_ADDR_ROW_MASK \ + & ((R_) << R_EFUSE_PGM_ADDR_ROW_SHIFT)) \ + | \ + (R_EFUSE_PGM_ADDR_COLUMN_MASK \ + & ((C_) << R_EFUSE_PGM_ADDR_COLUMN_SHIFT)))) + +#define EFUSE_TBIT_POS(A_) (BIT_POS_OF(A_) >= BIT_POS(0, 28)) + +#define EFUSE_ANCHOR_ROW (0) +#define EFUSE_ANCHOR_3_COL (27) +#define EFUSE_ANCHOR_1_COL (1) + +#define EFUSE_AES_KEY_START BIT_POS(12, 0) +#define EFUSE_AES_KEY_END BIT_POS(19, 31) +#define EFUSE_USER_KEY_0_START BIT_POS(20, 0) +#define EFUSE_USER_KEY_0_END BIT_POS(27, 31) +#define EFUSE_USER_KEY_1_START BIT_POS(28, 0) +#define EFUSE_USER_KEY_1_END BIT_POS(35, 31) + +#define EFUSE_RD_BLOCKED_START EFUSE_AES_KEY_START +#define EFUSE_RD_BLOCKED_END EFUSE_USER_KEY_1_END + +#define EFUSE_GLITCH_DET_WR_LK BIT_POS(4, 31) +#define EFUSE_PPK0_WR_LK BIT_POS(43, 6) +#define EFUSE_PPK1_WR_LK BIT_POS(43, 7) +#define EFUSE_PPK2_WR_LK BIT_POS(43, 8) +#define EFUSE_AES_WR_LK BIT_POS(43, 11) +#define EFUSE_USER_KEY_0_WR_LK BIT_POS(43, 13) +#define EFUSE_USER_KEY_1_WR_LK BIT_POS(43, 15) +#define EFUSE_PUF_SYN_LK BIT_POS(43, 16) +#define EFUSE_DNA_WR_LK BIT_POS(43, 27) +#define EFUSE_BOOT_ENV_WR_LK BIT_POS(43, 28) + +#define EFUSE_PGM_LOCKED_START BIT_POS(44, 0) +#define EFUSE_PGM_LOCKED_END BIT_POS(51, 31) + +#define EFUSE_PUF_PAGE (2) +#define EFUSE_PUF_SYN_START BIT_POS(129, 0) +#define EFUSE_PUF_SYN_END BIT_POS(255, 27) + +#define EFUSE_KEY_CRC_LK_ROW (43) +#define EFUSE_AES_KEY_CRC_LK_MASK ((1U << 9) | (1U << 10)) +#define EFUSE_USER_KEY_0_CRC_LK_MASK (1U << 12) +#define EFUSE_USER_KEY_1_CRC_LK_MASK (1U << 14) + +/* + * A handy macro to return value of an array element, + * or a specific default if given index is out of bound. + */ +#define ARRAY_GET(A_, I_, D_) \ + ((unsigned int)(I_) < ARRAY_SIZE(A_) ? (A_)[I_] : (D_)) + +QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxVersalEFuseCtrl *)0)->regs)); + +typedef struct XlnxEFuseLkSpec { + uint16_t row; + uint16_t lk_bit; +} XlnxEFuseLkSpec; + +static void efuse_imr_update_irq(XlnxVersalEFuseCtrl *s) +{ + bool pending = s->regs[R_EFUSE_ISR] & ~s->regs[R_EFUSE_IMR]; + qemu_set_irq(s->irq_efuse_imr, pending); +} + +static void efuse_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(reg->opaque); + efuse_imr_update_irq(s); +} + +static uint64_t efuse_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(reg->opaque); + uint32_t val = val64; + + s->regs[R_EFUSE_IMR] &= ~val; + efuse_imr_update_irq(s); + return 0; +} + +static uint64_t efuse_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(reg->opaque); + uint32_t val = val64; + + s->regs[R_EFUSE_IMR] |= val; + efuse_imr_update_irq(s); + return 0; +} + +static void efuse_status_tbits_sync(XlnxVersalEFuseCtrl *s) +{ + uint32_t check = xlnx_efuse_tbits_check(s->efuse); + uint32_t val = s->regs[R_STATUS]; + + val = FIELD_DP32(val, STATUS, EFUSE_0_TBIT, !!(check & (1 << 0))); + val = FIELD_DP32(val, STATUS, EFUSE_1_TBIT, !!(check & (1 << 1))); + val = FIELD_DP32(val, STATUS, EFUSE_2_TBIT, !!(check & (1 << 2))); + + s->regs[R_STATUS] = val; +} + +static void efuse_anchor_bits_check(XlnxVersalEFuseCtrl *s) +{ + unsigned page; + + if (!s->efuse || !s->efuse->init_tbits) { + return; + } + + for (page = 0; page < s->efuse->efuse_nr; page++) { + uint32_t row = 0, bit; + + row = FIELD_DP32(row, EFUSE_PGM_ADDR, PAGE, page); + row = FIELD_DP32(row, EFUSE_PGM_ADDR, ROW, EFUSE_ANCHOR_ROW); + + bit = FIELD_DP32(row, EFUSE_PGM_ADDR, COLUMN, EFUSE_ANCHOR_3_COL); + if (!xlnx_efuse_get_bit(s->efuse, bit)) { + xlnx_efuse_set_bit(s->efuse, bit); + } + + bit = FIELD_DP32(row, EFUSE_PGM_ADDR, COLUMN, EFUSE_ANCHOR_1_COL); + if (!xlnx_efuse_get_bit(s->efuse, bit)) { + xlnx_efuse_set_bit(s->efuse, bit); + } + } +} + +static void efuse_key_crc_check(RegisterInfo *reg, uint32_t crc, + uint32_t pass_mask, uint32_t done_mask, + unsigned first, uint32_t lk_mask) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(reg->opaque); + uint32_t r, lk_bits; + + /* + * To start, assume both DONE and PASS, and clear PASS by xor + * if CRC-check fails or CRC-check disabled by lock fuse. + */ + r = s->regs[R_STATUS] | done_mask | pass_mask; + + lk_bits = xlnx_efuse_get_row(s->efuse, EFUSE_KEY_CRC_LK_ROW) & lk_mask; + if (lk_bits == 0 && xlnx_efuse_k256_check(s->efuse, crc, first)) { + pass_mask = 0; + } + + s->regs[R_STATUS] = r ^ pass_mask; +} + +static void efuse_data_sync(XlnxVersalEFuseCtrl *s) +{ + efuse_status_tbits_sync(s); +} + +static int efuse_lk_spec_cmp(const void *a, const void *b) +{ + uint16_t r1 = ((const XlnxEFuseLkSpec *)a)->row; + uint16_t r2 = ((const XlnxEFuseLkSpec *)b)->row; + + return (r1 > r2) - (r1 < r2); +} + +static void efuse_lk_spec_sort(XlnxVersalEFuseCtrl *s) +{ + XlnxEFuseLkSpec *ary = s->extra_pg0_lock_spec; + const uint32_t n8 = s->extra_pg0_lock_n16 * 2; + const uint32_t sz = sizeof(ary[0]); + const uint32_t cnt = n8 / sz; + + if (ary && cnt) { + qsort(ary, cnt, sz, efuse_lk_spec_cmp); + } +} + +static uint32_t efuse_lk_spec_find(XlnxVersalEFuseCtrl *s, uint32_t row) +{ + const XlnxEFuseLkSpec *ary = s->extra_pg0_lock_spec; + const uint32_t n8 = s->extra_pg0_lock_n16 * 2; + const uint32_t sz = sizeof(ary[0]); + const uint32_t cnt = n8 / sz; + const XlnxEFuseLkSpec *item = NULL; + + if (ary && cnt) { + XlnxEFuseLkSpec k = { .row = row, }; + + item = bsearch(&k, ary, cnt, sz, efuse_lk_spec_cmp); + } + + return item ? item->lk_bit : 0; +} + +static uint32_t efuse_bit_locked(XlnxVersalEFuseCtrl *s, uint32_t bit) +{ + /* Hard-coded locks */ + static const uint16_t pg0_hard_lock[] = { + [4] = EFUSE_GLITCH_DET_WR_LK, + [37] = EFUSE_BOOT_ENV_WR_LK, + + [8 ... 11] = EFUSE_DNA_WR_LK, + [12 ... 19] = EFUSE_AES_WR_LK, + [20 ... 27] = EFUSE_USER_KEY_0_WR_LK, + [28 ... 35] = EFUSE_USER_KEY_1_WR_LK, + [64 ... 71] = EFUSE_PPK0_WR_LK, + [72 ... 79] = EFUSE_PPK1_WR_LK, + [80 ... 87] = EFUSE_PPK2_WR_LK, + }; + + uint32_t row = FIELD_EX32(bit, EFUSE_PGM_ADDR, ROW); + uint32_t lk_bit = ARRAY_GET(pg0_hard_lock, row, 0); + + return lk_bit ? lk_bit : efuse_lk_spec_find(s, row); +} + +static bool efuse_pgm_locked(XlnxVersalEFuseCtrl *s, unsigned int bit) +{ + + unsigned int lock = 1; + + /* Global lock */ + if (!ARRAY_FIELD_EX32(s->regs, CFG, PGM_EN)) { + goto ret_lock; + } + + /* Row lock */ + switch (FIELD_EX32(bit, EFUSE_PGM_ADDR, PAGE)) { + case 0: + if (ARRAY_FIELD_EX32(s->regs, EFUSE_PGM_LOCK, SPK_ID_LOCK) && + bit >= EFUSE_PGM_LOCKED_START && bit <= EFUSE_PGM_LOCKED_END) { + goto ret_lock; + } + + lock = efuse_bit_locked(s, bit); + break; + case EFUSE_PUF_PAGE: + if (bit < EFUSE_PUF_SYN_START || bit > EFUSE_PUF_SYN_END) { + lock = 0; + goto ret_lock; + } + + lock = EFUSE_PUF_SYN_LK; + break; + default: + lock = 0; + goto ret_lock; + } + + /* Row lock by an efuse bit */ + if (lock) { + lock = xlnx_efuse_get_bit(s->efuse, lock); + } + + ret_lock: + return lock != 0; +} + +static void efuse_pgm_addr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(reg->opaque); + unsigned bit = val64; + bool ok = false; + + /* Always zero out PGM_ADDR because it is write-only */ + s->regs[R_EFUSE_PGM_ADDR] = 0; + + /* + * Indicate error if bit is write-protected (or read-only + * as guarded by efuse_set_bit()). + * + * Keep it simple by not modeling program timing. + * + * Note: model must NEVER clear the PGM_ERROR bit; it is + * up to guest to do so (or by reset). + */ + if (efuse_pgm_locked(s, bit)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Denied setting of efuse<%u, %u, %u>\n", + object_get_canonical_path(OBJECT(s)), + FIELD_EX32(bit, EFUSE_PGM_ADDR, PAGE), + FIELD_EX32(bit, EFUSE_PGM_ADDR, ROW), + FIELD_EX32(bit, EFUSE_PGM_ADDR, COLUMN)); + } else if (xlnx_efuse_set_bit(s->efuse, bit)) { + ok = true; + if (EFUSE_TBIT_POS(bit)) { + efuse_status_tbits_sync(s); + } + } + + if (!ok) { + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_ERROR, 1); + } + + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_DONE, 1); + efuse_imr_update_irq(s); +} + +static void efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(reg->opaque); + unsigned bit = val64; + bool denied; + + /* Always zero out RD_ADDR because it is write-only */ + s->regs[R_EFUSE_RD_ADDR] = 0; + + /* + * Indicate error if row is read-blocked. + * + * Note: model must NEVER clear the RD_ERROR bit; it is + * up to guest to do so (or by reset). + */ + s->regs[R_EFUSE_RD_DATA] = xlnx_versal_efuse_read_row(s->efuse, + bit, &denied); + if (denied) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Denied reading of efuse<%u, %u>\n", + object_get_canonical_path(OBJECT(s)), + FIELD_EX32(bit, EFUSE_RD_ADDR, PAGE), + FIELD_EX32(bit, EFUSE_RD_ADDR, ROW)); + + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_ERROR, 1); + } + + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_DONE, 1); + efuse_imr_update_irq(s); + return; +} + +static uint64_t efuse_cache_load_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(reg->opaque); + + if (val64 & R_EFUSE_CACHE_LOAD_LOAD_MASK) { + efuse_data_sync(s); + + ARRAY_FIELD_DP32(s->regs, STATUS, CACHE_DONE, 1); + efuse_imr_update_irq(s); + } + + return 0; +} + +static uint64_t efuse_pgm_lock_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(reg->opaque); + + /* Ignore all other bits */ + val64 = FIELD_EX32(val64, EFUSE_PGM_LOCK, SPK_ID_LOCK); + + /* Once the bit is written 1, only reset will clear it to 0 */ + val64 |= ARRAY_FIELD_EX32(s->regs, EFUSE_PGM_LOCK, SPK_ID_LOCK); + + return val64; +} + +static void efuse_aes_crc_postw(RegisterInfo *reg, uint64_t val64) +{ + efuse_key_crc_check(reg, val64, + R_STATUS_AES_CRC_PASS_MASK, + R_STATUS_AES_CRC_DONE_MASK, + EFUSE_AES_KEY_START, + EFUSE_AES_KEY_CRC_LK_MASK); +} + +static void efuse_aes_u0_crc_postw(RegisterInfo *reg, uint64_t val64) +{ + efuse_key_crc_check(reg, val64, + R_STATUS_AES_USER_KEY_0_CRC_PASS_MASK, + R_STATUS_AES_USER_KEY_0_CRC_DONE_MASK, + EFUSE_USER_KEY_0_START, + EFUSE_USER_KEY_0_CRC_LK_MASK); +} + +static void efuse_aes_u1_crc_postw(RegisterInfo *reg, uint64_t val64) +{ + efuse_key_crc_check(reg, val64, + R_STATUS_AES_USER_KEY_1_CRC_PASS_MASK, + R_STATUS_AES_USER_KEY_1_CRC_DONE_MASK, + EFUSE_USER_KEY_1_START, + EFUSE_USER_KEY_1_CRC_LK_MASK); +} + +static uint64_t efuse_wr_lock_prew(RegisterInfo *reg, uint64_t val) +{ + return val != R_WR_LOCK_UNLOCK_PASSCODE; +} + +static const RegisterAccessInfo efuse_ctrl_regs_info[] = { + { .name = "WR_LOCK", .addr = A_WR_LOCK, + .reset = 0x1, + .pre_write = efuse_wr_lock_prew, + },{ .name = "CFG", .addr = A_CFG, + .rsvd = 0x9, + },{ .name = "STATUS", .addr = A_STATUS, + .rsvd = 0x8, + .ro = 0xfff, + },{ .name = "EFUSE_PGM_ADDR", .addr = A_EFUSE_PGM_ADDR, + .post_write = efuse_pgm_addr_postw, + },{ .name = "EFUSE_RD_ADDR", .addr = A_EFUSE_RD_ADDR, + .rsvd = 0x1f, + .post_write = efuse_rd_addr_postw, + },{ .name = "EFUSE_RD_DATA", .addr = A_EFUSE_RD_DATA, + .ro = 0xffffffff, + },{ .name = "TPGM", .addr = A_TPGM, + },{ .name = "TRD", .addr = A_TRD, + .reset = 0x19, + },{ .name = "TSU_H_PS", .addr = A_TSU_H_PS, + .reset = 0xff, + },{ .name = "TSU_H_PS_CS", .addr = A_TSU_H_PS_CS, + .reset = 0x11, + },{ .name = "TRDM", .addr = A_TRDM, + .reset = 0x3a, + },{ .name = "TSU_H_CS", .addr = A_TSU_H_CS, + .reset = 0x16, + },{ .name = "EFUSE_ISR", .addr = A_EFUSE_ISR, + .rsvd = 0x7fff8000, + .w1c = 0x80007fff, + .post_write = efuse_isr_postw, + },{ .name = "EFUSE_IMR", .addr = A_EFUSE_IMR, + .reset = 0x80007fff, + .rsvd = 0x7fff8000, + .ro = 0xffffffff, + },{ .name = "EFUSE_IER", .addr = A_EFUSE_IER, + .rsvd = 0x7fff8000, + .pre_write = efuse_ier_prew, + },{ .name = "EFUSE_IDR", .addr = A_EFUSE_IDR, + .rsvd = 0x7fff8000, + .pre_write = efuse_idr_prew, + },{ .name = "EFUSE_CACHE_LOAD", .addr = A_EFUSE_CACHE_LOAD, + .pre_write = efuse_cache_load_prew, + },{ .name = "EFUSE_PGM_LOCK", .addr = A_EFUSE_PGM_LOCK, + .pre_write = efuse_pgm_lock_prew, + },{ .name = "EFUSE_AES_CRC", .addr = A_EFUSE_AES_CRC, + .post_write = efuse_aes_crc_postw, + },{ .name = "EFUSE_AES_USR_KEY0_CRC", .addr = A_EFUSE_AES_USR_KEY0_CRC, + .post_write = efuse_aes_u0_crc_postw, + },{ .name = "EFUSE_AES_USR_KEY1_CRC", .addr = A_EFUSE_AES_USR_KEY1_CRC, + .post_write = efuse_aes_u1_crc_postw, + },{ .name = "EFUSE_PD", .addr = A_EFUSE_PD, + .ro = 0xfffffffe, + },{ .name = "EFUSE_ANLG_OSC_SW_1LP", .addr = A_EFUSE_ANLG_OSC_SW_1LP, + },{ .name = "EFUSE_TEST_CTRL", .addr = A_EFUSE_TEST_CTRL, + .reset = 0x8, + } +}; + +static void efuse_ctrl_reg_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + XlnxVersalEFuseCtrl *s; + Object *dev; + + assert(reg_array != NULL); + + dev = reg_array->mem.owner; + assert(dev); + + s = XLNX_VERSAL_EFUSE_CTRL(dev); + + if (addr != A_WR_LOCK && s->regs[R_WR_LOCK]) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s[reg_0x%02lx]: Attempt to write locked register.\n", + object_get_canonical_path(OBJECT(s)), (long)addr); + } else { + register_write_memory(opaque, addr, data, size); + } +} + +static void efuse_ctrl_register_reset(RegisterInfo *reg) +{ + if (!reg->data || !reg->access) { + return; + } + + /* Reset must not trigger some registers' writers */ + switch (reg->access->addr) { + case A_EFUSE_AES_CRC: + case A_EFUSE_AES_USR_KEY0_CRC: + case A_EFUSE_AES_USR_KEY1_CRC: + *(uint32_t *)reg->data = reg->access->reset; + return; + } + + register_reset(reg); +} + +static void efuse_ctrl_reset(DeviceState *dev) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + efuse_ctrl_register_reset(&s->regs_info[i]); + } + + efuse_anchor_bits_check(s); + efuse_data_sync(s); + efuse_imr_update_irq(s); +} + +static const MemoryRegionOps efuse_ctrl_ops = { + .read = register_read_memory, + .write = efuse_ctrl_reg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void efuse_ctrl_realize(DeviceState *dev, Error **errp) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(dev); + const uint32_t lks_sz = sizeof(XlnxEFuseLkSpec) / 2; + + if (!s->efuse) { + error_setg(errp, "%s.efuse: link property not connected to XLNX-EFUSE", + object_get_canonical_path(OBJECT(dev))); + return; + } + + /* Sort property-defined pgm-locks for bsearch lookup */ + if ((s->extra_pg0_lock_n16 % lks_sz) != 0) { + error_setg(errp, + "%s.pg0-lock: array property item-count not multiple of %u", + object_get_canonical_path(OBJECT(dev)), lks_sz); + return; + } + + efuse_lk_spec_sort(s); +} + +static void efuse_ctrl_init(Object *obj) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + reg_array = + register_init_block32(DEVICE(obj), efuse_ctrl_regs_info, + ARRAY_SIZE(efuse_ctrl_regs_info), + s->regs_info, s->regs, + &efuse_ctrl_ops, + XLNX_VERSAL_EFUSE_CTRL_ERR_DEBUG, + R_MAX * 4); + + sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_irq(sbd, &s->irq_efuse_imr); +} + +static const VMStateDescription vmstate_efuse_ctrl = { + .name = TYPE_XLNX_VERSAL_EFUSE_CTRL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxVersalEFuseCtrl, R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static Property efuse_ctrl_props[] = { + DEFINE_PROP_LINK("efuse", + XlnxVersalEFuseCtrl, efuse, + TYPE_XLNX_EFUSE, XlnxEFuse *), + DEFINE_PROP_ARRAY("pg0-lock", + XlnxVersalEFuseCtrl, extra_pg0_lock_n16, + extra_pg0_lock_spec, qdev_prop_uint16, uint16_t), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void efuse_ctrl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = efuse_ctrl_reset; + dc->realize = efuse_ctrl_realize; + dc->vmsd = &vmstate_efuse_ctrl; + device_class_set_props(dc, efuse_ctrl_props); +} + +static const TypeInfo efuse_ctrl_info = { + .name = TYPE_XLNX_VERSAL_EFUSE_CTRL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalEFuseCtrl), + .class_init = efuse_ctrl_class_init, + .instance_init = efuse_ctrl_init, +}; + +static void efuse_ctrl_register_types(void) +{ + type_register_static(&efuse_ctrl_info); +} + +type_init(efuse_ctrl_register_types) + +/* + * Retrieve a row, with unreadable bits returned as 0. + */ +uint32_t xlnx_versal_efuse_read_row(XlnxEFuse *efuse, + uint32_t bit, bool *denied) +{ + bool dummy; + + if (!denied) { + denied = &dummy; + } + + if (bit >= EFUSE_RD_BLOCKED_START && bit <= EFUSE_RD_BLOCKED_END) { + *denied = true; + return 0; + } + + *denied = false; + return xlnx_efuse_get_row(efuse, bit); +} diff --git a/include/hw/nvram/xlnx-versal-efuse.h b/include/hw/nvram/xlnx-versal-efuse.h new file mode 100644 index 0000000000..a873dc5cb0 --- /dev/null +++ b/include/hw/nvram/xlnx-versal-efuse.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 Xilinx Inc. + * + * 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. + */ +#ifndef XLNX_VERSAL_EFUSE_H +#define XLNX_VERSAL_EFUSE_H + +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/register.h" +#include "hw/nvram/xlnx-efuse.h" + +#define XLNX_VERSAL_EFUSE_CTRL_R_MAX ((0x100 / 4) + 1) + +#define TYPE_XLNX_VERSAL_EFUSE_CTRL "xlnx,versal-efuse" +#define TYPE_XLNX_VERSAL_EFUSE_CACHE "xlnx,pmc-efuse-cache" + +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalEFuseCtrl, XLNX_VERSAL_EFUSE_CTRL); +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalEFuseCache, XLNX_VERSAL_EFUSE_CACHE); + +struct XlnxVersalEFuseCtrl { + SysBusDevice parent_obj; + qemu_irq irq_efuse_imr; + + XlnxEFuse *efuse; + + void *extra_pg0_lock_spec; /* Opaque property */ + uint32_t extra_pg0_lock_n16; + + uint32_t regs[XLNX_VERSAL_EFUSE_CTRL_R_MAX]; + RegisterInfo regs_info[XLNX_VERSAL_EFUSE_CTRL_R_MAX]; +}; + +struct XlnxVersalEFuseCache { + SysBusDevice parent_obj; + MemoryRegion iomem; + + XlnxEFuse *efuse; +}; + +/** + * xlnx_versal_efuse_read_row: + * @s: the efuse object + * @bit: the bit-address within the 32-bit row to be read + * @denied: if non-NULL, to receive true if the row is write-only + * + * Returns: the 32-bit word containing address @bit; 0 if @denies is true + */ +uint32_t xlnx_versal_efuse_read_row(XlnxEFuse *s, uint32_t bit, bool *denied); + +#endif From 67fa02f89fbf7510b70080bbbea8ac0aa752e8ba Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:23:54 -0700 Subject: [PATCH 0088/1334] hw/nvram: Introduce Xilinx ZynqMP eFuse device This implements the Xilinx ZynqMP eFuse, an one-time field-programmable non-volatile storage device. There is only one such device in the Xilinx ZynqMP product family. Co-authored-by: Edgar E. Iglesias Co-authored-by: Sai Pavan Boddu Signed-off-by: Edgar E. Iglesias Signed-off-by: Sai Pavan Boddu Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-4-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/nvram/Kconfig | 4 + hw/nvram/meson.build | 2 + hw/nvram/xlnx-zynqmp-efuse.c | 855 +++++++++++++++++++++++++++ include/hw/nvram/xlnx-zynqmp-efuse.h | 44 ++ 4 files changed, 905 insertions(+) create mode 100644 hw/nvram/xlnx-zynqmp-efuse.c create mode 100644 include/hw/nvram/xlnx-zynqmp-efuse.h diff --git a/hw/nvram/Kconfig b/hw/nvram/Kconfig index 6c2bb5afe3..3059c5dae0 100644 --- a/hw/nvram/Kconfig +++ b/hw/nvram/Kconfig @@ -26,3 +26,7 @@ config XLNX_EFUSE config XLNX_EFUSE_VERSAL bool select XLNX_EFUSE + +config XLNX_EFUSE_ZYNQMP + bool + select XLNX_EFUSE diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index 62352ad8ec..6dc54d9873 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -14,5 +14,7 @@ softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE', if_true: files('xlnx-efuse.c')) softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_VERSAL', if_true: files( 'xlnx-versal-efuse-cache.c', 'xlnx-versal-efuse-ctrl.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_ZYNQMP', if_true: files( + 'xlnx-zynqmp-efuse.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c')) diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c new file mode 100644 index 0000000000..1f87dbf988 --- /dev/null +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -0,0 +1,855 @@ +/* + * QEMU model of the ZynqMP eFuse + * + * Copyright (c) 2015 Xilinx Inc. + * + * Written by Edgar E. Iglesias + * + * 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 "hw/nvram/xlnx-zynqmp-efuse.h" + +#include "qemu/log.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" + +#ifndef ZYNQMP_EFUSE_ERR_DEBUG +#define ZYNQMP_EFUSE_ERR_DEBUG 0 +#endif + +REG32(WR_LOCK, 0x0) + FIELD(WR_LOCK, LOCK, 0, 16) +REG32(CFG, 0x4) + FIELD(CFG, SLVERR_ENABLE, 5, 1) + FIELD(CFG, MARGIN_RD, 2, 2) + FIELD(CFG, PGM_EN, 1, 1) + FIELD(CFG, EFUSE_CLK_SEL, 0, 1) +REG32(STATUS, 0x8) + FIELD(STATUS, AES_CRC_PASS, 7, 1) + FIELD(STATUS, AES_CRC_DONE, 6, 1) + FIELD(STATUS, CACHE_DONE, 5, 1) + FIELD(STATUS, CACHE_LOAD, 4, 1) + FIELD(STATUS, EFUSE_3_TBIT, 2, 1) + FIELD(STATUS, EFUSE_2_TBIT, 1, 1) + FIELD(STATUS, EFUSE_0_TBIT, 0, 1) +REG32(EFUSE_PGM_ADDR, 0xc) + FIELD(EFUSE_PGM_ADDR, EFUSE, 11, 2) + FIELD(EFUSE_PGM_ADDR, ROW, 5, 6) + FIELD(EFUSE_PGM_ADDR, COLUMN, 0, 5) +REG32(EFUSE_RD_ADDR, 0x10) + FIELD(EFUSE_RD_ADDR, EFUSE, 11, 2) + FIELD(EFUSE_RD_ADDR, ROW, 5, 6) +REG32(EFUSE_RD_DATA, 0x14) +REG32(TPGM, 0x18) + FIELD(TPGM, VALUE, 0, 16) +REG32(TRD, 0x1c) + FIELD(TRD, VALUE, 0, 8) +REG32(TSU_H_PS, 0x20) + FIELD(TSU_H_PS, VALUE, 0, 8) +REG32(TSU_H_PS_CS, 0x24) + FIELD(TSU_H_PS_CS, VALUE, 0, 8) +REG32(TSU_H_CS, 0x2c) + FIELD(TSU_H_CS, VALUE, 0, 4) +REG32(EFUSE_ISR, 0x30) + FIELD(EFUSE_ISR, APB_SLVERR, 31, 1) + FIELD(EFUSE_ISR, CACHE_ERROR, 4, 1) + FIELD(EFUSE_ISR, RD_ERROR, 3, 1) + FIELD(EFUSE_ISR, RD_DONE, 2, 1) + FIELD(EFUSE_ISR, PGM_ERROR, 1, 1) + FIELD(EFUSE_ISR, PGM_DONE, 0, 1) +REG32(EFUSE_IMR, 0x34) + FIELD(EFUSE_IMR, APB_SLVERR, 31, 1) + FIELD(EFUSE_IMR, CACHE_ERROR, 4, 1) + FIELD(EFUSE_IMR, RD_ERROR, 3, 1) + FIELD(EFUSE_IMR, RD_DONE, 2, 1) + FIELD(EFUSE_IMR, PGM_ERROR, 1, 1) + FIELD(EFUSE_IMR, PGM_DONE, 0, 1) +REG32(EFUSE_IER, 0x38) + FIELD(EFUSE_IER, APB_SLVERR, 31, 1) + FIELD(EFUSE_IER, CACHE_ERROR, 4, 1) + FIELD(EFUSE_IER, RD_ERROR, 3, 1) + FIELD(EFUSE_IER, RD_DONE, 2, 1) + FIELD(EFUSE_IER, PGM_ERROR, 1, 1) + FIELD(EFUSE_IER, PGM_DONE, 0, 1) +REG32(EFUSE_IDR, 0x3c) + FIELD(EFUSE_IDR, APB_SLVERR, 31, 1) + FIELD(EFUSE_IDR, CACHE_ERROR, 4, 1) + FIELD(EFUSE_IDR, RD_ERROR, 3, 1) + FIELD(EFUSE_IDR, RD_DONE, 2, 1) + FIELD(EFUSE_IDR, PGM_ERROR, 1, 1) + FIELD(EFUSE_IDR, PGM_DONE, 0, 1) +REG32(EFUSE_CACHE_LOAD, 0x40) + FIELD(EFUSE_CACHE_LOAD, LOAD, 0, 1) +REG32(EFUSE_PGM_LOCK, 0x44) + FIELD(EFUSE_PGM_LOCK, SPK_ID_LOCK, 0, 1) +REG32(EFUSE_AES_CRC, 0x48) +REG32(EFUSE_TBITS_PRGRMG_EN, 0x100) + FIELD(EFUSE_TBITS_PRGRMG_EN, TBITS_PRGRMG_EN, 3, 1) +REG32(DNA_0, 0x100c) +REG32(DNA_1, 0x1010) +REG32(DNA_2, 0x1014) +REG32(IPDISABLE, 0x1018) + FIELD(IPDISABLE, VCU_DIS, 8, 1) + FIELD(IPDISABLE, GPU_DIS, 5, 1) + FIELD(IPDISABLE, APU3_DIS, 3, 1) + FIELD(IPDISABLE, APU2_DIS, 2, 1) + FIELD(IPDISABLE, APU1_DIS, 1, 1) + FIELD(IPDISABLE, APU0_DIS, 0, 1) +REG32(SYSOSC_CTRL, 0x101c) + FIELD(SYSOSC_CTRL, SYSOSC_EN, 0, 1) +REG32(USER_0, 0x1020) +REG32(USER_1, 0x1024) +REG32(USER_2, 0x1028) +REG32(USER_3, 0x102c) +REG32(USER_4, 0x1030) +REG32(USER_5, 0x1034) +REG32(USER_6, 0x1038) +REG32(USER_7, 0x103c) +REG32(MISC_USER_CTRL, 0x1040) + FIELD(MISC_USER_CTRL, FPD_SC_EN_0, 14, 1) + FIELD(MISC_USER_CTRL, LPD_SC_EN_0, 11, 1) + FIELD(MISC_USER_CTRL, LBIST_EN, 10, 1) + FIELD(MISC_USER_CTRL, USR_WRLK_7, 7, 1) + FIELD(MISC_USER_CTRL, USR_WRLK_6, 6, 1) + FIELD(MISC_USER_CTRL, USR_WRLK_5, 5, 1) + FIELD(MISC_USER_CTRL, USR_WRLK_4, 4, 1) + FIELD(MISC_USER_CTRL, USR_WRLK_3, 3, 1) + FIELD(MISC_USER_CTRL, USR_WRLK_2, 2, 1) + FIELD(MISC_USER_CTRL, USR_WRLK_1, 1, 1) + FIELD(MISC_USER_CTRL, USR_WRLK_0, 0, 1) +REG32(ROM_RSVD, 0x1044) + FIELD(ROM_RSVD, PBR_BOOT_ERROR, 0, 3) +REG32(PUF_CHASH, 0x1050) +REG32(PUF_MISC, 0x1054) + FIELD(PUF_MISC, REGISTER_DIS, 31, 1) + FIELD(PUF_MISC, SYN_WRLK, 30, 1) + FIELD(PUF_MISC, SYN_INVLD, 29, 1) + FIELD(PUF_MISC, TEST2_DIS, 28, 1) + FIELD(PUF_MISC, UNUSED27, 27, 1) + FIELD(PUF_MISC, UNUSED26, 26, 1) + FIELD(PUF_MISC, UNUSED25, 25, 1) + FIELD(PUF_MISC, UNUSED24, 24, 1) + FIELD(PUF_MISC, AUX, 0, 24) +REG32(SEC_CTRL, 0x1058) + FIELD(SEC_CTRL, PPK1_INVLD, 30, 2) + FIELD(SEC_CTRL, PPK1_WRLK, 29, 1) + FIELD(SEC_CTRL, PPK0_INVLD, 27, 2) + FIELD(SEC_CTRL, PPK0_WRLK, 26, 1) + FIELD(SEC_CTRL, RSA_EN, 11, 15) + FIELD(SEC_CTRL, SEC_LOCK, 10, 1) + FIELD(SEC_CTRL, PROG_GATE_2, 9, 1) + FIELD(SEC_CTRL, PROG_GATE_1, 8, 1) + FIELD(SEC_CTRL, PROG_GATE_0, 7, 1) + FIELD(SEC_CTRL, DFT_DIS, 6, 1) + FIELD(SEC_CTRL, JTAG_DIS, 5, 1) + FIELD(SEC_CTRL, ERROR_DIS, 4, 1) + FIELD(SEC_CTRL, BBRAM_DIS, 3, 1) + FIELD(SEC_CTRL, ENC_ONLY, 2, 1) + FIELD(SEC_CTRL, AES_WRLK, 1, 1) + FIELD(SEC_CTRL, AES_RDLK, 0, 1) +REG32(SPK_ID, 0x105c) +REG32(PPK0_0, 0x10a0) +REG32(PPK0_1, 0x10a4) +REG32(PPK0_2, 0x10a8) +REG32(PPK0_3, 0x10ac) +REG32(PPK0_4, 0x10b0) +REG32(PPK0_5, 0x10b4) +REG32(PPK0_6, 0x10b8) +REG32(PPK0_7, 0x10bc) +REG32(PPK0_8, 0x10c0) +REG32(PPK0_9, 0x10c4) +REG32(PPK0_10, 0x10c8) +REG32(PPK0_11, 0x10cc) +REG32(PPK1_0, 0x10d0) +REG32(PPK1_1, 0x10d4) +REG32(PPK1_2, 0x10d8) +REG32(PPK1_3, 0x10dc) +REG32(PPK1_4, 0x10e0) +REG32(PPK1_5, 0x10e4) +REG32(PPK1_6, 0x10e8) +REG32(PPK1_7, 0x10ec) +REG32(PPK1_8, 0x10f0) +REG32(PPK1_9, 0x10f4) +REG32(PPK1_10, 0x10f8) +REG32(PPK1_11, 0x10fc) + +#define BIT_POS(ROW, COLUMN) (ROW * 32 + COLUMN) +#define R_MAX (R_PPK1_11 + 1) + +/* #define EFUSE_XOSC 26 */ + +/* + * eFUSE layout references: + * ZynqMP: UG1085 (v2.1) August 21, 2019, p.277, Table 12-13 + */ +#define EFUSE_AES_RDLK BIT_POS(22, 0) +#define EFUSE_AES_WRLK BIT_POS(22, 1) +#define EFUSE_ENC_ONLY BIT_POS(22, 2) +#define EFUSE_BBRAM_DIS BIT_POS(22, 3) +#define EFUSE_ERROR_DIS BIT_POS(22, 4) +#define EFUSE_JTAG_DIS BIT_POS(22, 5) +#define EFUSE_DFT_DIS BIT_POS(22, 6) +#define EFUSE_PROG_GATE_0 BIT_POS(22, 7) +#define EFUSE_PROG_GATE_1 BIT_POS(22, 7) +#define EFUSE_PROG_GATE_2 BIT_POS(22, 9) +#define EFUSE_SEC_LOCK BIT_POS(22, 10) +#define EFUSE_RSA_EN BIT_POS(22, 11) +#define EFUSE_RSA_EN14 BIT_POS(22, 25) +#define EFUSE_PPK0_WRLK BIT_POS(22, 26) +#define EFUSE_PPK0_INVLD BIT_POS(22, 27) +#define EFUSE_PPK0_INVLD_1 BIT_POS(22, 28) +#define EFUSE_PPK1_WRLK BIT_POS(22, 29) +#define EFUSE_PPK1_INVLD BIT_POS(22, 30) +#define EFUSE_PPK1_INVLD_1 BIT_POS(22, 31) + +/* Areas. */ +#define EFUSE_TRIM_START BIT_POS(1, 0) +#define EFUSE_TRIM_END BIT_POS(1, 30) +#define EFUSE_DNA_START BIT_POS(3, 0) +#define EFUSE_DNA_END BIT_POS(5, 31) +#define EFUSE_AES_START BIT_POS(24, 0) +#define EFUSE_AES_END BIT_POS(31, 31) +#define EFUSE_ROM_START BIT_POS(17, 0) +#define EFUSE_ROM_END BIT_POS(17, 31) +#define EFUSE_IPDIS_START BIT_POS(6, 0) +#define EFUSE_IPDIS_END BIT_POS(6, 31) +#define EFUSE_USER_START BIT_POS(8, 0) +#define EFUSE_USER_END BIT_POS(15, 31) +#define EFUSE_BISR_START BIT_POS(32, 0) +#define EFUSE_BISR_END BIT_POS(39, 31) + +#define EFUSE_USER_CTRL_START BIT_POS(16, 0) +#define EFUSE_USER_CTRL_END BIT_POS(16, 16) +#define EFUSE_USER_CTRL_MASK ((uint32_t)MAKE_64BIT_MASK(0, 17)) + +#define EFUSE_PUF_CHASH_START BIT_POS(20, 0) +#define EFUSE_PUF_CHASH_END BIT_POS(20, 31) +#define EFUSE_PUF_MISC_START BIT_POS(21, 0) +#define EFUSE_PUF_MISC_END BIT_POS(21, 31) +#define EFUSE_PUF_SYN_WRLK BIT_POS(21, 30) + +#define EFUSE_SPK_START BIT_POS(23, 0) +#define EFUSE_SPK_END BIT_POS(23, 31) + +#define EFUSE_PPK0_START BIT_POS(40, 0) +#define EFUSE_PPK0_END BIT_POS(51, 31) +#define EFUSE_PPK1_START BIT_POS(52, 0) +#define EFUSE_PPK1_END BIT_POS(63, 31) + +#define EFUSE_CACHE_FLD(s, reg, field) \ + ARRAY_FIELD_DP32((s)->regs, reg, field, \ + (xlnx_efuse_get_row((s->efuse), EFUSE_ ## field) \ + >> (EFUSE_ ## field % 32))) + +#define EFUSE_CACHE_BIT(s, reg, field) \ + ARRAY_FIELD_DP32((s)->regs, reg, field, xlnx_efuse_get_bit((s->efuse), \ + EFUSE_ ## field)) + +#define FBIT_UNKNOWN (~0) + +QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxZynqMPEFuse *)0)->regs)); + +static void update_tbit_status(XlnxZynqMPEFuse *s) +{ + unsigned int check = xlnx_efuse_tbits_check(s->efuse); + uint32_t val = s->regs[R_STATUS]; + + val = FIELD_DP32(val, STATUS, EFUSE_0_TBIT, !!(check & (1 << 0))); + val = FIELD_DP32(val, STATUS, EFUSE_2_TBIT, !!(check & (1 << 1))); + val = FIELD_DP32(val, STATUS, EFUSE_3_TBIT, !!(check & (1 << 2))); + + s->regs[R_STATUS] = val; +} + +/* Update the u32 array from efuse bits. Slow but simple approach. */ +static void cache_sync_u32(XlnxZynqMPEFuse *s, unsigned int r_start, + unsigned int f_start, unsigned int f_end, + unsigned int f_written) +{ + uint32_t *u32 = &s->regs[r_start]; + unsigned int fbit, wbits = 0, u32_off = 0; + + /* Avoid working on bits that are not relevant. */ + if (f_written != FBIT_UNKNOWN + && (f_written < f_start || f_written > f_end)) { + return; + } + + for (fbit = f_start; fbit <= f_end; fbit++, wbits++) { + if (wbits == 32) { + /* Update the key offset. */ + u32_off += 1; + wbits = 0; + } + u32[u32_off] |= xlnx_efuse_get_bit(s->efuse, fbit) << wbits; + } +} + +/* + * Keep the syncs in bit order so we can bail out for the + * slower ones. + */ +static void zynqmp_efuse_sync_cache(XlnxZynqMPEFuse *s, unsigned int bit) +{ + EFUSE_CACHE_BIT(s, SEC_CTRL, AES_RDLK); + EFUSE_CACHE_BIT(s, SEC_CTRL, AES_WRLK); + EFUSE_CACHE_BIT(s, SEC_CTRL, ENC_ONLY); + EFUSE_CACHE_BIT(s, SEC_CTRL, BBRAM_DIS); + EFUSE_CACHE_BIT(s, SEC_CTRL, ERROR_DIS); + EFUSE_CACHE_BIT(s, SEC_CTRL, JTAG_DIS); + EFUSE_CACHE_BIT(s, SEC_CTRL, DFT_DIS); + EFUSE_CACHE_BIT(s, SEC_CTRL, PROG_GATE_0); + EFUSE_CACHE_BIT(s, SEC_CTRL, PROG_GATE_1); + EFUSE_CACHE_BIT(s, SEC_CTRL, PROG_GATE_2); + EFUSE_CACHE_BIT(s, SEC_CTRL, SEC_LOCK); + EFUSE_CACHE_BIT(s, SEC_CTRL, PPK0_WRLK); + EFUSE_CACHE_BIT(s, SEC_CTRL, PPK1_WRLK); + + EFUSE_CACHE_FLD(s, SEC_CTRL, RSA_EN); + EFUSE_CACHE_FLD(s, SEC_CTRL, PPK0_INVLD); + EFUSE_CACHE_FLD(s, SEC_CTRL, PPK1_INVLD); + + /* Update the tbits. */ + update_tbit_status(s); + + /* Sync the various areas. */ + s->regs[R_MISC_USER_CTRL] = xlnx_efuse_get_row(s->efuse, + EFUSE_USER_CTRL_START) + & EFUSE_USER_CTRL_MASK; + s->regs[R_PUF_CHASH] = xlnx_efuse_get_row(s->efuse, EFUSE_PUF_CHASH_START); + s->regs[R_PUF_MISC] = xlnx_efuse_get_row(s->efuse, EFUSE_PUF_MISC_START); + + cache_sync_u32(s, R_DNA_0, EFUSE_DNA_START, EFUSE_DNA_END, bit); + + if (bit < EFUSE_AES_START) { + return; + } + + cache_sync_u32(s, R_ROM_RSVD, EFUSE_ROM_START, EFUSE_ROM_END, bit); + cache_sync_u32(s, R_IPDISABLE, EFUSE_IPDIS_START, EFUSE_IPDIS_END, bit); + cache_sync_u32(s, R_USER_0, EFUSE_USER_START, EFUSE_USER_END, bit); + cache_sync_u32(s, R_SPK_ID, EFUSE_SPK_START, EFUSE_SPK_END, bit); + cache_sync_u32(s, R_PPK0_0, EFUSE_PPK0_START, EFUSE_PPK0_END, bit); + cache_sync_u32(s, R_PPK1_0, EFUSE_PPK1_START, EFUSE_PPK1_END, bit); +} + +static void zynqmp_efuse_update_irq(XlnxZynqMPEFuse *s) +{ + bool pending = s->regs[R_EFUSE_ISR] & s->regs[R_EFUSE_IMR]; + qemu_set_irq(s->irq, pending); +} + +static void zynqmp_efuse_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(reg->opaque); + zynqmp_efuse_update_irq(s); +} + +static uint64_t zynqmp_efuse_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(reg->opaque); + uint32_t val = val64; + + s->regs[R_EFUSE_IMR] |= val; + zynqmp_efuse_update_irq(s); + return 0; +} + +static uint64_t zynqmp_efuse_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(reg->opaque); + uint32_t val = val64; + + s->regs[R_EFUSE_IMR] &= ~val; + zynqmp_efuse_update_irq(s); + return 0; +} + +static void zynqmp_efuse_pgm_addr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(reg->opaque); + unsigned bit = val64; + unsigned page = FIELD_EX32(bit, EFUSE_PGM_ADDR, EFUSE); + bool puf_prot = false; + const char *errmsg = NULL; + + /* Allow only valid array, and adjust for skipped array 1 */ + switch (page) { + case 0: + break; + case 2 ... 3: + bit = FIELD_DP32(bit, EFUSE_PGM_ADDR, EFUSE, page - 1); + puf_prot = xlnx_efuse_get_bit(s->efuse, EFUSE_PUF_SYN_WRLK); + break; + default: + errmsg = "Invalid address"; + goto pgm_done; + } + + if (ARRAY_FIELD_EX32(s->regs, WR_LOCK, LOCK)) { + errmsg = "Array write-locked"; + goto pgm_done; + } + + if (!ARRAY_FIELD_EX32(s->regs, CFG, PGM_EN)) { + errmsg = "Array pgm-disabled"; + goto pgm_done; + } + + if (puf_prot) { + errmsg = "PUF_HD-store write-locked"; + goto pgm_done; + } + + if (ARRAY_FIELD_EX32(s->regs, SEC_CTRL, AES_WRLK) + && bit >= EFUSE_AES_START && bit <= EFUSE_AES_END) { + errmsg = "AES key-store Write-locked"; + goto pgm_done; + } + + if (!xlnx_efuse_set_bit(s->efuse, bit)) { + errmsg = "Write failed"; + } + + pgm_done: + if (!errmsg) { + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_ERROR, 0); + } else { + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_ERROR, 1); + qemu_log_mask(LOG_GUEST_ERROR, + "%s - eFuse write error: %s; addr=0x%x\n", + object_get_canonical_path(OBJECT(s)), + errmsg, (unsigned)val64); + } + + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_DONE, 1); + zynqmp_efuse_update_irq(s); +} + +static void zynqmp_efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(reg->opaque); + + /* + * Grant reads only to allowed bits; reference sources: + * 1/ XilSKey - XilSKey_ZynqMp_EfusePs_ReadRow() + * 2/ UG1085, v2.0, table 12-13 + * (note: enumerates the masks as per described in + * references to avoid mental translation). + */ +#define COL_MASK(L_, H_) \ + ((uint32_t)MAKE_64BIT_MASK((L_), (1 + (H_) - (L_)))) + + static const uint32_t ary0_col_mask[] = { + /* XilSKey - XSK_ZYNQMP_EFUSEPS_TBITS_ROW */ + [0] = COL_MASK(28, 31), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_USR{0:7}_FUSE_ROW */ + [8] = COL_MASK(0, 31), [9] = COL_MASK(0, 31), + [10] = COL_MASK(0, 31), [11] = COL_MASK(0, 31), + [12] = COL_MASK(0, 31), [13] = COL_MASK(0, 31), + [14] = COL_MASK(0, 31), [15] = COL_MASK(0, 31), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_MISC_USR_CTRL_ROW */ + [16] = COL_MASK(0, 7) | COL_MASK(10, 16), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_PBR_BOOT_ERR_ROW */ + [17] = COL_MASK(0, 2), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_PUF_CHASH_ROW */ + [20] = COL_MASK(0, 31), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_PUF_AUX_ROW */ + [21] = COL_MASK(0, 23) | COL_MASK(29, 31), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_SEC_CTRL_ROW */ + [22] = COL_MASK(0, 31), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_SPK_ID_ROW */ + [23] = COL_MASK(0, 31), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_PPK0_START_ROW */ + [40] = COL_MASK(0, 31), [41] = COL_MASK(0, 31), + [42] = COL_MASK(0, 31), [43] = COL_MASK(0, 31), + [44] = COL_MASK(0, 31), [45] = COL_MASK(0, 31), + [46] = COL_MASK(0, 31), [47] = COL_MASK(0, 31), + [48] = COL_MASK(0, 31), [49] = COL_MASK(0, 31), + [50] = COL_MASK(0, 31), [51] = COL_MASK(0, 31), + + /* XilSKey - XSK_ZYNQMP_EFUSEPS_PPK1_START_ROW */ + [52] = COL_MASK(0, 31), [53] = COL_MASK(0, 31), + [54] = COL_MASK(0, 31), [55] = COL_MASK(0, 31), + [56] = COL_MASK(0, 31), [57] = COL_MASK(0, 31), + [58] = COL_MASK(0, 31), [59] = COL_MASK(0, 31), + [60] = COL_MASK(0, 31), [61] = COL_MASK(0, 31), + [62] = COL_MASK(0, 31), [63] = COL_MASK(0, 31), + }; + + uint32_t col_mask = COL_MASK(0, 31); +#undef COL_MASK + + uint32_t efuse_idx = s->regs[R_EFUSE_RD_ADDR]; + uint32_t efuse_ary = FIELD_EX32(efuse_idx, EFUSE_RD_ADDR, EFUSE); + uint32_t efuse_row = FIELD_EX32(efuse_idx, EFUSE_RD_ADDR, ROW); + + switch (efuse_ary) { + case 0: /* Various */ + if (efuse_row >= ARRAY_SIZE(ary0_col_mask)) { + goto denied; + } + + col_mask = ary0_col_mask[efuse_row]; + if (!col_mask) { + goto denied; + } + break; + case 2: /* PUF helper data, adjust for skipped array 1 */ + case 3: + val64 = FIELD_DP32(efuse_idx, EFUSE_RD_ADDR, EFUSE, efuse_ary - 1); + break; + default: + goto denied; + } + + s->regs[R_EFUSE_RD_DATA] = xlnx_efuse_get_row(s->efuse, val64) & col_mask; + + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_ERROR, 0); + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_DONE, 1); + zynqmp_efuse_update_irq(s); + return; + + denied: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Denied efuse read from array %u, row %u\n", + object_get_canonical_path(OBJECT(s)), + efuse_ary, efuse_row); + + s->regs[R_EFUSE_RD_DATA] = 0; + + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_ERROR, 1); + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_DONE, 0); + zynqmp_efuse_update_irq(s); +} + +static void zynqmp_efuse_aes_crc_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(reg->opaque); + bool ok; + + ok = xlnx_efuse_k256_check(s->efuse, (uint32_t)val64, EFUSE_AES_START); + + ARRAY_FIELD_DP32(s->regs, STATUS, AES_CRC_PASS, (ok ? 1 : 0)); + ARRAY_FIELD_DP32(s->regs, STATUS, AES_CRC_DONE, 1); + + s->regs[R_EFUSE_AES_CRC] = 0; /* crc value is write-only */ +} + +static uint64_t zynqmp_efuse_cache_load_prew(RegisterInfo *reg, + uint64_t valu64) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(reg->opaque); + + if (valu64 & R_EFUSE_CACHE_LOAD_LOAD_MASK) { + zynqmp_efuse_sync_cache(s, FBIT_UNKNOWN); + ARRAY_FIELD_DP32(s->regs, STATUS, CACHE_DONE, 1); + zynqmp_efuse_update_irq(s); + } + + return 0; +} + +static uint64_t zynqmp_efuse_wr_lock_prew(RegisterInfo *reg, uint64_t val) +{ + return val == 0xDF0D ? 0 : 1; +} + +static RegisterAccessInfo zynqmp_efuse_regs_info[] = { + { .name = "WR_LOCK", .addr = A_WR_LOCK, + .reset = 0x1, + .pre_write = zynqmp_efuse_wr_lock_prew, + },{ .name = "CFG", .addr = A_CFG, + },{ .name = "STATUS", .addr = A_STATUS, + .rsvd = 0x8, + .ro = 0xff, + },{ .name = "EFUSE_PGM_ADDR", .addr = A_EFUSE_PGM_ADDR, + .post_write = zynqmp_efuse_pgm_addr_postw + },{ .name = "EFUSE_RD_ADDR", .addr = A_EFUSE_RD_ADDR, + .rsvd = 0x1f, + .post_write = zynqmp_efuse_rd_addr_postw, + },{ .name = "EFUSE_RD_DATA", .addr = A_EFUSE_RD_DATA, + .ro = 0xffffffff, + },{ .name = "TPGM", .addr = A_TPGM, + },{ .name = "TRD", .addr = A_TRD, + .reset = 0x1b, + },{ .name = "TSU_H_PS", .addr = A_TSU_H_PS, + .reset = 0xff, + },{ .name = "TSU_H_PS_CS", .addr = A_TSU_H_PS_CS, + .reset = 0xb, + },{ .name = "TSU_H_CS", .addr = A_TSU_H_CS, + .reset = 0x7, + },{ .name = "EFUSE_ISR", .addr = A_EFUSE_ISR, + .rsvd = 0x7fffffe0, + .w1c = 0x8000001f, + .post_write = zynqmp_efuse_isr_postw, + },{ .name = "EFUSE_IMR", .addr = A_EFUSE_IMR, + .reset = 0x8000001f, + .rsvd = 0x7fffffe0, + .ro = 0xffffffff, + },{ .name = "EFUSE_IER", .addr = A_EFUSE_IER, + .rsvd = 0x7fffffe0, + .pre_write = zynqmp_efuse_ier_prew, + },{ .name = "EFUSE_IDR", .addr = A_EFUSE_IDR, + .rsvd = 0x7fffffe0, + .pre_write = zynqmp_efuse_idr_prew, + },{ .name = "EFUSE_CACHE_LOAD", .addr = A_EFUSE_CACHE_LOAD, + .pre_write = zynqmp_efuse_cache_load_prew, + },{ .name = "EFUSE_PGM_LOCK", .addr = A_EFUSE_PGM_LOCK, + },{ .name = "EFUSE_AES_CRC", .addr = A_EFUSE_AES_CRC, + .post_write = zynqmp_efuse_aes_crc_postw, + },{ .name = "EFUSE_TBITS_PRGRMG_EN", .addr = A_EFUSE_TBITS_PRGRMG_EN, + .reset = R_EFUSE_TBITS_PRGRMG_EN_TBITS_PRGRMG_EN_MASK, + },{ .name = "DNA_0", .addr = A_DNA_0, + .ro = 0xffffffff, + },{ .name = "DNA_1", .addr = A_DNA_1, + .ro = 0xffffffff, + },{ .name = "DNA_2", .addr = A_DNA_2, + .ro = 0xffffffff, + },{ .name = "IPDISABLE", .addr = A_IPDISABLE, + .ro = 0xffffffff, + },{ .name = "SYSOSC_CTRL", .addr = A_SYSOSC_CTRL, + .ro = 0xffffffff, + },{ .name = "USER_0", .addr = A_USER_0, + .ro = 0xffffffff, + },{ .name = "USER_1", .addr = A_USER_1, + .ro = 0xffffffff, + },{ .name = "USER_2", .addr = A_USER_2, + .ro = 0xffffffff, + },{ .name = "USER_3", .addr = A_USER_3, + .ro = 0xffffffff, + },{ .name = "USER_4", .addr = A_USER_4, + .ro = 0xffffffff, + },{ .name = "USER_5", .addr = A_USER_5, + .ro = 0xffffffff, + },{ .name = "USER_6", .addr = A_USER_6, + .ro = 0xffffffff, + },{ .name = "USER_7", .addr = A_USER_7, + .ro = 0xffffffff, + },{ .name = "MISC_USER_CTRL", .addr = A_MISC_USER_CTRL, + .ro = 0xffffffff, + },{ .name = "ROM_RSVD", .addr = A_ROM_RSVD, + .ro = 0xffffffff, + },{ .name = "PUF_CHASH", .addr = A_PUF_CHASH, + .ro = 0xffffffff, + },{ .name = "PUF_MISC", .addr = A_PUF_MISC, + .ro = 0xffffffff, + },{ .name = "SEC_CTRL", .addr = A_SEC_CTRL, + .ro = 0xffffffff, + },{ .name = "SPK_ID", .addr = A_SPK_ID, + .ro = 0xffffffff, + },{ .name = "PPK0_0", .addr = A_PPK0_0, + .ro = 0xffffffff, + },{ .name = "PPK0_1", .addr = A_PPK0_1, + .ro = 0xffffffff, + },{ .name = "PPK0_2", .addr = A_PPK0_2, + .ro = 0xffffffff, + },{ .name = "PPK0_3", .addr = A_PPK0_3, + .ro = 0xffffffff, + },{ .name = "PPK0_4", .addr = A_PPK0_4, + .ro = 0xffffffff, + },{ .name = "PPK0_5", .addr = A_PPK0_5, + .ro = 0xffffffff, + },{ .name = "PPK0_6", .addr = A_PPK0_6, + .ro = 0xffffffff, + },{ .name = "PPK0_7", .addr = A_PPK0_7, + .ro = 0xffffffff, + },{ .name = "PPK0_8", .addr = A_PPK0_8, + .ro = 0xffffffff, + },{ .name = "PPK0_9", .addr = A_PPK0_9, + .ro = 0xffffffff, + },{ .name = "PPK0_10", .addr = A_PPK0_10, + .ro = 0xffffffff, + },{ .name = "PPK0_11", .addr = A_PPK0_11, + .ro = 0xffffffff, + },{ .name = "PPK1_0", .addr = A_PPK1_0, + .ro = 0xffffffff, + },{ .name = "PPK1_1", .addr = A_PPK1_1, + .ro = 0xffffffff, + },{ .name = "PPK1_2", .addr = A_PPK1_2, + .ro = 0xffffffff, + },{ .name = "PPK1_3", .addr = A_PPK1_3, + .ro = 0xffffffff, + },{ .name = "PPK1_4", .addr = A_PPK1_4, + .ro = 0xffffffff, + },{ .name = "PPK1_5", .addr = A_PPK1_5, + .ro = 0xffffffff, + },{ .name = "PPK1_6", .addr = A_PPK1_6, + .ro = 0xffffffff, + },{ .name = "PPK1_7", .addr = A_PPK1_7, + .ro = 0xffffffff, + },{ .name = "PPK1_8", .addr = A_PPK1_8, + .ro = 0xffffffff, + },{ .name = "PPK1_9", .addr = A_PPK1_9, + .ro = 0xffffffff, + },{ .name = "PPK1_10", .addr = A_PPK1_10, + .ro = 0xffffffff, + },{ .name = "PPK1_11", .addr = A_PPK1_11, + .ro = 0xffffffff, + } +}; + +static void zynqmp_efuse_reg_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + XlnxZynqMPEFuse *s; + Object *dev; + + assert(reg_array != NULL); + + dev = reg_array->mem.owner; + assert(dev); + + s = XLNX_ZYNQMP_EFUSE(dev); + + if (addr != A_WR_LOCK && s->regs[R_WR_LOCK]) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s[reg_0x%02lx]: Attempt to write locked register.\n", + object_get_canonical_path(OBJECT(s)), (long)addr); + } else { + register_write_memory(opaque, addr, data, size); + } +} + +static const MemoryRegionOps zynqmp_efuse_ops = { + .read = register_read_memory, + .write = zynqmp_efuse_reg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void zynqmp_efuse_register_reset(RegisterInfo *reg) +{ + if (!reg->data || !reg->access) { + return; + } + + /* Reset must not trigger some registers' writers */ + switch (reg->access->addr) { + case A_EFUSE_AES_CRC: + *(uint32_t *)reg->data = reg->access->reset; + return; + } + + register_reset(reg); +} + +static void zynqmp_efuse_reset(DeviceState *dev) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + zynqmp_efuse_register_reset(&s->regs_info[i]); + } + + zynqmp_efuse_sync_cache(s, FBIT_UNKNOWN); + ARRAY_FIELD_DP32(s->regs, STATUS, CACHE_DONE, 1); + zynqmp_efuse_update_irq(s); +} + +static void zynqmp_efuse_realize(DeviceState *dev, Error **errp) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(dev); + + if (!s->efuse) { + error_setg(errp, "%s.efuse: link property not connected to XLNX-EFUSE", + object_get_canonical_path(OBJECT(dev))); + return; + } + + s->efuse->dev = dev; +} + +static void zynqmp_efuse_init(Object *obj) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + reg_array = + register_init_block32(DEVICE(obj), zynqmp_efuse_regs_info, + ARRAY_SIZE(zynqmp_efuse_regs_info), + s->regs_info, s->regs, + &zynqmp_efuse_ops, + ZYNQMP_EFUSE_ERR_DEBUG, + R_MAX * 4); + + sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_irq(sbd, &s->irq); +} + +static const VMStateDescription vmstate_efuse = { + .name = TYPE_XLNX_ZYNQMP_EFUSE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPEFuse, R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static Property zynqmp_efuse_props[] = { + DEFINE_PROP_LINK("efuse", + XlnxZynqMPEFuse, efuse, + TYPE_XLNX_EFUSE, XlnxEFuse *), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void zynqmp_efuse_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = zynqmp_efuse_reset; + dc->realize = zynqmp_efuse_realize; + dc->vmsd = &vmstate_efuse; + device_class_set_props(dc, zynqmp_efuse_props); +} + + +static const TypeInfo efuse_info = { + .name = TYPE_XLNX_ZYNQMP_EFUSE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZynqMPEFuse), + .class_init = zynqmp_efuse_class_init, + .instance_init = zynqmp_efuse_init, +}; + +static void efuse_register_types(void) +{ + type_register_static(&efuse_info); +} + +type_init(efuse_register_types) diff --git a/include/hw/nvram/xlnx-zynqmp-efuse.h b/include/hw/nvram/xlnx-zynqmp-efuse.h new file mode 100644 index 0000000000..6b051ec4f1 --- /dev/null +++ b/include/hw/nvram/xlnx-zynqmp-efuse.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Xilinx Inc. + * + * 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. + */ +#ifndef XLNX_ZYNQMP_EFUSE_H +#define XLNX_ZYNQMP_EFUSE_H + +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/register.h" +#include "hw/nvram/xlnx-efuse.h" + +#define XLNX_ZYNQMP_EFUSE_R_MAX ((0x10fc / 4) + 1) + +#define TYPE_XLNX_ZYNQMP_EFUSE "xlnx,zynqmp-efuse" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPEFuse, XLNX_ZYNQMP_EFUSE); + +struct XlnxZynqMPEFuse { + SysBusDevice parent_obj; + qemu_irq irq; + + XlnxEFuse *efuse; + uint32_t regs[XLNX_ZYNQMP_EFUSE_R_MAX]; + RegisterInfo regs_info[XLNX_ZYNQMP_EFUSE_R_MAX]; +}; + +#endif From 461a6a6f199944e466ddb808516e63cf064c0105 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:23:55 -0700 Subject: [PATCH 0089/1334] hw/nvram: Introduce Xilinx battery-backed ram This device is present in Versal and ZynqMP product families to store a 256-bit encryption key. Co-authored-by: Edgar E. Iglesias Co-authored-by: Sai Pavan Boddu Signed-off-by: Edgar E. Iglesias Signed-off-by: Sai Pavan Boddu Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-5-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/nvram/Kconfig | 4 + hw/nvram/meson.build | 1 + hw/nvram/xlnx-bbram.c | 545 ++++++++++++++++++++++++++++++++++ include/hw/nvram/xlnx-bbram.h | 54 ++++ 4 files changed, 604 insertions(+) create mode 100644 hw/nvram/xlnx-bbram.c create mode 100644 include/hw/nvram/xlnx-bbram.h diff --git a/hw/nvram/Kconfig b/hw/nvram/Kconfig index 3059c5dae0..24cfc18f8b 100644 --- a/hw/nvram/Kconfig +++ b/hw/nvram/Kconfig @@ -30,3 +30,7 @@ config XLNX_EFUSE_VERSAL config XLNX_EFUSE_ZYNQMP bool select XLNX_EFUSE + +config XLNX_BBRAM + bool + select XLNX_EFUSE_CRC diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index 6dc54d9873..202a5466e6 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -16,5 +16,6 @@ softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_VERSAL', if_true: files( 'xlnx-versal-efuse-ctrl.c')) softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_ZYNQMP', if_true: files( 'xlnx-zynqmp-efuse.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_BBRAM', if_true: files('xlnx-bbram.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c')) diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c new file mode 100644 index 0000000000..b70828e5bf --- /dev/null +++ b/hw/nvram/xlnx-bbram.c @@ -0,0 +1,545 @@ +/* + * QEMU model of the Xilinx BBRAM Battery Backed RAM + * + * Copyright (c) 2014-2021 Xilinx Inc. + * + * 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 "hw/nvram/xlnx-bbram.h" + +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "sysemu/blockdev.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/nvram/xlnx-efuse.h" + +#ifndef XLNX_BBRAM_ERR_DEBUG +#define XLNX_BBRAM_ERR_DEBUG 0 +#endif + +REG32(BBRAM_STATUS, 0x0) + FIELD(BBRAM_STATUS, AES_CRC_PASS, 9, 1) + FIELD(BBRAM_STATUS, AES_CRC_DONE, 8, 1) + FIELD(BBRAM_STATUS, BBRAM_ZEROIZED, 4, 1) + FIELD(BBRAM_STATUS, PGM_MODE, 0, 1) +REG32(BBRAM_CTRL, 0x4) + FIELD(BBRAM_CTRL, ZEROIZE, 0, 1) +REG32(PGM_MODE, 0x8) +REG32(BBRAM_AES_CRC, 0xc) +REG32(BBRAM_0, 0x10) +REG32(BBRAM_1, 0x14) +REG32(BBRAM_2, 0x18) +REG32(BBRAM_3, 0x1c) +REG32(BBRAM_4, 0x20) +REG32(BBRAM_5, 0x24) +REG32(BBRAM_6, 0x28) +REG32(BBRAM_7, 0x2c) +REG32(BBRAM_8, 0x30) +REG32(BBRAM_SLVERR, 0x34) + FIELD(BBRAM_SLVERR, ENABLE, 0, 1) +REG32(BBRAM_ISR, 0x38) + FIELD(BBRAM_ISR, APB_SLVERR, 0, 1) +REG32(BBRAM_IMR, 0x3c) + FIELD(BBRAM_IMR, APB_SLVERR, 0, 1) +REG32(BBRAM_IER, 0x40) + FIELD(BBRAM_IER, APB_SLVERR, 0, 1) +REG32(BBRAM_IDR, 0x44) + FIELD(BBRAM_IDR, APB_SLVERR, 0, 1) +REG32(BBRAM_MSW_LOCK, 0x4c) + FIELD(BBRAM_MSW_LOCK, VAL, 0, 1) + +#define R_MAX (R_BBRAM_MSW_LOCK + 1) + +#define RAM_MAX (A_BBRAM_8 + 4 - A_BBRAM_0) + +#define BBRAM_PGM_MAGIC 0x757bdf0d + +QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxBBRam *)0)->regs)); + +static bool bbram_msw_locked(XlnxBBRam *s) +{ + return ARRAY_FIELD_EX32(s->regs, BBRAM_MSW_LOCK, VAL) != 0; +} + +static bool bbram_pgm_enabled(XlnxBBRam *s) +{ + return ARRAY_FIELD_EX32(s->regs, BBRAM_STATUS, PGM_MODE) != 0; +} + +static void bbram_bdrv_error(XlnxBBRam *s, int rc, gchar *detail) +{ + Error *errp; + + error_setg_errno(&errp, -rc, "%s: BBRAM backstore %s failed.", + blk_name(s->blk), detail); + error_report("%s", error_get_pretty(errp)); + error_free(errp); + + g_free(detail); +} + +static void bbram_bdrv_read(XlnxBBRam *s, Error **errp) +{ + uint32_t *ram = &s->regs[R_BBRAM_0]; + int nr = RAM_MAX; + + if (!s->blk) { + return; + } + + s->blk_ro = !blk_supports_write_perm(s->blk); + if (!s->blk_ro) { + int rc; + + rc = blk_set_perm(s->blk, + (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), + BLK_PERM_ALL, NULL); + if (rc) { + s->blk_ro = true; + } + } + if (s->blk_ro) { + warn_report("%s: Skip saving updates to read-only BBRAM backstore.", + blk_name(s->blk)); + } + + if (blk_pread(s->blk, 0, ram, nr) < 0) { + error_setg(errp, + "%s: Failed to read %u bytes from BBRAM backstore.", + blk_name(s->blk), nr); + return; + } + + /* Convert from little-endian backstore for each 32-bit word */ + nr /= 4; + while (nr--) { + ram[nr] = le32_to_cpu(ram[nr]); + } +} + +static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr) +{ + uint32_t le32; + unsigned offset; + int rc; + + assert(A_BBRAM_0 <= hwaddr && hwaddr <= A_BBRAM_8); + + /* Backstore is always in little-endian */ + le32 = cpu_to_le32(s->regs[hwaddr / 4]); + + /* Update zeroized flag */ + if (le32 && (hwaddr != A_BBRAM_8 || s->bbram8_wo)) { + ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 0); + } + + if (!s->blk || s->blk_ro) { + return; + } + + offset = hwaddr - A_BBRAM_0; + rc = blk_pwrite(s->blk, offset, &le32, 4, 0); + if (rc < 0) { + bbram_bdrv_error(s, rc, g_strdup_printf("write to offset %u", offset)); + } +} + +static void bbram_bdrv_zero(XlnxBBRam *s) +{ + int rc; + + ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 1); + + if (!s->blk || s->blk_ro) { + return; + } + + rc = blk_make_zero(s->blk, 0); + if (rc < 0) { + bbram_bdrv_error(s, rc, g_strdup("zeroizing")); + } + + /* Restore bbram8 if it is non-zero */ + if (s->regs[R_BBRAM_8]) { + bbram_bdrv_sync(s, A_BBRAM_8); + } +} + +static void bbram_zeroize(XlnxBBRam *s) +{ + int nr = RAM_MAX - (s->bbram8_wo ? 0 : 4); /* only wo bbram8 is cleared */ + + memset(&s->regs[R_BBRAM_0], 0, nr); + bbram_bdrv_zero(s); +} + +static void bbram_update_irq(XlnxBBRam *s) +{ + bool pending = s->regs[R_BBRAM_ISR] & ~s->regs[R_BBRAM_IMR]; + + qemu_set_irq(s->irq_bbram, pending); +} + +static void bbram_ctrl_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + uint32_t val = val64; + + if (val & R_BBRAM_CTRL_ZEROIZE_MASK) { + bbram_zeroize(s); + /* The bit is self clearing */ + s->regs[R_BBRAM_CTRL] &= ~R_BBRAM_CTRL_ZEROIZE_MASK; + } +} + +static void bbram_pgm_mode_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + uint32_t val = val64; + + if (val == BBRAM_PGM_MAGIC) { + bbram_zeroize(s); + + /* The status bit is cleared only by POR */ + ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, PGM_MODE, 1); + } +} + +static void bbram_aes_crc_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + uint32_t calc_crc; + + if (!bbram_pgm_enabled(s)) { + /* We are not in programming mode, don't do anything */ + return; + } + + /* Perform the AES integrity check */ + s->regs[R_BBRAM_STATUS] |= R_BBRAM_STATUS_AES_CRC_DONE_MASK; + + /* + * Set check status. + * + * ZynqMP BBRAM check has a zero-u32 prepended; see: + * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_bbramps_zynqmp.c#L311 + */ + calc_crc = xlnx_efuse_calc_crc(&s->regs[R_BBRAM_0], + (R_BBRAM_8 - R_BBRAM_0), s->crc_zpads); + + ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, AES_CRC_PASS, + (s->regs[R_BBRAM_AES_CRC] == calc_crc)); +} + +static uint64_t bbram_key_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + uint32_t original_data = *(uint32_t *) reg->data; + + if (bbram_pgm_enabled(s)) { + return val64; + } else { + /* We are not in programming mode, don't do anything */ + qemu_log_mask(LOG_GUEST_ERROR, + "Not in programming mode, dropping the write\n"); + return original_data; + } +} + +static void bbram_key_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + + bbram_bdrv_sync(s, reg->access->addr); +} + +static uint64_t bbram_wo_postr(RegisterInfo *reg, uint64_t val) +{ + return 0; +} + +static uint64_t bbram_r8_postr(RegisterInfo *reg, uint64_t val) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + + return s->bbram8_wo ? bbram_wo_postr(reg, val) : val; +} + +static bool bbram_r8_readonly(XlnxBBRam *s) +{ + return !bbram_pgm_enabled(s) || bbram_msw_locked(s); +} + +static uint64_t bbram_r8_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + + if (bbram_r8_readonly(s)) { + val64 = *(uint32_t *)reg->data; + } + + return val64; +} + +static void bbram_r8_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + + if (!bbram_r8_readonly(s)) { + bbram_bdrv_sync(s, A_BBRAM_8); + } +} + +static uint64_t bbram_msw_lock_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + + /* Never lock if bbram8 is wo; and, only POR can clear the lock */ + if (s->bbram8_wo) { + val64 = 0; + } else { + val64 |= s->regs[R_BBRAM_MSW_LOCK]; + } + + return val64; +} + +static void bbram_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + + bbram_update_irq(s); +} + +static uint64_t bbram_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + uint32_t val = val64; + + s->regs[R_BBRAM_IMR] &= ~val; + bbram_update_irq(s); + return 0; +} + +static uint64_t bbram_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxBBRam *s = XLNX_BBRAM(reg->opaque); + uint32_t val = val64; + + s->regs[R_BBRAM_IMR] |= val; + bbram_update_irq(s); + return 0; +} + +static RegisterAccessInfo bbram_ctrl_regs_info[] = { + { .name = "BBRAM_STATUS", .addr = A_BBRAM_STATUS, + .rsvd = 0xee, + .ro = 0x3ff, + },{ .name = "BBRAM_CTRL", .addr = A_BBRAM_CTRL, + .post_write = bbram_ctrl_postw, + },{ .name = "PGM_MODE", .addr = A_PGM_MODE, + .post_write = bbram_pgm_mode_postw, + },{ .name = "BBRAM_AES_CRC", .addr = A_BBRAM_AES_CRC, + .post_write = bbram_aes_crc_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_0", .addr = A_BBRAM_0, + .pre_write = bbram_key_prew, + .post_write = bbram_key_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_1", .addr = A_BBRAM_1, + .pre_write = bbram_key_prew, + .post_write = bbram_key_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_2", .addr = A_BBRAM_2, + .pre_write = bbram_key_prew, + .post_write = bbram_key_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_3", .addr = A_BBRAM_3, + .pre_write = bbram_key_prew, + .post_write = bbram_key_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_4", .addr = A_BBRAM_4, + .pre_write = bbram_key_prew, + .post_write = bbram_key_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_5", .addr = A_BBRAM_5, + .pre_write = bbram_key_prew, + .post_write = bbram_key_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_6", .addr = A_BBRAM_6, + .pre_write = bbram_key_prew, + .post_write = bbram_key_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_7", .addr = A_BBRAM_7, + .pre_write = bbram_key_prew, + .post_write = bbram_key_postw, + .post_read = bbram_wo_postr, + },{ .name = "BBRAM_8", .addr = A_BBRAM_8, + .pre_write = bbram_r8_prew, + .post_write = bbram_r8_postw, + .post_read = bbram_r8_postr, + },{ .name = "BBRAM_SLVERR", .addr = A_BBRAM_SLVERR, + .rsvd = ~1, + },{ .name = "BBRAM_ISR", .addr = A_BBRAM_ISR, + .w1c = 0x1, + .post_write = bbram_isr_postw, + },{ .name = "BBRAM_IMR", .addr = A_BBRAM_IMR, + .ro = 0x1, + },{ .name = "BBRAM_IER", .addr = A_BBRAM_IER, + .pre_write = bbram_ier_prew, + },{ .name = "BBRAM_IDR", .addr = A_BBRAM_IDR, + .pre_write = bbram_idr_prew, + },{ .name = "BBRAM_MSW_LOCK", .addr = A_BBRAM_MSW_LOCK, + .pre_write = bbram_msw_lock_prew, + .ro = ~R_BBRAM_MSW_LOCK_VAL_MASK, + } +}; + +static void bbram_ctrl_reset(DeviceState *dev) +{ + XlnxBBRam *s = XLNX_BBRAM(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + if (i < R_BBRAM_0 || i > R_BBRAM_8) { + register_reset(&s->regs_info[i]); + } + } + + bbram_update_irq(s); +} + +static const MemoryRegionOps bbram_ctrl_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void bbram_ctrl_realize(DeviceState *dev, Error **errp) +{ + XlnxBBRam *s = XLNX_BBRAM(dev); + + if (s->crc_zpads) { + s->bbram8_wo = true; + } + + bbram_bdrv_read(s, errp); +} + +static void bbram_ctrl_init(Object *obj) +{ + XlnxBBRam *s = XLNX_BBRAM(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + reg_array = + register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, + ARRAY_SIZE(bbram_ctrl_regs_info), + s->regs_info, s->regs, + &bbram_ctrl_ops, + XLNX_BBRAM_ERR_DEBUG, + R_MAX * 4); + + sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_irq(sbd, &s->irq_bbram); +} + +static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + + qdev_prop_drive.set(obj, v, name, opaque, errp); + + /* Fill initial data if backend is attached after realized */ + if (dev->realized) { + bbram_bdrv_read(XLNX_BBRAM(obj), errp); + } +} + +static void bbram_prop_get_drive(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + qdev_prop_drive.get(obj, v, name, opaque, errp); +} + +static void bbram_prop_release_drive(Object *obj, const char *name, + void *opaque) +{ + qdev_prop_drive.release(obj, name, opaque); +} + +static const PropertyInfo bbram_prop_drive = { + .name = "str", + .description = "Node name or ID of a block device to use as BBRAM backend", + .realized_set_allowed = true, + .get = bbram_prop_get_drive, + .set = bbram_prop_set_drive, + .release = bbram_prop_release_drive, +}; + +static const VMStateDescription vmstate_bbram_ctrl = { + .name = TYPE_XLNX_BBRAM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxBBRam, R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static Property bbram_ctrl_props[] = { + DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *), + DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bbram_ctrl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = bbram_ctrl_reset; + dc->realize = bbram_ctrl_realize; + dc->vmsd = &vmstate_bbram_ctrl; + device_class_set_props(dc, bbram_ctrl_props); +} + +static const TypeInfo bbram_ctrl_info = { + .name = TYPE_XLNX_BBRAM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxBBRam), + .class_init = bbram_ctrl_class_init, + .instance_init = bbram_ctrl_init, +}; + +static void bbram_ctrl_register_types(void) +{ + type_register_static(&bbram_ctrl_info); +} + +type_init(bbram_ctrl_register_types) diff --git a/include/hw/nvram/xlnx-bbram.h b/include/hw/nvram/xlnx-bbram.h new file mode 100644 index 0000000000..87d59ef3c0 --- /dev/null +++ b/include/hw/nvram/xlnx-bbram.h @@ -0,0 +1,54 @@ +/* + * QEMU model of the Xilinx BBRAM Battery Backed RAM + * + * Copyright (c) 2015-2021 Xilinx Inc. + * + * Written by Edgar E. Iglesias + * + * 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. + */ +#ifndef XLNX_BBRAM_H +#define XLNX_BBRAM_H + +#include "sysemu/block-backend.h" +#include "hw/qdev-core.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/register.h" + +#define RMAX_XLNX_BBRAM ((0x4c / 4) + 1) + +#define TYPE_XLNX_BBRAM "xlnx,bbram-ctrl" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxBBRam, XLNX_BBRAM); + +struct XlnxBBRam { + SysBusDevice parent_obj; + qemu_irq irq_bbram; + + BlockBackend *blk; + + uint32_t crc_zpads; + bool bbram8_wo; + bool blk_ro; + + uint32_t regs[RMAX_XLNX_BBRAM]; + RegisterInfo regs_info[RMAX_XLNX_BBRAM]; +}; + +#endif From 393185bc9de599d82725c2a17d5db91d037745be Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:23:56 -0700 Subject: [PATCH 0090/1334] hw/arm: xlnx-versal-virt: Add Xilinx BBRAM device Connect the support for Versal Battery-Backed RAM (BBRAM) The command argument: -drive if=pflash,index=0,... Can be used to optionally connect the bbram to a backend storage, such that field-programmed values in one invocation can be made available to next invocation. The backend storage must be a seekable binary file, and its size must be 36 bytes or larger. A file with all binary 0's is a 'blank'. Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-6-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/xlnx-versal-virt.c | 36 ++++++++++++++++++++++++++++++++++++ hw/arm/xlnx-versal.c | 18 ++++++++++++++++++ include/hw/arm/xlnx-versal.h | 5 +++++ 4 files changed, 60 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 18832abf7d..d35ded9b24 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -381,6 +381,7 @@ config XLNX_VERSAL select XLNX_ZDMA select XLNX_ZYNQMP select OR_IRQ + select XLNX_BBRAM config NPCM7XX bool diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 5bca360dce..e1c5ead475 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -356,6 +356,26 @@ static void fdt_add_rtc_node(VersalVirt *s) g_free(name); } +static void fdt_add_bbram_node(VersalVirt *s) +{ + const char compat[] = TYPE_XLNX_BBRAM; + const char interrupt_names[] = "bbram-error"; + char *name = g_strdup_printf("/bbram@%x", MM_PMC_BBRAM_CTRL); + + qemu_fdt_add_subnode(s->fdt, name); + + qemu_fdt_setprop_cells(s->fdt, name, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, VERSAL_BBRAM_APB_IRQ_0, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop(s->fdt, name, "interrupt-names", + interrupt_names, sizeof(interrupt_names)); + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, MM_PMC_BBRAM_CTRL, + 2, MM_PMC_BBRAM_CTRL_SIZE); + qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); + g_free(name); +} + static void fdt_nop_memory_nodes(void *fdt, Error **errp) { Error *err = NULL; @@ -510,6 +530,18 @@ static void create_virtio_regions(VersalVirt *s) } } +static void bbram_attach_drive(XlnxBBRam *dev) +{ + DriveInfo *dinfo; + BlockBackend *blk; + + dinfo = drive_get_by_index(IF_PFLASH, 0); + blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; + if (blk) { + qdev_prop_set_drive(DEVICE(dev), "drive", blk); + } +} + static void sd_plugin_card(SDHCIState *sd, DriveInfo *di) { BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; @@ -570,6 +602,7 @@ static void versal_virt_init(MachineState *machine) fdt_add_usb_xhci_nodes(s); fdt_add_sd_nodes(s); fdt_add_rtc_node(s); + fdt_add_bbram_node(s); fdt_add_cpu_nodes(s, psci_conduit); fdt_add_clk_node(s, "/clk125", 125000000, s->phandle.clk_125Mhz); fdt_add_clk_node(s, "/clk25", 25000000, s->phandle.clk_25Mhz); @@ -579,6 +612,9 @@ static void versal_virt_init(MachineState *machine) memory_region_add_subregion_overlap(get_system_memory(), 0, &s->soc.fpd.apu.mr, 0); + /* Attach bbram backend, if given */ + bbram_attach_drive(&s->soc.pmc.bbram); + /* Plugin SD cards. */ for (i = 0; i < ARRAY_SIZE(s->soc.pmc.iou.sd); i++) { sd_plugin_card(&s->soc.pmc.iou.sd[i], drive_get_next(IF_SD)); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 547a26603a..23451ae012 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -314,6 +314,23 @@ static void versal_create_xrams(Versal *s, qemu_irq *pic) } } +static void versal_create_bbram(Versal *s, qemu_irq *pic) +{ + SysBusDevice *sbd; + + object_initialize_child_with_props(OBJECT(s), "bbram", &s->pmc.bbram, + sizeof(s->pmc.bbram), TYPE_XLNX_BBRAM, + &error_fatal, + "crc-zpads", "0", + NULL); + sbd = SYS_BUS_DEVICE(&s->pmc.bbram); + + sysbus_realize(sbd, &error_fatal); + memory_region_add_subregion(&s->mr_ps, MM_PMC_BBRAM_CTRL, + sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(sbd, 0, pic[VERSAL_BBRAM_APB_IRQ_0]); +} + /* This takes the board allocated linear DDR memory and creates aliases * for each split DDR range/aperture on the Versal address map. */ @@ -402,6 +419,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_sds(s, pic); versal_create_rtc(s, pic); versal_create_xrams(s, pic); + versal_create_bbram(s, pic); versal_map_ddr(s); versal_unimp(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 9b79051747..1cac613338 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -24,6 +24,7 @@ #include "qom/object.h" #include "hw/usb/xlnx-usb-subsystem.h" #include "hw/misc/xlnx-versal-xramc.h" +#include "hw/nvram/xlnx-bbram.h" #define TYPE_XLNX_VERSAL "xlnx-versal" OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) @@ -79,6 +80,7 @@ struct Versal { } iou; XlnxZynqMPRTC rtc; + XlnxBBRam bbram; } pmc; struct { @@ -105,6 +107,7 @@ struct Versal { #define VERSAL_GEM1_WAKE_IRQ_0 59 #define VERSAL_ADMA_IRQ_0 60 #define VERSAL_XRAM_IRQ_0 79 +#define VERSAL_BBRAM_APB_IRQ_0 121 #define VERSAL_RTC_APB_ERR_IRQ 121 #define VERSAL_SD0_IRQ_0 126 #define VERSAL_RTC_ALARM_IRQ 142 @@ -172,6 +175,8 @@ struct Versal { #define MM_PMC_SD0 0xf1040000U #define MM_PMC_SD0_SIZE 0x10000 +#define MM_PMC_BBRAM_CTRL 0xf11f0000 +#define MM_PMC_BBRAM_CTRL_SIZE 0x00050 #define MM_PMC_CRP 0xf1260000U #define MM_PMC_CRP_SIZE 0x10000 #define MM_PMC_RTC 0xf12a0000 From 5f4910ff12f88f9750608cbfe07895204405bed1 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:23:57 -0700 Subject: [PATCH 0091/1334] hw/arm: xlnx-versal-virt: Add Xilinx eFUSE device Connect the support for Versal eFUSE one-time field-programmable bit array. The command argument: -drive if=pflash,index=1,... Can be used to optionally connect the bit array to a backend storage, such that field-programmed values in one invocation can be made available to next invocation. The backend storage must be a seekable binary file, and its size must be 3072 bytes or larger. A file with all binary 0's is a 'blank'. Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-7-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/xlnx-versal-virt.c | 52 ++++++++++++++++++++++++++++++++++++ hw/arm/xlnx-versal.c | 39 +++++++++++++++++++++++++++ include/hw/arm/xlnx-versal.h | 10 +++++++ 4 files changed, 102 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index d35ded9b24..2d37d29f02 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -382,6 +382,7 @@ config XLNX_VERSAL select XLNX_ZYNQMP select OR_IRQ select XLNX_BBRAM + select XLNX_EFUSE_VERSAL config NPCM7XX bool diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index e1c5ead475..d2f55e29b6 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -376,6 +376,41 @@ static void fdt_add_bbram_node(VersalVirt *s) g_free(name); } +static void fdt_add_efuse_ctrl_node(VersalVirt *s) +{ + const char compat[] = TYPE_XLNX_VERSAL_EFUSE_CTRL; + const char interrupt_names[] = "pmc_efuse"; + char *name = g_strdup_printf("/pmc_efuse@%x", MM_PMC_EFUSE_CTRL); + + qemu_fdt_add_subnode(s->fdt, name); + + qemu_fdt_setprop_cells(s->fdt, name, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, VERSAL_EFUSE_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop(s->fdt, name, "interrupt-names", + interrupt_names, sizeof(interrupt_names)); + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, MM_PMC_EFUSE_CTRL, + 2, MM_PMC_EFUSE_CTRL_SIZE); + qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); + g_free(name); +} + +static void fdt_add_efuse_cache_node(VersalVirt *s) +{ + const char compat[] = TYPE_XLNX_VERSAL_EFUSE_CACHE; + char *name = g_strdup_printf("/xlnx_pmc_efuse_cache@%x", + MM_PMC_EFUSE_CACHE); + + qemu_fdt_add_subnode(s->fdt, name); + + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, MM_PMC_EFUSE_CACHE, + 2, MM_PMC_EFUSE_CACHE_SIZE); + qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); + g_free(name); +} + static void fdt_nop_memory_nodes(void *fdt, Error **errp) { Error *err = NULL; @@ -542,6 +577,18 @@ static void bbram_attach_drive(XlnxBBRam *dev) } } +static void efuse_attach_drive(XlnxEFuse *dev) +{ + DriveInfo *dinfo; + BlockBackend *blk; + + dinfo = drive_get_by_index(IF_PFLASH, 1); + blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; + if (blk) { + qdev_prop_set_drive(DEVICE(dev), "drive", blk); + } +} + static void sd_plugin_card(SDHCIState *sd, DriveInfo *di) { BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; @@ -603,6 +650,8 @@ static void versal_virt_init(MachineState *machine) fdt_add_sd_nodes(s); fdt_add_rtc_node(s); fdt_add_bbram_node(s); + fdt_add_efuse_ctrl_node(s); + fdt_add_efuse_cache_node(s); fdt_add_cpu_nodes(s, psci_conduit); fdt_add_clk_node(s, "/clk125", 125000000, s->phandle.clk_125Mhz); fdt_add_clk_node(s, "/clk25", 25000000, s->phandle.clk_25Mhz); @@ -615,6 +664,9 @@ static void versal_virt_init(MachineState *machine) /* Attach bbram backend, if given */ bbram_attach_drive(&s->soc.pmc.bbram); + /* Attach efuse backend, if given */ + efuse_attach_drive(&s->soc.pmc.efuse); + /* Plugin SD cards. */ for (i = 0; i < ARRAY_SIZE(s->soc.pmc.iou.sd); i++) { sd_plugin_card(&s->soc.pmc.iou.sd[i], drive_get_next(IF_SD)); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 23451ae012..b2705b6925 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -331,6 +331,44 @@ static void versal_create_bbram(Versal *s, qemu_irq *pic) sysbus_connect_irq(sbd, 0, pic[VERSAL_BBRAM_APB_IRQ_0]); } +static void versal_realize_efuse_part(Versal *s, Object *dev, hwaddr base) +{ + SysBusDevice *part = SYS_BUS_DEVICE(dev); + + object_property_set_link(OBJECT(part), "efuse", + OBJECT(&s->pmc.efuse), &error_abort); + + sysbus_realize(part, &error_abort); + memory_region_add_subregion(&s->mr_ps, base, + sysbus_mmio_get_region(part, 0)); +} + +static void versal_create_efuse(Versal *s, qemu_irq *pic) +{ + Object *bits = OBJECT(&s->pmc.efuse); + Object *ctrl = OBJECT(&s->pmc.efuse_ctrl); + Object *cache = OBJECT(&s->pmc.efuse_cache); + + object_initialize_child(OBJECT(s), "efuse-ctrl", &s->pmc.efuse_ctrl, + TYPE_XLNX_VERSAL_EFUSE_CTRL); + + object_initialize_child(OBJECT(s), "efuse-cache", &s->pmc.efuse_cache, + TYPE_XLNX_VERSAL_EFUSE_CACHE); + + object_initialize_child_with_props(ctrl, "xlnx-efuse@0", bits, + sizeof(s->pmc.efuse), + TYPE_XLNX_EFUSE, &error_abort, + "efuse-nr", "3", + "efuse-size", "8192", + NULL); + + qdev_realize(DEVICE(bits), NULL, &error_abort); + versal_realize_efuse_part(s, ctrl, MM_PMC_EFUSE_CTRL); + versal_realize_efuse_part(s, cache, MM_PMC_EFUSE_CACHE); + + sysbus_connect_irq(SYS_BUS_DEVICE(ctrl), 0, pic[VERSAL_EFUSE_IRQ]); +} + /* This takes the board allocated linear DDR memory and creates aliases * for each split DDR range/aperture on the Versal address map. */ @@ -420,6 +458,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_rtc(s, pic); versal_create_xrams(s, pic); versal_create_bbram(s, pic); + versal_create_efuse(s, pic); versal_map_ddr(s); versal_unimp(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 1cac613338..895ba12c61 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -25,6 +25,7 @@ #include "hw/usb/xlnx-usb-subsystem.h" #include "hw/misc/xlnx-versal-xramc.h" #include "hw/nvram/xlnx-bbram.h" +#include "hw/nvram/xlnx-versal-efuse.h" #define TYPE_XLNX_VERSAL "xlnx-versal" OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) @@ -81,6 +82,9 @@ struct Versal { XlnxZynqMPRTC rtc; XlnxBBRam bbram; + XlnxEFuse efuse; + XlnxVersalEFuseCtrl efuse_ctrl; + XlnxVersalEFuseCache efuse_cache; } pmc; struct { @@ -110,6 +114,7 @@ struct Versal { #define VERSAL_BBRAM_APB_IRQ_0 121 #define VERSAL_RTC_APB_ERR_IRQ 121 #define VERSAL_SD0_IRQ_0 126 +#define VERSAL_EFUSE_IRQ 139 #define VERSAL_RTC_ALARM_IRQ 142 #define VERSAL_RTC_SECONDS_IRQ 143 @@ -177,6 +182,11 @@ struct Versal { #define MM_PMC_SD0_SIZE 0x10000 #define MM_PMC_BBRAM_CTRL 0xf11f0000 #define MM_PMC_BBRAM_CTRL_SIZE 0x00050 +#define MM_PMC_EFUSE_CTRL 0xf1240000 +#define MM_PMC_EFUSE_CTRL_SIZE 0x00104 +#define MM_PMC_EFUSE_CACHE 0xf1250000 +#define MM_PMC_EFUSE_CACHE_SIZE 0x00C00 + #define MM_PMC_CRP 0xf1260000U #define MM_PMC_CRP_SIZE 0x10000 #define MM_PMC_RTC 0xf12a0000 From 7e47e15c8b47ac866e8f07998276b01e612a360a Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:23:58 -0700 Subject: [PATCH 0092/1334] hw/arm: xlnx-zcu102: Add Xilinx BBRAM device Connect the support for Xilinx ZynqMP Battery-Backed RAM (BBRAM) The command argument: -drive if=pflash,index=2,... Can be used to optionally connect the bbram to a backend storage, such that field-programmed values in one invocation can be made available to next invocation. The backend storage must be a seekable binary file, and its size must be 36 bytes or larger. A file with all binary 0's is a 'blank'. Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-8-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/Kconfig | 1 + hw/arm/xlnx-zcu102.c | 15 +++++++++++++++ hw/arm/xlnx-zynqmp.c | 20 ++++++++++++++++++++ include/hw/arm/xlnx-zynqmp.h | 2 ++ 4 files changed, 38 insertions(+) diff --git a/hw/Kconfig b/hw/Kconfig index 8cb7664d70..b6fb6a4507 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -81,3 +81,4 @@ config XLNX_ZYNQMP select REGISTER select CAN_BUS select PTIMER + select XLNX_BBRAM diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 6c6cb02e86..b247c5779b 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -98,6 +98,18 @@ static void zcu102_modify_dtb(const struct arm_boot_info *binfo, void *fdt) } } +static void bbram_attach_drive(XlnxBBRam *dev) +{ + DriveInfo *dinfo; + BlockBackend *blk; + + dinfo = drive_get_by_index(IF_PFLASH, 2); + blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; + if (blk) { + qdev_prop_set_drive(DEVICE(dev), "drive", blk); + } +} + static void xlnx_zcu102_init(MachineState *machine) { XlnxZCU102 *s = ZCU102_MACHINE(machine); @@ -136,6 +148,9 @@ static void xlnx_zcu102_init(MachineState *machine) qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + /* Attach bbram backend, if given */ + bbram_attach_drive(&s->soc.bbram); + /* Create and plug in the SD cards */ for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { BusState *bus; diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 4e5a471e30..1e8e2ddcc2 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -66,6 +66,9 @@ #define RTC_ADDR 0xffa60000 #define RTC_IRQ 26 +#define BBRAM_ADDR 0xffcd0000 +#define BBRAM_IRQ 11 + #define SDHCI_CAPABILITIES 0x280737ec6481 /* Datasheet: UG1085 (v1.7) */ static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = { @@ -226,6 +229,22 @@ static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s, qdev_realize(DEVICE(&s->rpu_cluster), NULL, &error_fatal); } +static void xlnx_zynqmp_create_bbram(XlnxZynqMPState *s, qemu_irq *gic) +{ + SysBusDevice *sbd; + + object_initialize_child_with_props(OBJECT(s), "bbram", &s->bbram, + sizeof(s->bbram), TYPE_XLNX_BBRAM, + &error_fatal, + "crc-zpads", "1", + NULL); + sbd = SYS_BUS_DEVICE(&s->bbram); + + sysbus_realize(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, BBRAM_ADDR); + sysbus_connect_irq(sbd, 0, gic[BBRAM_IRQ]); +} + static void xlnx_zynqmp_create_unimp_mmio(XlnxZynqMPState *s) { static const struct UnimpInfo { @@ -626,6 +645,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]); + xlnx_zynqmp_create_bbram(s, gic_spi); xlnx_zynqmp_create_unimp_mmio(s); for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index c84fe15996..067e8a5238 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -36,6 +36,7 @@ #include "qom/object.h" #include "net/can_emu.h" #include "hw/dma/xlnx_csu_dma.h" +#include "hw/nvram/xlnx-bbram.h" #define TYPE_XLNX_ZYNQMP "xlnx-zynqmp" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) @@ -100,6 +101,7 @@ struct XlnxZynqMPState { MemoryRegion *ddr_ram; MemoryRegion ddr_ram_low, ddr_ram_high; + XlnxBBRam bbram; MemoryRegion mr_unimp[XLNX_ZYNQMP_NUM_UNIMP_AREAS]; From db1264df32d5482cb49ea5acbd6dcf2f466325c2 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:23:59 -0700 Subject: [PATCH 0093/1334] hw/arm: xlnx-zcu102: Add Xilinx eFUSE device Connect the support for ZynqMP eFUSE one-time field-programmable bit array. The command argument: -drive if=pflash,index=3,... Can be used to optionally connect the bit array to a backend storage, such that field-programmed values in one invocation can be made available to next invocation. The backend storage must be a seekable binary file, and its size must be 768 bytes or larger. A file with all binary 0's is a 'blank'. Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-9-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/Kconfig | 1 + hw/arm/xlnx-zcu102.c | 15 +++++++++++++++ hw/arm/xlnx-zynqmp.c | 29 +++++++++++++++++++++++++++++ include/hw/arm/xlnx-zynqmp.h | 3 +++ 4 files changed, 48 insertions(+) diff --git a/hw/Kconfig b/hw/Kconfig index b6fb6a4507..ad20cce0a9 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -82,3 +82,4 @@ config XLNX_ZYNQMP select CAN_BUS select PTIMER select XLNX_BBRAM + select XLNX_EFUSE_ZYNQMP diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index b247c5779b..3dc2b5e8ca 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -110,6 +110,18 @@ static void bbram_attach_drive(XlnxBBRam *dev) } } +static void efuse_attach_drive(XlnxEFuse *dev) +{ + DriveInfo *dinfo; + BlockBackend *blk; + + dinfo = drive_get_by_index(IF_PFLASH, 3); + blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; + if (blk) { + qdev_prop_set_drive(DEVICE(dev), "drive", blk); + } +} + static void xlnx_zcu102_init(MachineState *machine) { XlnxZCU102 *s = ZCU102_MACHINE(machine); @@ -151,6 +163,9 @@ static void xlnx_zcu102_init(MachineState *machine) /* Attach bbram backend, if given */ bbram_attach_drive(&s->soc.bbram); + /* Attach efuse backend, if given */ + efuse_attach_drive(&s->soc.efuse); + /* Create and plug in the SD cards */ for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { BusState *bus; diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 1e8e2ddcc2..1c52a575aa 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -69,6 +69,9 @@ #define BBRAM_ADDR 0xffcd0000 #define BBRAM_IRQ 11 +#define EFUSE_ADDR 0xffcc0000 +#define EFUSE_IRQ 87 + #define SDHCI_CAPABILITIES 0x280737ec6481 /* Datasheet: UG1085 (v1.7) */ static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = { @@ -245,6 +248,31 @@ static void xlnx_zynqmp_create_bbram(XlnxZynqMPState *s, qemu_irq *gic) sysbus_connect_irq(sbd, 0, gic[BBRAM_IRQ]); } +static void xlnx_zynqmp_create_efuse(XlnxZynqMPState *s, qemu_irq *gic) +{ + Object *bits = OBJECT(&s->efuse); + Object *ctrl = OBJECT(&s->efuse_ctrl); + SysBusDevice *sbd; + + object_initialize_child(OBJECT(s), "efuse-ctrl", &s->efuse_ctrl, + TYPE_XLNX_ZYNQMP_EFUSE); + + object_initialize_child_with_props(ctrl, "xlnx-efuse@0", bits, + sizeof(s->efuse), + TYPE_XLNX_EFUSE, &error_abort, + "efuse-nr", "3", + "efuse-size", "2048", + NULL); + + qdev_realize(DEVICE(bits), NULL, &error_abort); + object_property_set_link(ctrl, "efuse", bits, &error_abort); + + sbd = SYS_BUS_DEVICE(ctrl); + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, EFUSE_ADDR); + sysbus_connect_irq(sbd, 0, gic[EFUSE_IRQ]); +} + static void xlnx_zynqmp_create_unimp_mmio(XlnxZynqMPState *s) { static const struct UnimpInfo { @@ -646,6 +674,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]); xlnx_zynqmp_create_bbram(s, gic_spi); + xlnx_zynqmp_create_efuse(s, gic_spi); xlnx_zynqmp_create_unimp_mmio(s); for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 067e8a5238..062e637fe4 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -37,6 +37,7 @@ #include "net/can_emu.h" #include "hw/dma/xlnx_csu_dma.h" #include "hw/nvram/xlnx-bbram.h" +#include "hw/nvram/xlnx-zynqmp-efuse.h" #define TYPE_XLNX_ZYNQMP "xlnx-zynqmp" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) @@ -102,6 +103,8 @@ struct XlnxZynqMPState { MemoryRegion *ddr_ram; MemoryRegion ddr_ram_low, ddr_ram_high; XlnxBBRam bbram; + XlnxEFuse efuse; + XlnxZynqMPEFuse efuse_ctrl; MemoryRegion mr_unimp[XLNX_ZYNQMP_NUM_UNIMP_AREAS]; From 09e010aedeb0a20aefa1fa0c06cf421e80f25edd Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Thu, 16 Sep 2021 22:24:00 -0700 Subject: [PATCH 0094/1334] docs/system/arm: xlnx-versal-virt: BBRAM and eFUSE Usage Add BBRAM and eFUSE usage to the Xilinx Versal Virt board document. Signed-off-by: Tong Ho Message-id: 20210917052400.1249094-10-tong.ho@xilinx.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- docs/system/arm/xlnx-versal-virt.rst | 49 ++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index 27f73500d9..92ad10d2da 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -32,6 +32,8 @@ Implemented devices: - OCM (256KB of On Chip Memory) - XRAM (4MB of on chip Accelerator RAM) - DDR memory +- BBRAM (36 bytes of Battery-backed RAM) +- eFUSE (3072 bytes of one-time field-programmable bit array) QEMU does not yet model any other devices, including the PL and the AI Engine. @@ -175,3 +177,50 @@ Run the following at the U-Boot prompt: fdt set /chosen/dom0 reg <0x00000000 0x40000000 0x0 0x03100000> booti 30000000 - 20000000 +BBRAM File Backend +"""""""""""""""""" +BBRAM can have an optional file backend, which must be a seekable +binary file with a size of 36 bytes or larger. A file with all +binary 0s is a 'blank'. + +To add a file-backend for the BBRAM: + +.. code-block:: bash + + -drive if=pflash,index=0,file=versal-bbram.bin,format=raw + +To use a different index value, N, from default of 0, add: + +.. code-block:: bash + + -global xlnx,bbram-ctrl.drive-index=N + +eFUSE File Backend +"""""""""""""""""" +eFUSE can have an optional file backend, which must be a seekable +binary file with a size of 3072 bytes or larger. A file with all +binary 0s is a 'blank'. + +To add a file-backend for the eFUSE: + +.. code-block:: bash + + -drive if=pflash,index=1,file=versal-efuse.bin,format=raw + +To use a different index value, N, from default of 1, add: + +.. code-block:: bash + + -global xlnx,efuse.drive-index=N + +.. warning:: + In actual physical Versal, BBRAM and eFUSE contain sensitive data. + The QEMU device models do **not** encrypt nor obfuscate any data + when holding them in models' memory or when writing them to their + file backends. + + Thus, a file backend should be used with caution, and 'format=luks' + is highly recommended (albeit with usage complexity). + + Better yet, do not use actual product data when running guest image + on this Xilinx Versal Virt board. From 0e2a76110465ec95fd9f2c4820f186ca8106ab49 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 21 Sep 2021 17:28:57 +0100 Subject: [PATCH 0095/1334] configs: Don't include 32-bit-only GDB XML in aarch64 linux configs The aarch64-linux QEMU usermode binaries can never run 32-bit code, so they do not need to include the GDB XML for it. (arm_cpu_register_gdb_regs_for_features() will not use these XML files if the CPU has ARM_FEATURE_AARCH64, so we will not advertise to gdb that we have them.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210921162901.17508-2-peter.maydell@linaro.org --- configs/targets/aarch64-linux-user.mak | 2 +- configs/targets/aarch64_be-linux-user.mak | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak index 4713253709..d0c603c54e 100644 --- a/configs/targets/aarch64-linux-user.mak +++ b/configs/targets/aarch64-linux-user.mak @@ -1,5 +1,5 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_HAS_BFLT=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak index fae831558d..d3ee10c00f 100644 --- a/configs/targets/aarch64_be-linux-user.mak +++ b/configs/targets/aarch64_be-linux-user.mak @@ -1,6 +1,6 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_WORDS_BIGENDIAN=y -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_HAS_BFLT=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y From d59b7cdccc6558f126c3081f7582131029c35660 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 21 Sep 2021 17:28:58 +0100 Subject: [PATCH 0096/1334] target/arm: Fix coding style issues in gdbstub code in helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're going to move this code to a different file; fix the coding style first so checkpatch doesn't complain. This includes deleting the spurious 'break' statements after returns in the vfp_gdb_get_reg() function. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20210921162901.17508-3-peter.maydell@linaro.org --- target/arm/helper.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6274221447..84bba9a224 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -72,9 +72,12 @@ static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) } } switch (reg - nregs) { - case 0: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); break; - case 1: return gdb_get_reg32(buf, vfp_get_fpscr(env)); break; - case 2: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); break; + case 0: + return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); + case 1: + return gdb_get_reg32(buf, vfp_get_fpscr(env)); + case 2: + return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); } return 0; } @@ -98,9 +101,15 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) } } switch (reg - nregs) { - case 0: env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); return 4; - case 1: vfp_set_fpscr(env, ldl_p(buf)); return 4; - case 2: env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf) & (1 << 30); return 4; + case 0: + env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); + return 4; + case 1: + vfp_set_fpscr(env, ldl_p(buf)); + return 4; + case 2: + env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf) & (1 << 30); + return 4; } return 0; } @@ -119,7 +128,7 @@ static int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) return gdb_get_reg32(buf, vfp_get_fpsr(env)); case 33: /* FPCR */ - return gdb_get_reg32(buf,vfp_get_fpcr(env)); + return gdb_get_reg32(buf, vfp_get_fpcr(env)); default: return 0; } From 89f4f20e276e6e5dc08fca5e75e2bfbd92280072 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 21 Sep 2021 17:28:59 +0100 Subject: [PATCH 0097/1334] target/arm: Move gdbstub related code out of helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently helper.c includes some code which is part of the arm target's gdbstub support. This code has a better home: in gdbstub.c and gdbstub64.c. Move it there. Because aarch64_fpu_gdb_get_reg() and aarch64_fpu_gdb_set_reg() move into gdbstub64.c, this means that they're now compiled only for TARGET_AARCH64 rather than always. That is the only case when they would ever be used, but it does mean that the ifdef in arm_cpu_register_gdb_regs_for_features() needs to be adjusted to match. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20210921162901.17508-4-peter.maydell@linaro.org --- target/arm/gdbstub.c | 130 ++++++++++++++++++++ target/arm/gdbstub64.c | 140 +++++++++++++++++++++ target/arm/helper.c | 271 ----------------------------------------- target/arm/internals.h | 7 ++ 4 files changed, 277 insertions(+), 271 deletions(-) diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 826601b341..cbf156d192 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" +#include "internals.h" #include "exec/gdbstub.h" typedef struct RegisterSysregXmlParam { @@ -124,6 +125,98 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } +static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; + + /* VFP data registers are always little-endian. */ + if (reg < nregs) { + return gdb_get_reg64(buf, *aa32_vfp_dreg(env, reg)); + } + if (arm_feature(env, ARM_FEATURE_NEON)) { + /* Aliases for Q regs. */ + nregs += 16; + if (reg < nregs) { + uint64_t *q = aa32_vfp_qreg(env, reg - 32); + return gdb_get_reg128(buf, q[0], q[1]); + } + } + switch (reg - nregs) { + case 0: + return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); + case 1: + return gdb_get_reg32(buf, vfp_get_fpscr(env)); + case 2: + return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); + } + return 0; +} + +static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; + + if (reg < nregs) { + *aa32_vfp_dreg(env, reg) = ldq_le_p(buf); + return 8; + } + if (arm_feature(env, ARM_FEATURE_NEON)) { + nregs += 16; + if (reg < nregs) { + uint64_t *q = aa32_vfp_qreg(env, reg - 32); + q[0] = ldq_le_p(buf); + q[1] = ldq_le_p(buf + 8); + return 16; + } + } + switch (reg - nregs) { + case 0: + env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); + return 4; + case 1: + vfp_set_fpscr(env, ldl_p(buf)); + return 4; + case 2: + env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf) & (1 << 30); + return 4; + } + return 0; +} + +/** + * arm_get/set_gdb_*: get/set a gdb register + * @env: the CPU state + * @buf: a buffer to copy to/from + * @reg: register number (offset from start of group) + * + * We return the number of bytes copied + */ + +static int arm_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + const ARMCPRegInfo *ri; + uint32_t key; + + key = cpu->dyn_sysreg_xml.data.cpregs.keys[reg]; + ri = get_arm_cp_reginfo(cpu->cp_regs, key); + if (ri) { + if (cpreg_field_is_64bit(ri)) { + return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri)); + } else { + return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri)); + } + } + return 0; +} + +static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) +{ + return 0; +} + static void arm_gen_one_xml_sysreg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, ARMCPRegInfo *ri, uint32_t ri_key, int bitsize, int regnum) @@ -319,3 +412,40 @@ const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) } return NULL; } + +void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUARMState *env = &cpu->env; + + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + /* + * The lower part of each SVE register aliases to the FPU + * registers so we don't need to include both. + */ +#ifdef TARGET_AARCH64 + if (isar_feature_aa64_sve(&cpu->isar)) { + gdb_register_coprocessor(cs, arm_gdb_get_svereg, arm_gdb_set_svereg, + arm_gen_dynamic_svereg_xml(cs, cs->gdb_num_regs), + "sve-registers.xml", 0); + } else { + gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, + aarch64_fpu_gdb_set_reg, + 34, "aarch64-fpu.xml", 0); + } +#endif + } else if (arm_feature(env, ARM_FEATURE_NEON)) { + gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, + 51, "arm-neon.xml", 0); + } else if (cpu_isar_feature(aa32_simd_r32, cpu)) { + gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, + 35, "arm-vfp3.xml", 0); + } else if (cpu_isar_feature(aa32_vfp_simd, cpu)) { + gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, + 19, "arm-vfp.xml", 0); + } + gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, + arm_gen_dynamic_sysreg_xml(cs, cs->gdb_num_regs), + "system-registers.xml", 0); + +} diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 251539ef79..596878666d 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -17,7 +17,9 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" +#include "internals.h" #include "exec/gdbstub.h" int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) @@ -69,3 +71,141 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* Unknown register. */ return 0; } + +int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) +{ + switch (reg) { + case 0 ... 31: + { + /* 128 bit FP register - quads are in LE order */ + uint64_t *q = aa64_vfp_qreg(env, reg); + return gdb_get_reg128(buf, q[1], q[0]); + } + case 32: + /* FPSR */ + return gdb_get_reg32(buf, vfp_get_fpsr(env)); + case 33: + /* FPCR */ + return gdb_get_reg32(buf, vfp_get_fpcr(env)); + default: + return 0; + } +} + +int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) +{ + switch (reg) { + case 0 ... 31: + /* 128 bit FP register */ + { + uint64_t *q = aa64_vfp_qreg(env, reg); + q[0] = ldq_le_p(buf); + q[1] = ldq_le_p(buf + 8); + return 16; + } + case 32: + /* FPSR */ + vfp_set_fpsr(env, ldl_p(buf)); + return 4; + case 33: + /* FPCR */ + vfp_set_fpcr(env, ldl_p(buf)); + return 4; + default: + return 0; + } +} + +int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + + switch (reg) { + /* The first 32 registers are the zregs */ + case 0 ... 31: + { + int vq, len = 0; + for (vq = 0; vq < cpu->sve_max_vq; vq++) { + len += gdb_get_reg128(buf, + env->vfp.zregs[reg].d[vq * 2 + 1], + env->vfp.zregs[reg].d[vq * 2]); + } + return len; + } + case 32: + return gdb_get_reg32(buf, vfp_get_fpsr(env)); + case 33: + return gdb_get_reg32(buf, vfp_get_fpcr(env)); + /* then 16 predicates and the ffr */ + case 34 ... 50: + { + int preg = reg - 34; + int vq, len = 0; + for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { + len += gdb_get_reg64(buf, env->vfp.pregs[preg].p[vq / 4]); + } + return len; + } + case 51: + { + /* + * We report in Vector Granules (VG) which is 64bit in a Z reg + * while the ZCR works in Vector Quads (VQ) which is 128bit chunks. + */ + int vq = sve_zcr_len_for_el(env, arm_current_el(env)) + 1; + return gdb_get_reg64(buf, vq * 2); + } + default: + /* gdbstub asked for something out our range */ + qemu_log_mask(LOG_UNIMP, "%s: out of range register %d", __func__, reg); + break; + } + + return 0; +} + +int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + + /* The first 32 registers are the zregs */ + switch (reg) { + /* The first 32 registers are the zregs */ + case 0 ... 31: + { + int vq, len = 0; + uint64_t *p = (uint64_t *) buf; + for (vq = 0; vq < cpu->sve_max_vq; vq++) { + env->vfp.zregs[reg].d[vq * 2 + 1] = *p++; + env->vfp.zregs[reg].d[vq * 2] = *p++; + len += 16; + } + return len; + } + case 32: + vfp_set_fpsr(env, *(uint32_t *)buf); + return 4; + case 33: + vfp_set_fpcr(env, *(uint32_t *)buf); + return 4; + case 34 ... 50: + { + int preg = reg - 34; + int vq, len = 0; + uint64_t *p = (uint64_t *) buf; + for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { + env->vfp.pregs[preg].p[vq / 4] = *p++; + len += 8; + } + return len; + } + case 51: + /* cannot set vg via gdbstub */ + return 0; + default: + /* gdbstub asked for something out our range */ + break; + } + + return 0; +} diff --git a/target/arm/helper.c b/target/arm/helper.c index 84bba9a224..9b317899a6 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -12,7 +12,6 @@ #include "trace.h" #include "cpu.h" #include "internals.h" -#include "exec/gdbstub.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "qemu/main-loop.h" @@ -54,110 +53,6 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, static void switch_mode(CPUARMState *env, int mode); static int aa64_va_parameter_tbi(uint64_t tcr, ARMMMUIdx mmu_idx); -static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) -{ - ARMCPU *cpu = env_archcpu(env); - int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; - - /* VFP data registers are always little-endian. */ - if (reg < nregs) { - return gdb_get_reg64(buf, *aa32_vfp_dreg(env, reg)); - } - if (arm_feature(env, ARM_FEATURE_NEON)) { - /* Aliases for Q regs. */ - nregs += 16; - if (reg < nregs) { - uint64_t *q = aa32_vfp_qreg(env, reg - 32); - return gdb_get_reg128(buf, q[0], q[1]); - } - } - switch (reg - nregs) { - case 0: - return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); - case 1: - return gdb_get_reg32(buf, vfp_get_fpscr(env)); - case 2: - return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); - } - return 0; -} - -static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) -{ - ARMCPU *cpu = env_archcpu(env); - int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; - - if (reg < nregs) { - *aa32_vfp_dreg(env, reg) = ldq_le_p(buf); - return 8; - } - if (arm_feature(env, ARM_FEATURE_NEON)) { - nregs += 16; - if (reg < nregs) { - uint64_t *q = aa32_vfp_qreg(env, reg - 32); - q[0] = ldq_le_p(buf); - q[1] = ldq_le_p(buf + 8); - return 16; - } - } - switch (reg - nregs) { - case 0: - env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); - return 4; - case 1: - vfp_set_fpscr(env, ldl_p(buf)); - return 4; - case 2: - env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf) & (1 << 30); - return 4; - } - return 0; -} - -static int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) -{ - switch (reg) { - case 0 ... 31: - { - /* 128 bit FP register - quads are in LE order */ - uint64_t *q = aa64_vfp_qreg(env, reg); - return gdb_get_reg128(buf, q[1], q[0]); - } - case 32: - /* FPSR */ - return gdb_get_reg32(buf, vfp_get_fpsr(env)); - case 33: - /* FPCR */ - return gdb_get_reg32(buf, vfp_get_fpcr(env)); - default: - return 0; - } -} - -static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) -{ - switch (reg) { - case 0 ... 31: - /* 128 bit FP register */ - { - uint64_t *q = aa64_vfp_qreg(env, reg); - q[0] = ldq_le_p(buf); - q[1] = ldq_le_p(buf + 8); - return 16; - } - case 32: - /* FPSR */ - vfp_set_fpsr(env, ldl_p(buf)); - return 4; - case 33: - /* FPCR */ - vfp_set_fpcr(env, ldl_p(buf)); - return 4; - default: - return 0; - } -} - static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) { assert(ri->fieldoffset); @@ -217,134 +112,6 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, } } -/** - * arm_get/set_gdb_*: get/set a gdb register - * @env: the CPU state - * @buf: a buffer to copy to/from - * @reg: register number (offset from start of group) - * - * We return the number of bytes copied - */ - -static int arm_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) -{ - ARMCPU *cpu = env_archcpu(env); - const ARMCPRegInfo *ri; - uint32_t key; - - key = cpu->dyn_sysreg_xml.data.cpregs.keys[reg]; - ri = get_arm_cp_reginfo(cpu->cp_regs, key); - if (ri) { - if (cpreg_field_is_64bit(ri)) { - return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri)); - } else { - return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri)); - } - } - return 0; -} - -static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) -{ - return 0; -} - -#ifdef TARGET_AARCH64 -static int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg) -{ - ARMCPU *cpu = env_archcpu(env); - - switch (reg) { - /* The first 32 registers are the zregs */ - case 0 ... 31: - { - int vq, len = 0; - for (vq = 0; vq < cpu->sve_max_vq; vq++) { - len += gdb_get_reg128(buf, - env->vfp.zregs[reg].d[vq * 2 + 1], - env->vfp.zregs[reg].d[vq * 2]); - } - return len; - } - case 32: - return gdb_get_reg32(buf, vfp_get_fpsr(env)); - case 33: - return gdb_get_reg32(buf, vfp_get_fpcr(env)); - /* then 16 predicates and the ffr */ - case 34 ... 50: - { - int preg = reg - 34; - int vq, len = 0; - for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { - len += gdb_get_reg64(buf, env->vfp.pregs[preg].p[vq / 4]); - } - return len; - } - case 51: - { - /* - * We report in Vector Granules (VG) which is 64bit in a Z reg - * while the ZCR works in Vector Quads (VQ) which is 128bit chunks. - */ - int vq = sve_zcr_len_for_el(env, arm_current_el(env)) + 1; - return gdb_get_reg64(buf, vq * 2); - } - default: - /* gdbstub asked for something out our range */ - qemu_log_mask(LOG_UNIMP, "%s: out of range register %d", __func__, reg); - break; - } - - return 0; -} - -static int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg) -{ - ARMCPU *cpu = env_archcpu(env); - - /* The first 32 registers are the zregs */ - switch (reg) { - /* The first 32 registers are the zregs */ - case 0 ... 31: - { - int vq, len = 0; - uint64_t *p = (uint64_t *) buf; - for (vq = 0; vq < cpu->sve_max_vq; vq++) { - env->vfp.zregs[reg].d[vq * 2 + 1] = *p++; - env->vfp.zregs[reg].d[vq * 2] = *p++; - len += 16; - } - return len; - } - case 32: - vfp_set_fpsr(env, *(uint32_t *)buf); - return 4; - case 33: - vfp_set_fpcr(env, *(uint32_t *)buf); - return 4; - case 34 ... 50: - { - int preg = reg - 34; - int vq, len = 0; - uint64_t *p = (uint64_t *) buf; - for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { - env->vfp.pregs[preg].p[vq / 4] = *p++; - len += 8; - } - return len; - } - case 51: - /* cannot set vg via gdbstub */ - return 0; - default: - /* gdbstub asked for something out our range */ - break; - } - - return 0; -} -#endif /* TARGET_AARCH64 */ - static bool raw_accessors_invalid(const ARMCPRegInfo *ri) { /* Return true if the regdef would cause an assertion if you called @@ -8667,44 +8434,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) #endif } -void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUARMState *env = &cpu->env; - - if (arm_feature(env, ARM_FEATURE_AARCH64)) { - /* - * The lower part of each SVE register aliases to the FPU - * registers so we don't need to include both. - */ -#ifdef TARGET_AARCH64 - if (isar_feature_aa64_sve(&cpu->isar)) { - gdb_register_coprocessor(cs, arm_gdb_get_svereg, arm_gdb_set_svereg, - arm_gen_dynamic_svereg_xml(cs, cs->gdb_num_regs), - "sve-registers.xml", 0); - } else -#endif - { - gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, - aarch64_fpu_gdb_set_reg, - 34, "aarch64-fpu.xml", 0); - } - } else if (arm_feature(env, ARM_FEATURE_NEON)) { - gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 51, "arm-neon.xml", 0); - } else if (cpu_isar_feature(aa32_simd_r32, cpu)) { - gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 35, "arm-vfp3.xml", 0); - } else if (cpu_isar_feature(aa32_vfp_simd, cpu)) { - gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 19, "arm-vfp.xml", 0); - } - gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, - arm_gen_dynamic_sysreg_xml(cs, cs->gdb_num_regs), - "system-registers.xml", 0); - -} - /* Sort alphabetically by type name, except for "any". */ static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b) { diff --git a/target/arm/internals.h b/target/arm/internals.h index 9fbb364968..3612107ab2 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1270,4 +1270,11 @@ static inline uint64_t pmu_counter_mask(CPUARMState *env) return (1 << 31) | ((1 << pmu_num_counters(env)) - 1); } +#ifdef TARGET_AARCH64 +int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg); +int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg); +int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg); +int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg); +#endif + #endif From b355f08a3724d3f29e1c177dde3a01b649108f98 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 21 Sep 2021 17:29:00 +0100 Subject: [PATCH 0098/1334] target/arm: Don't put FPEXC and FPSID in org.gnu.gdb.arm.vfp XML Currently we send VFP XML which includes D0..D15 or D0..D31, plus FPSID, FPSCR and FPEXC. The upstream GDB tolerates this, but its definition of this XML feature does not include FPSID or FPEXC. In particular, for M-profile cores there are no FPSID or FPEXC registers, so advertising those is wrong. Move FPSID and FPEXC into their own bit of XML which we only send for A and R profile cores. This brings our definition of the XML org.gnu.gdb.arm.vfp feature into line with GDB's own (at least for non-Neon cores...) and means we don't claim to have FPSID and FPEXC on M-profile. (It seems unlikely to me that any gdbstub users really care about being able to look at FPEXC and FPSID; but we've supplied them to gdb for a decade and it's not hard to keep doing so.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210921162901.17508-5-peter.maydell@linaro.org --- configs/targets/aarch64-softmmu.mak | 2 +- configs/targets/arm-linux-user.mak | 2 +- configs/targets/arm-softmmu.mak | 2 +- configs/targets/armeb-linux-user.mak | 2 +- gdb-xml/arm-neon.xml | 2 - gdb-xml/arm-vfp-sysregs.xml | 17 +++++++++ gdb-xml/arm-vfp.xml | 2 - gdb-xml/arm-vfp3.xml | 2 - target/arm/gdbstub.c | 56 ++++++++++++++++++++-------- 9 files changed, 61 insertions(+), 26 deletions(-) create mode 100644 gdb-xml/arm-vfp-sysregs.xml diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index 7703127674..13d40b55e6 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -1,5 +1,5 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml TARGET_NEED_FDT=y diff --git a/configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak index e741ffd4d3..acecc339e3 100644 --- a/configs/targets/arm-linux-user.mak +++ b/configs/targets/arm-linux-user.mak @@ -1,6 +1,6 @@ TARGET_ARCH=arm TARGET_SYSTBL_ABI=common,oabi TARGET_SYSTBL=syscall.tbl -TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml TARGET_HAS_BFLT=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak index 84a98f4818..f6c95ba07a 100644 --- a/configs/targets/arm-softmmu.mak +++ b/configs/targets/arm-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=arm TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml TARGET_NEED_FDT=y diff --git a/configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak index 255e44e8b0..662c73d8fb 100644 --- a/configs/targets/armeb-linux-user.mak +++ b/configs/targets/armeb-linux-user.mak @@ -2,6 +2,6 @@ TARGET_ARCH=arm TARGET_SYSTBL_ABI=common,oabi TARGET_SYSTBL=syscall.tbl TARGET_WORDS_BIGENDIAN=y -TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml TARGET_HAS_BFLT=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/gdb-xml/arm-neon.xml b/gdb-xml/arm-neon.xml index ce3ee03ec4..9dce0a996f 100644 --- a/gdb-xml/arm-neon.xml +++ b/gdb-xml/arm-neon.xml @@ -82,7 +82,5 @@ - - diff --git a/gdb-xml/arm-vfp-sysregs.xml b/gdb-xml/arm-vfp-sysregs.xml new file mode 100644 index 0000000000..c4aa2721c8 --- /dev/null +++ b/gdb-xml/arm-vfp-sysregs.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/gdb-xml/arm-vfp.xml b/gdb-xml/arm-vfp.xml index b20881e9a9..ebed5b3d57 100644 --- a/gdb-xml/arm-vfp.xml +++ b/gdb-xml/arm-vfp.xml @@ -23,7 +23,5 @@ - - diff --git a/gdb-xml/arm-vfp3.xml b/gdb-xml/arm-vfp3.xml index 227afd8017..ef391c7144 100644 --- a/gdb-xml/arm-vfp3.xml +++ b/gdb-xml/arm-vfp3.xml @@ -39,7 +39,5 @@ - - diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index cbf156d192..e0dcb33e32 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -144,11 +144,7 @@ static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) } switch (reg - nregs) { case 0: - return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); - case 1: return gdb_get_reg32(buf, vfp_get_fpscr(env)); - case 2: - return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); } return 0; } @@ -172,13 +168,31 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) } } switch (reg - nregs) { + case 0: + vfp_set_fpscr(env, ldl_p(buf)); + return 4; + } + return 0; +} + +static int vfp_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) +{ + switch (reg) { + case 0: + return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); + case 1: + return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); + } + return 0; +} + +static int vfp_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) +{ + switch (reg) { case 0: env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); return 4; case 1: - vfp_set_fpscr(env, ldl_p(buf)); - return 4; - case 2: env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf) & (1 << 30); return 4; } @@ -434,15 +448,25 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) 34, "aarch64-fpu.xml", 0); } #endif - } else if (arm_feature(env, ARM_FEATURE_NEON)) { - gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 51, "arm-neon.xml", 0); - } else if (cpu_isar_feature(aa32_simd_r32, cpu)) { - gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 35, "arm-vfp3.xml", 0); - } else if (cpu_isar_feature(aa32_vfp_simd, cpu)) { - gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 19, "arm-vfp.xml", 0); + } else { + if (arm_feature(env, ARM_FEATURE_NEON)) { + gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, + 49, "arm-neon.xml", 0); + } else if (cpu_isar_feature(aa32_simd_r32, cpu)) { + gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, + 33, "arm-vfp3.xml", 0); + } else if (cpu_isar_feature(aa32_vfp_simd, cpu)) { + gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, + 17, "arm-vfp.xml", 0); + } + if (!arm_feature(env, ARM_FEATURE_M)) { + /* + * A and R profile have FP sysregs FPEXC and FPSID that we + * expose to gdb. + */ + gdb_register_coprocessor(cs, vfp_gdb_get_sysreg, vfp_gdb_set_sysreg, + 2, "arm-vfp-sysregs.xml", 0); + } } gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, arm_gen_dynamic_sysreg_xml(cs, cs->gdb_num_regs), From 739e95f5741b8efd74331bb74b9446bcb5ede71e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 23 Sep 2021 13:11:48 +0100 Subject: [PATCH 0099/1334] scsi: Replace scsi_bus_new() with scsi_bus_init(), scsi_bus_init_named() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function scsi_bus_new() creates a new SCSI bus; callers can either pass in a name argument to specify the name of the new bus, or they can pass in NULL to allow the bus to be given an automatically generated unique name. Almost all callers want to use the autogenerated name; the only exception is the virtio-scsi device. Taking a name argument that should almost always be NULL is an easy-to-misuse API design -- it encourages callers to think perhaps they should pass in some standard name like "scsi" or "scsi-bus". We don't do this anywhere for SCSI, but we do (incorrectly) do it for other bus types such as i2c. The function name also implies that it will return a newly allocated object, when it in fact does in-place allocation. We more commonly name such functions foo_init(), with foo_new() being the allocate-and-return variant. Replace all the scsi_bus_new() callsites with either: * scsi_bus_init() for the usual case where the caller wants an autogenerated bus name * scsi_bus_init_named() for the rare case where the caller needs to specify the bus name and document that for the _named() version it's then the caller's responsibility to think about uniqueness of bus names. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Acked-by: Paolo Bonzini Message-id: 20210923121153.23754-2-peter.maydell@linaro.org --- hw/scsi/esp-pci.c | 2 +- hw/scsi/esp.c | 2 +- hw/scsi/lsi53c895a.c | 2 +- hw/scsi/megasas.c | 3 +-- hw/scsi/mptsas.c | 2 +- hw/scsi/scsi-bus.c | 4 ++-- hw/scsi/spapr_vscsi.c | 3 +-- hw/scsi/virtio-scsi.c | 4 ++-- hw/scsi/vmw_pvscsi.c | 3 +-- hw/usb/dev-storage-bot.c | 3 +-- hw/usb/dev-storage-classic.c | 4 ++-- hw/usb/dev-uas.c | 3 +-- include/hw/scsi/scsi.h | 30 ++++++++++++++++++++++++++++-- 13 files changed, 43 insertions(+), 22 deletions(-) diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 9db10b1a48..dac054aeed 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -388,7 +388,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io); s->irq = pci_allocate_irq(dev); - scsi_bus_new(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info); } static void esp_pci_scsi_exit(PCIDevice *d) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 4ac2114788..8454ed1773 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -1348,7 +1348,7 @@ static void sysbus_esp_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, sysbus_esp_gpio_demux, 2); - scsi_bus_new(&s->bus, sizeof(s->bus), dev, &esp_scsi_info, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), dev, &esp_scsi_info); } static void sysbus_esp_hard_reset(DeviceState *dev) diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index e2c19180a0..85e907a785 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -2309,7 +2309,7 @@ static void lsi_scsi_realize(PCIDevice *dev, Error **errp) pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io); QTAILQ_INIT(&s->queue); - scsi_bus_new(&s->bus, sizeof(s->bus), d, &lsi_scsi_info, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), d, &lsi_scsi_info); } static void lsi_scsi_exit(PCIDevice *dev) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 8f2389d2c6..4ff51221d4 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2416,8 +2416,7 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) s->frames[i].state = s; } - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &megasas_scsi_info, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(dev), &megasas_scsi_info); } static Property megasas_properties_gen1[] = { diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index db3219e7d2..f6c7765544 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1315,7 +1315,7 @@ static void mptsas_scsi_realize(PCIDevice *dev, Error **errp) s->request_bh = qemu_bh_new(mptsas_fetch_requests, s); - scsi_bus_new(&s->bus, sizeof(s->bus), &dev->qdev, &mptsas_scsi_info, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), &dev->qdev, &mptsas_scsi_info); } static void mptsas_scsi_uninit(PCIDevice *dev) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 2a0a98cac9..e28a6ea053 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -134,8 +134,8 @@ void scsi_device_unit_attention_reported(SCSIDevice *s) } /* Create a scsi bus, and attach devices to it. */ -void scsi_bus_new(SCSIBus *bus, size_t bus_size, DeviceState *host, - const SCSIBusInfo *info, const char *bus_name) +void scsi_bus_init_named(SCSIBus *bus, size_t bus_size, DeviceState *host, + const SCSIBusInfo *info, const char *bus_name) { qbus_create_inplace(bus, bus_size, TYPE_SCSI_BUS, host, bus_name); bus->busnr = next_scsi_bus++; diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index c210262484..a07a8e1523 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -1216,8 +1216,7 @@ static void spapr_vscsi_realize(SpaprVioDevice *dev, Error **errp) dev->crq.SendFunc = vscsi_do_crq; - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &vscsi_scsi_info, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(dev), &vscsi_scsi_info); /* ibmvscsi SCSI bus does not allow hotplug. */ qbus_set_hotplug_handler(BUS(&s->bus), NULL); diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 6d80730287..51fd09522a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -1019,8 +1019,8 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) return; } - scsi_bus_new(&s->bus, sizeof(s->bus), dev, - &virtio_scsi_scsi_info, vdev->bus_name); + scsi_bus_init_named(&s->bus, sizeof(s->bus), dev, + &virtio_scsi_scsi_info, vdev->bus_name); /* override default SCSI bus hotplug-handler, with virtio-scsi's one */ qbus_set_hotplug_handler(BUS(&s->bus), OBJECT(dev)); diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 1f30cb020a..cd76bd67ab 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1180,8 +1180,7 @@ pvscsi_realizefn(PCIDevice *pci_dev, Error **errp) s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s); - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(pci_dev), - &pvscsi_scsi_info, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(pci_dev), &pvscsi_scsi_info); /* override default SCSI bus hotplug-handler, with pvscsi's one */ qbus_set_hotplug_handler(BUS(&s->bus), OBJECT(s)); pvscsi_reset_state(s); diff --git a/hw/usb/dev-storage-bot.c b/hw/usb/dev-storage-bot.c index 68ebaca10c..b24b3148c2 100644 --- a/hw/usb/dev-storage-bot.c +++ b/hw/usb/dev-storage-bot.c @@ -37,8 +37,7 @@ static void usb_msd_bot_realize(USBDevice *dev, Error **errp) s->dev.auto_attach = 0; } - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &usb_msd_scsi_info_bot, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(dev), &usb_msd_scsi_info_bot); usb_msd_handle_reset(dev); } diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c index 3d017a4e67..00f25bade2 100644 --- a/hw/usb/dev-storage-classic.c +++ b/hw/usb/dev-storage-classic.c @@ -65,8 +65,8 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) usb_desc_create_serial(dev); usb_desc_init(dev); dev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE); - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &usb_msd_scsi_info_storage, NULL); + scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(dev), + &usb_msd_scsi_info_storage); scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, s->conf.bootindex, s->conf.share_rw, s->conf.rerror, s->conf.werror, diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index f6309a5ebf..599d6b52a0 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -938,8 +938,7 @@ static void usb_uas_realize(USBDevice *dev, Error **errp) uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas); dev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE); - scsi_bus_new(&uas->bus, sizeof(uas->bus), DEVICE(dev), - &usb_uas_scsi_info, NULL); + scsi_bus_init(&uas->bus, sizeof(uas->bus), DEVICE(dev), &usb_uas_scsi_info); } static const VMStateDescription vmstate_usb_uas = { diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 0b726bc78c..a567a5ed86 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -146,8 +146,34 @@ struct SCSIBus { const SCSIBusInfo *info; }; -void scsi_bus_new(SCSIBus *bus, size_t bus_size, DeviceState *host, - const SCSIBusInfo *info, const char *bus_name); +/** + * scsi_bus_init_named: Initialize a SCSI bus with the specified name + * @bus: SCSIBus object to initialize + * @bus_size: size of @bus object + * @host: Device which owns the bus (generally the SCSI controller) + * @info: structure defining callbacks etc for the controller + * @bus_name: Name to use for this bus + * + * This in-place initializes @bus as a new SCSI bus with a name + * provided by the caller. It is the caller's responsibility to make + * sure that name does not clash with the name of any other bus in the + * system. Unless you need the new bus to have a specific name, you + * should use scsi_bus_new() instead. + */ +void scsi_bus_init_named(SCSIBus *bus, size_t bus_size, DeviceState *host, + const SCSIBusInfo *info, const char *bus_name); + +/** + * scsi_bus_init: Initialize a SCSI bus + * + * This in-place-initializes @bus as a new SCSI bus and gives it + * an automatically generated unique name. + */ +static inline void scsi_bus_init(SCSIBus *bus, size_t bus_size, + DeviceState *host, const SCSIBusInfo *info) +{ + scsi_bus_init_named(bus, bus_size, host, info, NULL); +} static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) { From 43417c0c27fd0851707a1f3bf50244d24aeeaf82 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 23 Sep 2021 13:11:49 +0100 Subject: [PATCH 0100/1334] ipack: Rename ipack_bus_new_inplace() to ipack_bus_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename ipack_bus_new_inplace() to ipack_bus_init(), to bring it in to line with a "_init for in-place init, _new for allocate-and-return" convention. Drop the 'name' argument, because the only caller does not pass in a name. If a future caller does need to specify the bus name, we should create an ipack_bus_init_named() function at that point. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Message-id: 20210923121153.23754-3-peter.maydell@linaro.org --- hw/ipack/ipack.c | 10 +++++----- hw/ipack/tpci200.c | 4 ++-- include/hw/ipack/ipack.h | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index f19ecaeb1c..d28e7f6449 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -30,12 +30,12 @@ IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot) return NULL; } -void ipack_bus_new_inplace(IPackBus *bus, size_t bus_size, - DeviceState *parent, - const char *name, uint8_t n_slots, - qemu_irq_handler handler) +void ipack_bus_init(IPackBus *bus, size_t bus_size, + DeviceState *parent, + uint8_t n_slots, + qemu_irq_handler handler) { - qbus_create_inplace(bus, bus_size, TYPE_IPACK_BUS, parent, name); + qbus_create_inplace(bus, bus_size, TYPE_IPACK_BUS, parent, NULL); bus->n_slots = n_slots; bus->set_irq = handler; } diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index d107e134c4..1f764fc85b 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -611,8 +611,8 @@ static void tpci200_realize(PCIDevice *pci_dev, Error **errp) pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2); pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3); - ipack_bus_new_inplace(&s->bus, sizeof(s->bus), DEVICE(pci_dev), NULL, - N_MODULES, tpci200_set_irq); + ipack_bus_init(&s->bus, sizeof(s->bus), DEVICE(pci_dev), + N_MODULES, tpci200_set_irq); } static const VMStateDescription vmstate_tpci200 = { diff --git a/include/hw/ipack/ipack.h b/include/hw/ipack/ipack.h index 75014e74ae..cbcdda509d 100644 --- a/include/hw/ipack/ipack.h +++ b/include/hw/ipack/ipack.h @@ -73,9 +73,9 @@ extern const VMStateDescription vmstate_ipack_device; VMSTATE_STRUCT(_field, _state, 1, vmstate_ipack_device, IPackDevice) IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot); -void ipack_bus_new_inplace(IPackBus *bus, size_t bus_size, - DeviceState *parent, - const char *name, uint8_t n_slots, - qemu_irq_handler handler); +void ipack_bus_init(IPackBus *bus, size_t bus_size, + DeviceState *parent, + uint8_t n_slots, + qemu_irq_handler handler); #endif From 8d4cdf01f89fd834b952df2dd08f55e897a72ea8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 23 Sep 2021 13:11:50 +0100 Subject: [PATCH 0101/1334] pci: Rename pci_root_bus_new_inplace() to pci_root_bus_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the pci_root_bus_new_inplace() function to pci_root_bus_init(); this brings the bus type in to line with a "_init for in-place init, _new for allocate-and-return" convention. To do this we need to rename the implementation-internal function that was using the pci_root_bus_init() name to pci_root_bus_internal_init(). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Message-id: 20210923121153.23754-4-peter.maydell@linaro.org --- hw/pci-host/raven.c | 4 ++-- hw/pci-host/versatile.c | 6 +++--- hw/pci/pci.c | 26 +++++++++++++------------- include/hw/pci/pci.h | 10 +++++----- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 3be27f0a14..6e514f75eb 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -300,8 +300,8 @@ static void raven_pcihost_initfn(Object *obj) memory_region_add_subregion_overlap(address_space_mem, PCI_IO_BASE_ADDR, &s->pci_io_non_contiguous, 1); memory_region_add_subregion(address_space_mem, 0xc0000000, &s->pci_memory); - pci_root_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), NULL, - &s->pci_memory, &s->pci_io, 0, TYPE_PCI_BUS); + pci_root_bus_init(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), NULL, + &s->pci_memory, &s->pci_io, 0, TYPE_PCI_BUS); /* Bus master address space */ memory_region_init(&s->bm, obj, "bm-raven", 4 * GiB); diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 3553277f94..f66384fa02 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -405,9 +405,9 @@ static void pci_vpb_realize(DeviceState *dev, Error **errp) memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 4 * GiB); memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 4 * GiB); - pci_root_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), dev, "pci", - &s->pci_mem_space, &s->pci_io_space, - PCI_DEVFN(11, 0), TYPE_PCI_BUS); + pci_root_bus_init(&s->pci_bus, sizeof(s->pci_bus), dev, "pci", + &s->pci_mem_space, &s->pci_io_space, + PCI_DEVFN(11, 0), TYPE_PCI_BUS); h->bus = &s->pci_bus; object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 23d2ae2ab2..19881c84f2 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -432,10 +432,10 @@ bool pci_bus_bypass_iommu(PCIBus *bus) return host_bridge->bypass_iommu; } -static void pci_root_bus_init(PCIBus *bus, DeviceState *parent, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min) +static void pci_root_bus_internal_init(PCIBus *bus, DeviceState *parent, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min) { assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; @@ -460,15 +460,15 @@ bool pci_bus_is_express(PCIBus *bus) return object_dynamic_cast(OBJECT(bus), TYPE_PCIE_BUS); } -void pci_root_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent, - const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min, const char *typename) +void pci_root_bus_init(PCIBus *bus, size_t bus_size, DeviceState *parent, + const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min, const char *typename) { qbus_create_inplace(bus, bus_size, typename, parent, name); - pci_root_bus_init(bus, parent, address_space_mem, address_space_io, - devfn_min); + pci_root_bus_internal_init(bus, parent, address_space_mem, + address_space_io, devfn_min); } PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, @@ -479,8 +479,8 @@ PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, PCIBus *bus; bus = PCI_BUS(qbus_create(typename, parent, name)); - pci_root_bus_init(bus, parent, address_space_mem, address_space_io, - devfn_min); + pci_root_bus_internal_init(bus, parent, address_space_mem, + address_space_io, devfn_min); return bus; } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index d0f4266e37..7fc90132cf 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -403,11 +403,11 @@ OBJECT_DECLARE_TYPE(PCIBus, PCIBusClass, PCI_BUS) bool pci_bus_is_express(PCIBus *bus); -void pci_root_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent, - const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min, const char *typename); +void pci_root_bus_init(PCIBus *bus, size_t bus_size, DeviceState *parent, + const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min, const char *typename); PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, From d637e1dc6de0e171dca6fbb5384668c642aa5ab6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 23 Sep 2021 13:11:51 +0100 Subject: [PATCH 0102/1334] qbus: Rename qbus_create_inplace() to qbus_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename qbus_create_inplace() to qbus_init(); this is more in line with our usual naming convention for functions that in-place initialize objects. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Message-id: 20210923121153.23754-5-peter.maydell@linaro.org --- hw/audio/intel-hda.c | 2 +- hw/block/fdc.c | 2 +- hw/block/swim.c | 3 +-- hw/char/virtio-serial-bus.c | 4 ++-- hw/core/bus.c | 11 ++++++----- hw/core/sysbus.c | 10 ++++++---- hw/gpio/bcm2835_gpio.c | 3 +-- hw/ide/qdev.c | 2 +- hw/ipack/ipack.c | 2 +- hw/misc/mac_via.c | 4 ++-- hw/misc/macio/cuda.c | 4 ++-- hw/misc/macio/macio.c | 4 ++-- hw/misc/macio/pmu.c | 4 ++-- hw/nubus/nubus-bridge.c | 2 +- hw/nvme/ctrl.c | 4 ++-- hw/nvme/subsys.c | 3 +-- hw/pci/pci.c | 2 +- hw/pci/pci_bridge.c | 4 ++-- hw/s390x/event-facility.c | 4 ++-- hw/s390x/virtio-ccw.c | 3 +-- hw/scsi/scsi-bus.c | 2 +- hw/sd/allwinner-sdhost.c | 4 ++-- hw/sd/bcm2835_sdhost.c | 4 ++-- hw/sd/pl181.c | 3 +-- hw/sd/pxa2xx_mmci.c | 4 ++-- hw/sd/sdhci.c | 3 +-- hw/sd/ssi-sd.c | 3 +-- hw/usb/bus.c | 2 +- hw/usb/dev-smartcard-reader.c | 3 +-- hw/virtio/virtio-mmio.c | 3 +-- hw/virtio/virtio-pci.c | 3 +-- include/hw/qdev-core.h | 4 ++-- 32 files changed, 54 insertions(+), 61 deletions(-) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 4330213fff..8ce9df64e3 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -52,7 +52,7 @@ void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, size_t bus_size, hda_codec_response_func response, hda_codec_xfer_func xfer) { - qbus_create_inplace(bus, bus_size, TYPE_HDA_BUS, dev, NULL); + qbus_init(bus, bus_size, TYPE_HDA_BUS, dev, NULL); bus->response = response; bus->xfer = xfer; } diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 9014cd30b3..fa933cd326 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -77,7 +77,7 @@ static const TypeInfo floppy_bus_info = { static void floppy_bus_create(FDCtrl *fdc, FloppyBus *bus, DeviceState *dev) { - qbus_create_inplace(bus, sizeof(FloppyBus), TYPE_FLOPPY_BUS, dev, NULL); + qbus_init(bus, sizeof(FloppyBus), TYPE_FLOPPY_BUS, dev, NULL); bus->fdc = fdc; } diff --git a/hw/block/swim.c b/hw/block/swim.c index 509c2f4900..333da08ce0 100644 --- a/hw/block/swim.c +++ b/hw/block/swim.c @@ -421,8 +421,7 @@ static void sysbus_swim_realize(DeviceState *dev, Error **errp) Swim *sys = SWIM(dev); SWIMCtrl *swimctrl = &sys->ctrl; - qbus_create_inplace(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev, - NULL); + qbus_init(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev, NULL); swimctrl->bus.ctrl = swimctrl; } diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index dd6bc27b3b..f01ec2137c 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -1048,8 +1048,8 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) config_size); /* Spawn a new virtio-serial bus on which the ports will ride as devices */ - qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS, - dev, vdev->bus_name); + qbus_init(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS, + dev, vdev->bus_name); qbus_set_hotplug_handler(BUS(&vser->bus), OBJECT(vser)); vser->bus.vser = vser; QTAILQ_INIT(&vser->ports); diff --git a/hw/core/bus.c b/hw/core/bus.c index 9cfbc3a687..cec4998502 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -99,7 +99,8 @@ static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb, } } -static void qbus_init(BusState *bus, DeviceState *parent, const char *name) +static void qbus_init_internal(BusState *bus, DeviceState *parent, + const char *name) { const char *typename = object_get_typename(OBJECT(bus)); BusClass *bc; @@ -151,11 +152,11 @@ static void bus_unparent(Object *obj) bus->parent = NULL; } -void qbus_create_inplace(void *bus, size_t size, const char *typename, - DeviceState *parent, const char *name) +void qbus_init(void *bus, size_t size, const char *typename, + DeviceState *parent, const char *name) { object_initialize(bus, size, typename); - qbus_init(bus, parent, name); + qbus_init_internal(bus, parent, name); } BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) @@ -163,7 +164,7 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam BusState *bus; bus = BUS(object_new(typename)); - qbus_init(bus, parent, name); + qbus_init_internal(bus, parent, name); return bus; } diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index aaae8e23cc..05c1da3d31 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -340,11 +340,13 @@ static BusState *main_system_bus; static void main_system_bus_create(void) { - /* assign main_system_bus before qbus_create_inplace() - * in order to make "if (bus != sysbus_get_default())" work */ + /* + * assign main_system_bus before qbus_init() + * in order to make "if (bus != sysbus_get_default())" work + */ main_system_bus = g_malloc0(system_bus_info.instance_size); - qbus_create_inplace(main_system_bus, system_bus_info.instance_size, - TYPE_SYSTEM_BUS, NULL, "main-system-bus"); + qbus_init(main_system_bus, system_bus_info.instance_size, + TYPE_SYSTEM_BUS, NULL, "main-system-bus"); OBJECT(main_system_bus)->free = g_free; } diff --git a/hw/gpio/bcm2835_gpio.c b/hw/gpio/bcm2835_gpio.c index abdddbc67c..c995bba1d9 100644 --- a/hw/gpio/bcm2835_gpio.c +++ b/hw/gpio/bcm2835_gpio.c @@ -299,8 +299,7 @@ static void bcm2835_gpio_init(Object *obj) DeviceState *dev = DEVICE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), - TYPE_SD_BUS, DEVICE(s), "sd-bus"); + qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(s), "sd-bus"); memory_region_init_io(&s->iomem, obj, &bcm2835_gpio_ops, s, "bcm2835_gpio", 0x1000); diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index e70ebc83a0..ca84441910 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -71,7 +71,7 @@ static const TypeInfo ide_bus_info = { void ide_bus_new(IDEBus *idebus, size_t idebus_size, DeviceState *dev, int bus_id, int max_units) { - qbus_create_inplace(idebus, idebus_size, TYPE_IDE_BUS, dev, NULL); + qbus_init(idebus, idebus_size, TYPE_IDE_BUS, dev, NULL); idebus->bus_id = bus_id; idebus->max_units = max_units; } diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index d28e7f6449..ae20f36da6 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -35,7 +35,7 @@ void ipack_bus_init(IPackBus *bus, size_t bus_size, uint8_t n_slots, qemu_irq_handler handler) { - qbus_create_inplace(bus, bus_size, TYPE_IPACK_BUS, parent, NULL); + qbus_init(bus, bus_size, TYPE_IPACK_BUS, parent, NULL); bus->n_slots = n_slots; bus->set_irq = handler; } diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index d1abcd97b5..993bac017d 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -1038,8 +1038,8 @@ static void mos6522_q800_via1_init(Object *obj) sysbus_init_mmio(sbd, &v1s->via_mem); /* ADB */ - qbus_create_inplace((BusState *)&v1s->adb_bus, sizeof(v1s->adb_bus), - TYPE_ADB_BUS, DEVICE(v1s), "adb.0"); + qbus_init((BusState *)&v1s->adb_bus, sizeof(v1s->adb_bus), + TYPE_ADB_BUS, DEVICE(v1s), "adb.0"); qdev_init_gpio_in(DEVICE(obj), via1_irq_request, VIA1_IRQ_NB); } diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index edbd4186b2..e917a6a095 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -553,8 +553,8 @@ static void cuda_init(Object *obj) memory_region_init_io(&s->mem, obj, &mos6522_cuda_ops, s, "cuda", 0x2000); sysbus_init_mmio(sbd, &s->mem); - qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, - DEVICE(obj), "adb.0"); + qbus_init(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, + DEVICE(obj), "adb.0"); } static Property cuda_properties[] = { diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 4515296e66..c1fad43f6c 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -387,8 +387,8 @@ static void macio_instance_init(Object *obj) memory_region_init(&s->bar, obj, "macio", 0x80000); - qbus_create_inplace(&s->macio_bus, sizeof(s->macio_bus), TYPE_MACIO_BUS, - DEVICE(obj), "macio.0"); + qbus_init(&s->macio_bus, sizeof(s->macio_bus), TYPE_MACIO_BUS, + DEVICE(obj), "macio.0"); object_initialize_child(OBJECT(s), "dbdma", &s->dbdma, TYPE_MAC_DBDMA); diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 71924d4768..4ad4f50e08 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -754,8 +754,8 @@ static void pmu_realize(DeviceState *dev, Error **errp) timer_mod(s->one_sec_timer, s->one_sec_target); if (s->has_adb) { - qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, - dev, "adb.0"); + qbus_init(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, + dev, "adb.0"); adb_register_autopoll_callback(adb_bus, pmu_adb_poll, s); } } diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index c517a8a704..a42c86080f 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -18,7 +18,7 @@ static void nubus_bridge_init(Object *obj) NubusBridge *s = NUBUS_BRIDGE(obj); NubusBus *bus = &s->bus; - qbus_create_inplace(bus, sizeof(s->bus), TYPE_NUBUS_BUS, DEVICE(s), NULL); + qbus_init(bus, sizeof(s->bus), TYPE_NUBUS_BUS, DEVICE(s), NULL); qdev_init_gpio_out(DEVICE(s), bus->irqs, NUBUS_IRQS); } diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 2f247a9275..6a571d18cf 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -6539,8 +6539,8 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) return; } - qbus_create_inplace(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, - &pci_dev->qdev, n->parent_obj.qdev.id); + qbus_init(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, + &pci_dev->qdev, n->parent_obj.qdev.id); nvme_init_state(n); if (nvme_init_pci(n, pci_dev, errp)) { diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 93c35950d6..495dcff5eb 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -50,8 +50,7 @@ static void nvme_subsys_realize(DeviceState *dev, Error **errp) { NvmeSubsystem *subsys = NVME_SUBSYS(dev); - qbus_create_inplace(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, - dev->id); + qbus_init(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); nvme_subsys_setup(subsys); } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 19881c84f2..14cb15a0aa 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -466,7 +466,7 @@ void pci_root_bus_init(PCIBus *bus, size_t bus_size, DeviceState *parent, MemoryRegion *address_space_io, uint8_t devfn_min, const char *typename) { - qbus_create_inplace(bus, bus_size, typename, parent, name); + qbus_init(bus, bus_size, typename, parent, name); pci_root_bus_internal_init(bus, parent, address_space_mem, address_space_io, devfn_min); } diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 3789c17edc..d1f902ee86 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -374,8 +374,8 @@ void pci_bridge_initfn(PCIDevice *dev, const char *typename) br->bus_name = dev->qdev.id; } - qbus_create_inplace(sec_bus, sizeof(br->sec_bus), typename, DEVICE(dev), - br->bus_name); + qbus_init(sec_bus, sizeof(br->sec_bus), typename, DEVICE(dev), + br->bus_name); sec_bus->parent_dev = dev; sec_bus->map_irq = br->map_irq ? br->map_irq : pci_swizzle_map_irq_fn; sec_bus->address_space_mem = &br->address_space_mem; diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index ed92ce510d..6fa47b889c 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -427,8 +427,8 @@ static void init_event_facility(Object *obj) sclp_event_set_allow_all_mask_sizes); /* Spawn a new bus for SCLP events */ - qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus), - TYPE_SCLP_EVENTS_BUS, sdev, NULL); + qbus_init(&event_facility->sbus, sizeof(event_facility->sbus), + TYPE_SCLP_EVENTS_BUS, sdev, NULL); object_initialize_child(obj, TYPE_SCLP_QUIESCE, &event_facility->quiesce, diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 6a2df1c1e9..c845a92c3a 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1261,8 +1261,7 @@ static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, DeviceState *qdev = DEVICE(dev); char virtio_bus_name[] = "virtio-bus"; - qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_CCW_BUS, - qdev, virtio_bus_name); + qbus_init(bus, bus_size, TYPE_VIRTIO_CCW_BUS, qdev, virtio_bus_name); } static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index e28a6ea053..77325d8cc7 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -137,7 +137,7 @@ void scsi_device_unit_attention_reported(SCSIDevice *s) void scsi_bus_init_named(SCSIBus *bus, size_t bus_size, DeviceState *host, const SCSIBusInfo *info, const char *bus_name) { - qbus_create_inplace(bus, bus_size, TYPE_SCSI_BUS, host, bus_name); + qbus_init(bus, bus_size, TYPE_SCSI_BUS, host, bus_name); bus->busnr = next_scsi_bus++; bus->info = info; qbus_set_bus_hotplug_handler(BUS(bus)); diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index bea6d97ef8..9166d6638d 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -738,8 +738,8 @@ static void allwinner_sdhost_init(Object *obj) { AwSdHostState *s = AW_SDHOST(obj); - qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), - TYPE_AW_SDHOST_BUS, DEVICE(s), "sd-bus"); + qbus_init(&s->sdbus, sizeof(s->sdbus), + TYPE_AW_SDHOST_BUS, DEVICE(s), "sd-bus"); memory_region_init_io(&s->iomem, obj, &allwinner_sdhost_ops, s, TYPE_AW_SDHOST, 4 * KiB); diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 50f5fdb88b..088a7ac6ed 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -403,8 +403,8 @@ static void bcm2835_sdhost_init(Object *obj) { BCM2835SDHostState *s = BCM2835_SDHOST(obj); - qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), - TYPE_BCM2835_SDHOST_BUS, DEVICE(s), "sd-bus"); + qbus_init(&s->sdbus, sizeof(s->sdbus), + TYPE_BCM2835_SDHOST_BUS, DEVICE(s), "sd-bus"); memory_region_init_io(&s->iomem, obj, &bcm2835_sdhost_ops, s, TYPE_BCM2835_SDHOST, 0x1000); diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 960f155098..5e554bd467 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -506,8 +506,7 @@ static void pl181_init(Object *obj) qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1); qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1); - qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), - TYPE_PL181_BUS, dev, "sd-bus"); + qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_PL181_BUS, dev, "sd-bus"); } static void pl181_class_init(ObjectClass *klass, void *data) diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index 3dd2fc7a83..124fbf8bbd 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -560,8 +560,8 @@ static void pxa2xx_mmci_instance_init(Object *obj) qdev_init_gpio_out_named(dev, &s->rx_dma, "rx-dma", 1); qdev_init_gpio_out_named(dev, &s->tx_dma, "tx-dma", 1); - qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), - TYPE_PXA2XX_MMCI_BUS, DEVICE(obj), "sd-bus"); + qbus_init(&s->sdbus, sizeof(s->sdbus), + TYPE_PXA2XX_MMCI_BUS, DEVICE(obj), "sd-bus"); } static void pxa2xx_mmci_class_init(ObjectClass *klass, void *data) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 5b8678110b..c9dc065cc5 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1337,8 +1337,7 @@ static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp) void sdhci_initfn(SDHCIState *s) { - qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), - TYPE_SDHCI_BUS, DEVICE(s), "sd-bus"); + qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SDHCI_BUS, DEVICE(s), "sd-bus"); s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s); s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s); diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 97ee58e20c..e60854eeef 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -373,8 +373,7 @@ static void ssi_sd_realize(SSIPeripheral *d, Error **errp) DeviceState *carddev; DriveInfo *dinfo; - qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, - DEVICE(d), "sd-bus"); + qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(d), "sd-bus"); /* Create and plug in the sd card */ /* FIXME use a qdev drive property instead of drive_get_next() */ diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 07083349f5..5d441a7065 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -82,7 +82,7 @@ const VMStateDescription vmstate_usb_device = { void usb_bus_new(USBBus *bus, size_t bus_size, USBBusOps *ops, DeviceState *host) { - qbus_create_inplace(bus, bus_size, TYPE_USB_BUS, host, NULL); + qbus_init(bus, bus_size, TYPE_USB_BUS, host, NULL); qbus_set_bus_hotplug_handler(BUS(bus)); bus->ops = ops; bus->busnr = next_usb_bus++; diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index f7043075be..91ffd9f8ae 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1320,8 +1320,7 @@ static void ccid_realize(USBDevice *dev, Error **errp) usb_desc_create_serial(dev); usb_desc_init(dev); - qbus_create_inplace(&s->bus, sizeof(s->bus), TYPE_CCID_BUS, DEVICE(dev), - NULL); + qbus_init(&s->bus, sizeof(s->bus), TYPE_CCID_BUS, DEVICE(dev), NULL); qbus_set_hotplug_handler(BUS(&s->bus), OBJECT(dev)); s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP); s->bulk = usb_ep_get(dev, USB_TOKEN_IN, CCID_BULK_IN_EP); diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 1af48a1b04..7b3ebca178 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -733,8 +733,7 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp) VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); SysBusDevice *sbd = SYS_BUS_DEVICE(d); - qbus_create_inplace(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, - d, NULL); + qbus_init(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, d, NULL); sysbus_init_irq(sbd, &proxy->irq); if (!kvm_eventfds_enabled()) { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 6e16e2705c..750aa47ec1 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2187,8 +2187,7 @@ static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, DeviceState *qdev = DEVICE(dev); char virtio_bus_name[] = "virtio-bus"; - qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_PCI_BUS, qdev, - virtio_bus_name); + qbus_init(bus, bus_size, TYPE_VIRTIO_PCI_BUS, qdev, virtio_bus_name); } static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 34c8a7506a..ebca8cf9fc 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -678,8 +678,8 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id); typedef int (qbus_walkerfn)(BusState *bus, void *opaque); typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque); -void qbus_create_inplace(void *bus, size_t size, const char *typename, - DeviceState *parent, const char *name); +void qbus_init(void *bus, size_t size, const char *typename, + DeviceState *parent, const char *name); BusState *qbus_create(const char *typename, DeviceState *parent, const char *name); bool qbus_realize(BusState *bus, Error **errp); void qbus_unrealize(BusState *bus); From 9388d1701efa87095d31ed5f68793dfc82cdd47e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 23 Sep 2021 13:11:52 +0100 Subject: [PATCH 0103/1334] qbus: Rename qbus_create() to qbus_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the "allocate and return" qbus creation function to qbus_new(), to bring it into line with our _init vs _new convention. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Reviewed-by: Corey Minyard Message-id: 20210923121153.23754-6-peter.maydell@linaro.org --- hw/core/bus.c | 2 +- hw/hyperv/vmbus.c | 2 +- hw/i2c/core.c | 2 +- hw/isa/isa-bus.c | 2 +- hw/misc/auxbus.c | 2 +- hw/pci/pci.c | 2 +- hw/ppc/spapr_vio.c | 2 +- hw/s390x/ap-bridge.c | 2 +- hw/s390x/css-bridge.c | 2 +- hw/s390x/s390-pci-bus.c | 2 +- hw/ssi/ssi.c | 2 +- hw/xen/xen-bus.c | 2 +- hw/xen/xen-legacy-backend.c | 2 +- include/hw/qdev-core.h | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/hw/core/bus.c b/hw/core/bus.c index cec4998502..c7831b5293 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -159,7 +159,7 @@ void qbus_init(void *bus, size_t size, const char *typename, qbus_init_internal(bus, parent, name); } -BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) +BusState *qbus_new(const char *typename, DeviceState *parent, const char *name) { BusState *bus; diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index c9887d5a7b..dbce3b35fb 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2729,7 +2729,7 @@ static void vmbus_bridge_realize(DeviceState *dev, Error **errp) return; } - bridge->bus = VMBUS(qbus_create(TYPE_VMBUS, dev, "vmbus")); + bridge->bus = VMBUS(qbus_new(TYPE_VMBUS, dev, "vmbus")); } static char *vmbus_bridge_ofw_unit_address(const SysBusDevice *dev) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 416372ad00..0e7d2763b9 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -60,7 +60,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) { I2CBus *bus; - bus = I2C_BUS(qbus_create(TYPE_I2C_BUS, parent, name)); + bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name)); QLIST_INIT(&bus->current_devs); vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus); return bus; diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index cffaa35e9c..6c31398dda 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -64,7 +64,7 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space, sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); } - isabus = ISA_BUS(qbus_create(TYPE_ISA_BUS, dev, NULL)); + isabus = ISA_BUS(qbus_new(TYPE_ISA_BUS, dev, NULL)); isabus->address_space = address_space; isabus->address_space_io = address_space_io; return isabus; diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index 434ff8d910..8a8012f5f0 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -65,7 +65,7 @@ AUXBus *aux_bus_init(DeviceState *parent, const char *name) AUXBus *bus; Object *auxtoi2c; - bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name)); + bus = AUX_BUS(qbus_new(TYPE_AUX_BUS, parent, name)); auxtoi2c = object_new_with_props(TYPE_AUXTOI2C, OBJECT(bus), "i2c", &error_abort, NULL); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 14cb15a0aa..186758ee11 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -478,7 +478,7 @@ PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, { PCIBus *bus; - bus = PCI_BUS(qbus_create(typename, parent, name)); + bus = PCI_BUS(qbus_new(typename, parent, name)); pci_root_bus_internal_init(bus, parent, address_space_mem, address_space_io, devfn_min); return bus; diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index b59452bcd6..b975ed29ca 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -577,7 +577,7 @@ SpaprVioBus *spapr_vio_bus_init(void) sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); /* Create bus on bridge device */ - qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio"); + qbus = qbus_new(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio"); bus = SPAPR_VIO_BUS(qbus); bus->next_reg = SPAPR_VIO_REG_BASE; diff --git a/hw/s390x/ap-bridge.c b/hw/s390x/ap-bridge.c index 8bcf8ece9d..ef8fa2b15b 100644 --- a/hw/s390x/ap-bridge.c +++ b/hw/s390x/ap-bridge.c @@ -55,7 +55,7 @@ void s390_init_ap(void) sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); /* Create bus on bridge device */ - bus = qbus_create(TYPE_AP_BUS, dev, TYPE_AP_BUS); + bus = qbus_new(TYPE_AP_BUS, dev, TYPE_AP_BUS); /* Enable hotplugging */ qbus_set_hotplug_handler(bus, OBJECT(dev)); diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 191b29f077..4017081d49 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -106,7 +106,7 @@ VirtualCssBus *virtual_css_bus_init(void) sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); /* Create bus on bridge device */ - bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); + bus = qbus_new(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); cbus = VIRTUAL_CSS_BUS(bus); /* Enable hotplugging */ diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 6c0225c3a0..6fafffb029 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -813,7 +813,7 @@ static void s390_pcihost_realize(DeviceState *dev, Error **errp) qbus_set_hotplug_handler(bus, OBJECT(dev)); phb->bus = b; - s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, dev, NULL)); + s->bus = S390_PCI_BUS(qbus_new(TYPE_S390_PCI_BUS, dev, NULL)); qbus_set_hotplug_handler(BUS(s->bus), OBJECT(dev)); s->iommu_table = g_hash_table_new_full(g_int64_hash, g_int64_equal, diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index e5d7ce9523..003931fb50 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -107,7 +107,7 @@ DeviceState *ssi_create_peripheral(SSIBus *bus, const char *name) SSIBus *ssi_create_bus(DeviceState *parent, const char *name) { BusState *bus; - bus = qbus_create(TYPE_SSI_BUS, parent, name); + bus = qbus_new(TYPE_SSI_BUS, parent, name); return SSI_BUS(bus); } diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 8c588920d9..416583f130 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -1398,7 +1398,7 @@ type_init(xen_register_types) void xen_bus_init(void) { DeviceState *dev = qdev_new(TYPE_XEN_BRIDGE); - BusState *bus = qbus_create(TYPE_XEN_BUS, dev, NULL); + BusState *bus = qbus_new(TYPE_XEN_BUS, dev, NULL); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); qbus_set_bus_hotplug_handler(bus); diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index dd8ae1452d..be3cf4a195 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -702,7 +702,7 @@ int xen_be_init(void) xen_sysdev = qdev_new(TYPE_XENSYSDEV); sysbus_realize_and_unref(SYS_BUS_DEVICE(xen_sysdev), &error_fatal); - xen_sysbus = qbus_create(TYPE_XENSYSBUS, xen_sysdev, "xen-sysbus"); + xen_sysbus = qbus_new(TYPE_XENSYSBUS, xen_sysdev, "xen-sysbus"); qbus_set_bus_hotplug_handler(xen_sysbus); return 0; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index ebca8cf9fc..4ff19c714b 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -680,7 +680,7 @@ typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque); void qbus_init(void *bus, size_t size, const char *typename, DeviceState *parent, const char *name); -BusState *qbus_create(const char *typename, DeviceState *parent, const char *name); +BusState *qbus_new(const char *typename, DeviceState *parent, const char *name); bool qbus_realize(BusState *bus, Error **errp); void qbus_unrealize(BusState *bus); From 82c74ac42e1b9e564a3c011dca6215d130b7e6a0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 23 Sep 2021 13:11:53 +0100 Subject: [PATCH 0104/1334] ide: Rename ide_bus_new() to ide_bus_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function ide_bus_new() does an in-place initialization. Rename it to ide_bus_init() to follow our _init vs _new convention. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Reviewed-by: Corey Minyard Reviewed-by: John Snow Acked-by: John Snow (Feel free to merge.) Message-id: 20210923121153.23754-7-peter.maydell@linaro.org --- hw/ide/ahci.c | 2 +- hw/ide/cmd646.c | 2 +- hw/ide/isa.c | 2 +- hw/ide/macio.c | 2 +- hw/ide/microdrive.c | 2 +- hw/ide/mmio.c | 2 +- hw/ide/piix.c | 2 +- hw/ide/qdev.c | 2 +- hw/ide/sii3112.c | 2 +- hw/ide/via.c | 2 +- include/hw/ide/internal.h | 4 ++-- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index f2c5157483..a94c6e26fb 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1548,7 +1548,7 @@ void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) for (i = 0; i < s->ports; i++) { AHCIDevice *ad = &s->dev[i]; - ide_bus_new(&ad->port, sizeof(ad->port), qdev, i, 1); + ide_bus_init(&ad->port, sizeof(ad->port), qdev, i, 1); ide_init2(&ad->port, irqs[i]); ad->hba = s; diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index c254631485..94c576262c 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -293,7 +293,7 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) qdev_init_gpio_in(ds, cmd646_set_irq, 2); for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); + ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); ide_init2(&d->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&d->bus[i], &d->bmdma[i], d); diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 6bc19de226..24bbde24c2 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -73,7 +73,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ISADevice *isadev = ISA_DEVICE(dev); ISAIDEState *s = ISA_IDE(dev); - ide_bus_new(&s->bus, sizeof(s->bus), dev, 0, 2); + ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2); ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); isa_init_irq(isadev, &s->irq, s->isairq); ide_init2(&s->bus, s->irq); diff --git a/hw/ide/macio.c b/hw/ide/macio.c index b270a10163..b03d401ceb 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -449,7 +449,7 @@ static void macio_ide_initfn(Object *obj) SysBusDevice *d = SYS_BUS_DEVICE(obj); MACIOIDEState *s = MACIO_IDE(obj); - ide_bus_new(&s->bus, sizeof(s->bus), DEVICE(obj), 0, 2); + ide_bus_init(&s->bus, sizeof(s->bus), DEVICE(obj), 0, 2); memory_region_init_io(&s->mem, obj, &pmac_ide_ops, s, "pmac-ide", 0x1000); sysbus_init_mmio(d, &s->mem); sysbus_init_irq(d, &s->real_ide_irq); diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 58a14fea36..6df9b4cbbe 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -605,7 +605,7 @@ static void microdrive_init(Object *obj) { MicroDriveState *md = MICRODRIVE(obj); - ide_bus_new(&md->bus, sizeof(md->bus), DEVICE(obj), 0, 1); + ide_bus_init(&md->bus, sizeof(md->bus), DEVICE(obj), 0, 1); } static void microdrive_class_init(ObjectClass *oc, void *data) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 36e2f4790a..fb2ebd4847 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -142,7 +142,7 @@ static void mmio_ide_initfn(Object *obj) SysBusDevice *d = SYS_BUS_DEVICE(obj); MMIOState *s = MMIO_IDE(obj); - ide_bus_new(&s->bus, sizeof(s->bus), DEVICE(obj), 0, 2); + ide_bus_init(&s->bus, sizeof(s->bus), DEVICE(obj), 0, 2); sysbus_init_irq(d, &s->irq); } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index d3e738320b..ce89fd0aa3 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -137,7 +137,7 @@ static int pci_piix_init_ports(PCIIDEState *d) int i, ret; for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); + ide_bus_init(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); ret = ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, port_info[i].iobase2); if (ret) { diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index ca84441910..618045b85a 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -68,7 +68,7 @@ static const TypeInfo ide_bus_info = { .class_init = ide_bus_class_init, }; -void ide_bus_new(IDEBus *idebus, size_t idebus_size, DeviceState *dev, +void ide_bus_init(IDEBus *idebus, size_t idebus_size, DeviceState *dev, int bus_id, int max_units) { qbus_init(idebus, idebus_size, TYPE_IDE_BUS, dev, NULL); diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index 34c347b9c2..46204f10d7 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -283,7 +283,7 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp) qdev_init_gpio_in(ds, sii3112_set_irq, 2); for (i = 0; i < 2; i++) { - ide_bus_new(&s->bus[i], sizeof(s->bus[i]), ds, i, 1); + ide_bus_init(&s->bus[i], sizeof(s->bus[i]), ds, i, 1); ide_init2(&s->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&s->bus[i], &s->bmdma[i], s); diff --git a/hw/ide/via.c b/hw/ide/via.c index be09912b33..94cc2142c7 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -190,7 +190,7 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) qdev_init_gpio_in(ds, via_ide_set_irq, 2); for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); + ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); ide_init2(&d->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&d->bus[i], &d->bmdma[i], d); diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 79172217cc..97e7e59dc5 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -648,8 +648,8 @@ void ide_atapi_cmd(IDEState *s); void ide_atapi_cmd_reply_end(IDEState *s); /* hw/ide/qdev.c */ -void ide_bus_new(IDEBus *idebus, size_t idebus_size, DeviceState *dev, - int bus_id, int max_units); +void ide_bus_init(IDEBus *idebus, size_t idebus_size, DeviceState *dev, + int bus_id, int max_units); IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive); int ide_handle_rw_error(IDEState *s, int error, int op); From 1f4b2ec701b9d73d3fa7bb90c8b4376bc7d3c42b Mon Sep 17 00:00:00 2001 From: Xuzhou Cheng Date: Mon, 27 Sep 2021 22:28:25 +0800 Subject: [PATCH 0105/1334] hw/arm: sabrelite: Connect SPI flash CS line to GPIO3_19 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Linux spi-imx driver does not work on QEMU. The reason is that the state of m25p80 loops in STATE_READING_DATA state after receiving RDSR command, the new command is ignored. Before sending a new command, CS line should be pulled high to make the state of m25p80 back to IDLE. Currently the SPI flash CS line is connected to the SPI controller, but on the real board, it's connected to GPIO3_19. This matches the ecspi1 device node in the board dts. ecspi1 node in imx6qdl-sabrelite.dtsi: &ecspi1 { cs-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ecspi1>; status = "okay"; flash: m25p80@0 { compatible = "sst,sst25vf016b", "jedec,spi-nor"; spi-max-frequency = <20000000>; reg = <0>; }; }; Should connect the SSI_GPIO_CS to GPIO3_19 when adding a spi-nor to spi1 on sabrelite machine. Verified this patch on Linux v5.14. Logs: # echo "01234567899876543210" > test # mtd_debug erase /dev/mtd0 0x0 0x1000 Erased 4096 bytes from address 0x00000000 in flash # mtd_debug write /dev/mtdblock0 0x0 20 test Copied 20 bytes from test to address 0x00000000 in flash # mtd_debug read /dev/mtdblock0 0x0 20 test_out Copied 20 bytes from address 0x00000000 in flash to test_out # cat test_out 01234567899876543210# Signed-off-by: Xuzhou Cheng Reported-by: Guenter Roeck Reviewed-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210927142825.491-1-xchengl.cn@gmail.com Signed-off-by: Peter Maydell --- hw/arm/sabrelite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 29fc777b61..553608e583 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -87,7 +87,7 @@ static void sabrelite_init(MachineState *machine) qdev_realize_and_unref(flash_dev, BUS(spi_bus), &error_fatal); cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); - sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line); + qdev_connect_gpio_out(DEVICE(&s->gpio[2]), 19, cs_line); } } } From 56918a126ae25383cb0c2c74a6f0f784a6d59ac1 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:04 +0800 Subject: [PATCH 0106/1334] memory: Add RAM_PROTECTED flag to skip IOMMU mappings Add a new RAMBlock flag to denote "protected" memory, i.e. memory that looks and acts like RAM but is inaccessible via normal mechanisms, including DMA. Use the flag to skip protected memory regions when mapping RAM for DMA in VFIO. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Signed-off-by: Paolo Bonzini --- hw/vfio/common.c | 1 + include/exec/memory.h | 15 ++++++++++++++- softmmu/memory.c | 5 +++++ softmmu/physmem.c | 3 ++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 8728d4d5c2..1289cfa8be 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -562,6 +562,7 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section) { return (!memory_region_is_ram(section->mr) && !memory_region_is_iommu(section->mr)) || + memory_region_is_protected(section->mr) || /* * Sizing an enabled 64-bit BAR can cause spurious mappings to * addresses in the upper part of the 64-bit address space. These diff --git a/include/exec/memory.h b/include/exec/memory.h index c3d417d317..9446874d21 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -190,6 +190,9 @@ typedef struct IOMMUTLBEvent { */ #define RAM_NORESERVE (1 << 7) +/* RAM that isn't accessible through normal means. */ +#define RAM_PROTECTED (1 << 8) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, @@ -1267,7 +1270,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, * @name: the name of the region. * @size: size of the region. * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, - * RAM_NORESERVE. + * RAM_NORESERVE, RAM_PROTECTED. * @fd: the fd to mmap. * @offset: offset within the file referenced by fd * @errp: pointer to Error*, to store an error if it happens. @@ -1568,6 +1571,16 @@ static inline bool memory_region_is_romd(MemoryRegion *mr) return mr->rom_device && mr->romd_mode; } +/** + * memory_region_is_protected: check whether a memory region is protected + * + * Returns %true if a memory region is protected RAM and cannot be accessed + * via standard mechanisms, e.g. DMA. + * + * @mr: the memory region being queried + */ +bool memory_region_is_protected(MemoryRegion *mr); + /** * memory_region_get_iommu: check whether a memory region is an iommu * diff --git a/softmmu/memory.c b/softmmu/memory.c index bfedaf9c4d..54cd0e9824 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1811,6 +1811,11 @@ bool memory_region_is_ram_device(MemoryRegion *mr) return mr->ram_device; } +bool memory_region_is_protected(MemoryRegion *mr) +{ + return mr->ram && (mr->ram_block->flags & RAM_PROTECTED); +} + uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr) { uint8_t mask = mr->dirty_log_mask; diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 23e77cb771..088660d973 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -2055,7 +2055,8 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, int64_t file_size, file_align; /* Just support these ram flags by now. */ - assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE)) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE | + RAM_PROTECTED)) == 0); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); From 2f44bea907bb76adc511de5ba733c5164eedbce8 Mon Sep 17 00:00:00 2001 From: Yang Zhong Date: Mon, 19 Jul 2021 19:21:34 +0800 Subject: [PATCH 0107/1334] Kconfig: Add CONFIG_SGX support Add new CONFIG_SGX for sgx support in the Qemu, and the Kconfig default enable sgx in the i386 platform. Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-32-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- configs/devices/i386-softmmu/default.mak | 1 + hw/i386/Kconfig | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak index 84d1a2487c..598c6646df 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -22,6 +22,7 @@ #CONFIG_TPM_CRB=n #CONFIG_TPM_TIS_ISA=n #CONFIG_VTD=n +#CONFIG_SGX=n # Boards: # diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index ddedcef0b2..962d2c981b 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -6,6 +6,10 @@ config SEV select X86_FW_OVMF depends on KVM +config SGX + bool + depends on KVM + config PC bool imply APPLESMC @@ -21,6 +25,7 @@ config PC imply PVPANIC_ISA imply QXL imply SEV + imply SGX imply SGA imply TEST_DEVICES imply TPM_CRB From c6c023200045fc9d61574cb157d368707e18efd5 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:05 +0800 Subject: [PATCH 0108/1334] hostmem: Add hostmem-epc as a backend for SGX EPC EPC (Enclave Page Cahe) is a specialized type of memory used by Intel SGX (Software Guard Extensions). The SDM desribes EPC as: The Enclave Page Cache (EPC) is the secure storage used to store enclave pages when they are a part of an executing enclave. For an EPC page, hardware performs additional access control checks to restrict access to the page. After the current page access checks and translations are performed, the hardware checks that the EPC page is accessible to the program currently executing. Generally an EPC page is only accessed by the owner of the executing enclave or an instruction which is setting up an EPC page. Because of its unique requirements, Linux manages EPC separately from normal memory. Similar to memfd, the device /dev/sgx_vepc can be opened to obtain a file descriptor which can in turn be used to mmap() EPC memory. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-3-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- backends/hostmem-epc.c | 82 +++++++++++++++++++++++++++++++++++ backends/meson.build | 1 + include/hw/i386/hostmem-epc.h | 28 ++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 backends/hostmem-epc.c create mode 100644 include/hw/i386/hostmem-epc.h diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c new file mode 100644 index 0000000000..b47f98b6a3 --- /dev/null +++ b/backends/hostmem-epc.c @@ -0,0 +1,82 @@ +/* + * QEMU host SGX EPC memory backend + * + * Copyright (C) 2019 Intel Corporation + * + * Authors: + * Sean Christopherson + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qom/object_interfaces.h" +#include "qapi/error.h" +#include "sysemu/hostmem.h" +#include "hw/i386/hostmem-epc.h" + +static void +sgx_epc_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + uint32_t ram_flags; + char *name; + int fd; + + if (!backend->size) { + error_setg(errp, "can't create backend with size 0"); + return; + } + + fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); + if (fd < 0) { + error_setg_errno(errp, errno, + "failed to open /dev/sgx_vepc to alloc SGX EPC"); + return; + } + + name = object_get_canonical_path(OBJECT(backend)); + ram_flags = (backend->share ? RAM_SHARED : 0) | RAM_PROTECTED; + memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), + name, backend->size, ram_flags, + fd, 0, errp); + g_free(name); +} + +static void sgx_epc_backend_instance_init(Object *obj) +{ + HostMemoryBackend *m = MEMORY_BACKEND(obj); + + m->share = true; + m->merge = false; + m->dump = false; +} + +static void sgx_epc_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = sgx_epc_backend_memory_alloc; +} + +static const TypeInfo sgx_epc_backed_info = { + .name = TYPE_MEMORY_BACKEND_EPC, + .parent = TYPE_MEMORY_BACKEND, + .instance_init = sgx_epc_backend_instance_init, + .class_init = sgx_epc_backend_class_init, + .instance_size = sizeof(HostMemoryBackendEpc), +}; + +static void register_types(void) +{ + int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); + if (fd >= 0) { + close(fd); + + type_register_static(&sgx_epc_backed_info); + } +} + +type_init(register_types); diff --git a/backends/meson.build b/backends/meson.build index d4221831fc..6e68945528 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -16,5 +16,6 @@ softmmu_ss.add(when: ['CONFIG_VHOST_USER', 'CONFIG_VIRTIO'], if_true: files('vho softmmu_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost.c')) softmmu_ss.add(when: ['CONFIG_VIRTIO_CRYPTO', 'CONFIG_VHOST_CRYPTO'], if_true: files('cryptodev-vhost-user.c')) softmmu_ss.add(when: 'CONFIG_GIO', if_true: [files('dbus-vmstate.c'), gio]) +softmmu_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) subdir('tpm') diff --git a/include/hw/i386/hostmem-epc.h b/include/hw/i386/hostmem-epc.h new file mode 100644 index 0000000000..846c726085 --- /dev/null +++ b/include/hw/i386/hostmem-epc.h @@ -0,0 +1,28 @@ +/* + * SGX EPC backend + * + * Copyright (C) 2019 Intel Corporation + * + * Authors: + * Sean Christopherson + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_HOSTMEM_EPC_H +#define QEMU_HOSTMEM_EPC_H + +#include "sysemu/hostmem.h" + +#define TYPE_MEMORY_BACKEND_EPC "memory-backend-epc" + +#define MEMORY_BACKEND_EPC(obj) \ + OBJECT_CHECK(HostMemoryBackendEpc, (obj), TYPE_MEMORY_BACKEND_EPC) + +typedef struct HostMemoryBackendEpc HostMemoryBackendEpc; + +struct HostMemoryBackendEpc { + HostMemoryBackend parent_obj; +}; + +#endif From 46a1d21dbaafab2df25ac354095d90492cd3a98b Mon Sep 17 00:00:00 2001 From: Yang Zhong Date: Mon, 19 Jul 2021 19:21:06 +0800 Subject: [PATCH 0109/1334] qom: Add memory-backend-epc ObjectOptions support Add the new 'memory-backend-epc' user creatable QOM object in the ObjectOptions to support SGX since v6.1, or the sgx backend object cannot bootup. Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-4-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- qapi/qom.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/qapi/qom.json b/qapi/qom.json index a25616bc7a..0222bb4506 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -647,6 +647,23 @@ '*hugetlbsize': 'size', '*seal': 'bool' } } +## +# @MemoryBackendEpcProperties: +# +# Properties for memory-backend-epc objects. +# +# The @share boolean option is true by default with epc +# +# The @merge boolean option is false by default with epc +# +# The @dump boolean option is false by default with epc +# +# Since: 6.2 +## +{ 'struct': 'MemoryBackendEpcProperties', + 'base': 'MemoryBackendProperties', + 'data': {} } + ## # @PrManagerHelperProperties: # @@ -797,6 +814,7 @@ { 'name': 'memory-backend-memfd', 'if': 'CONFIG_LINUX' }, 'memory-backend-ram', + 'memory-backend-epc', 'pef-guest', 'pr-manager-helper', 'qtest', @@ -855,6 +873,7 @@ 'memory-backend-memfd': { 'type': 'MemoryBackendMemfdProperties', 'if': 'CONFIG_LINUX' }, 'memory-backend-ram': 'MemoryBackendProperties', + 'memory-backend-epc': 'MemoryBackendEpcProperties', 'pr-manager-helper': 'PrManagerHelperProperties', 'qtest': 'QtestProperties', 'rng-builtin': 'RngProperties', From 80509c5557a152f876aec524e8136f309583b2cd Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:07 +0800 Subject: [PATCH 0110/1334] i386: Add 'sgx-epc' device to expose EPC sections to guest SGX EPC is enumerated through CPUID, i.e. EPC "devices" need to be realized prior to realizing the vCPUs themselves, which occurs long before generic devices are parsed and realized. Because of this, do not allow 'sgx-epc' devices to be instantiated after vCPUS have been created. The 'sgx-epc' device is essentially a placholder at this time, it will be fully implemented in a future patch along with a dedicated command to create 'sgx-epc' devices. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-5-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/meson.build | 1 + hw/i386/sgx-epc.c | 167 ++++++++++++++++++++++++++++++++++++++ include/hw/i386/sgx-epc.h | 44 ++++++++++ 3 files changed, 212 insertions(+) create mode 100644 hw/i386/sgx-epc.c create mode 100644 include/hw/i386/sgx-epc.h diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 80dad29f2b..b1862c83d4 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -16,6 +16,7 @@ i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) i386_ss.add(when: 'CONFIG_VTD', if_true: files('intel_iommu.c')) +i386_ss.add(when: 'CONFIG_SGX', if_true: files('sgx-epc.c')) i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c')) i386_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device_x86.c')) diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c new file mode 100644 index 0000000000..c584acc17b --- /dev/null +++ b/hw/i386/sgx-epc.c @@ -0,0 +1,167 @@ +/* + * SGX EPC device + * + * Copyright (C) 2019 Intel Corporation + * + * Authors: + * Sean Christopherson + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "hw/i386/pc.h" +#include "hw/i386/sgx-epc.h" +#include "hw/mem/memory-device.h" +#include "hw/qdev-properties.h" +#include "monitor/qdev.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "qemu/units.h" +#include "target/i386/cpu.h" +#include "exec/address-spaces.h" + +static Property sgx_epc_properties[] = { + DEFINE_PROP_UINT64(SGX_EPC_ADDR_PROP, SGXEPCDevice, addr, 0), + DEFINE_PROP_LINK(SGX_EPC_MEMDEV_PROP, SGXEPCDevice, hostmem, + TYPE_MEMORY_BACKEND_EPC, HostMemoryBackendEpc *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sgx_epc_get_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Error *local_err = NULL; + uint64_t value; + + value = memory_device_get_region_size(MEMORY_DEVICE(obj), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + visit_type_uint64(v, name, &value, errp); +} + +static void sgx_epc_init(Object *obj) +{ + object_property_add(obj, SGX_EPC_SIZE_PROP, "uint64", sgx_epc_get_size, + NULL, NULL, NULL); +} + +static void sgx_epc_realize(DeviceState *dev, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + X86MachineState *x86ms = X86_MACHINE(pcms); + SGXEPCDevice *epc = SGX_EPC(dev); + HostMemoryBackend *hostmem; + const char *path; + + if (x86ms->boot_cpus != 0) { + error_setg(errp, "'" TYPE_SGX_EPC "' can't be created after vCPUs," + "e.g. via -device"); + return; + } + + if (!epc->hostmem) { + error_setg(errp, "'" SGX_EPC_MEMDEV_PROP "' property is not set"); + return; + } + hostmem = MEMORY_BACKEND(epc->hostmem); + if (host_memory_backend_is_mapped(hostmem)) { + path = object_get_canonical_path_component(OBJECT(hostmem)); + error_setg(errp, "can't use already busy memdev: %s", path); + return; + } + + error_setg(errp, "'" TYPE_SGX_EPC "' not supported"); +} + +static void sgx_epc_unrealize(DeviceState *dev) +{ + SGXEPCDevice *epc = SGX_EPC(dev); + HostMemoryBackend *hostmem = MEMORY_BACKEND(epc->hostmem); + + host_memory_backend_set_mapped(hostmem, false); +} + +static uint64_t sgx_epc_md_get_addr(const MemoryDeviceState *md) +{ + const SGXEPCDevice *epc = SGX_EPC(md); + + return epc->addr; +} + +static void sgx_epc_md_set_addr(MemoryDeviceState *md, uint64_t addr, + Error **errp) +{ + object_property_set_uint(OBJECT(md), SGX_EPC_ADDR_PROP, addr, errp); +} + +static uint64_t sgx_epc_md_get_plugged_size(const MemoryDeviceState *md, + Error **errp) +{ + return 0; +} + +static MemoryRegion *sgx_epc_md_get_memory_region(MemoryDeviceState *md, + Error **errp) +{ + SGXEPCDevice *epc = SGX_EPC(md); + HostMemoryBackend *hostmem; + + if (!epc->hostmem) { + error_setg(errp, "'" SGX_EPC_MEMDEV_PROP "' property must be set"); + return NULL; + } + + hostmem = MEMORY_BACKEND(epc->hostmem); + return host_memory_backend_get_memory(hostmem); +} + +static void sgx_epc_md_fill_device_info(const MemoryDeviceState *md, + MemoryDeviceInfo *info) +{ + /* TODO */ +} + +static void sgx_epc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); + + dc->hotpluggable = false; + dc->realize = sgx_epc_realize; + dc->unrealize = sgx_epc_unrealize; + dc->desc = "SGX EPC section"; + device_class_set_props(dc, sgx_epc_properties); + + mdc->get_addr = sgx_epc_md_get_addr; + mdc->set_addr = sgx_epc_md_set_addr; + mdc->get_plugged_size = sgx_epc_md_get_plugged_size; + mdc->get_memory_region = sgx_epc_md_get_memory_region; + mdc->fill_device_info = sgx_epc_md_fill_device_info; +} + +static TypeInfo sgx_epc_info = { + .name = TYPE_SGX_EPC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SGXEPCDevice), + .instance_init = sgx_epc_init, + .class_init = sgx_epc_class_init, + .class_size = sizeof(DeviceClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_MEMORY_DEVICE }, + { } + }, +}; + +static void sgx_epc_register_types(void) +{ + type_register_static(&sgx_epc_info); +} + +type_init(sgx_epc_register_types) diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h new file mode 100644 index 0000000000..cf3ed5c0cd --- /dev/null +++ b/include/hw/i386/sgx-epc.h @@ -0,0 +1,44 @@ +/* + * SGX EPC device + * + * Copyright (C) 2019 Intel Corporation + * + * Authors: + * Sean Christopherson + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_SGX_EPC_H +#define QEMU_SGX_EPC_H + +#include "hw/i386/hostmem-epc.h" + +#define TYPE_SGX_EPC "sgx-epc" +#define SGX_EPC(obj) \ + OBJECT_CHECK(SGXEPCDevice, (obj), TYPE_SGX_EPC) +#define SGX_EPC_CLASS(oc) \ + OBJECT_CLASS_CHECK(SGXEPCDeviceClass, (oc), TYPE_SGX_EPC) +#define SGX_EPC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SGXEPCDeviceClass, (obj), TYPE_SGX_EPC) + +#define SGX_EPC_ADDR_PROP "addr" +#define SGX_EPC_SIZE_PROP "size" +#define SGX_EPC_MEMDEV_PROP "memdev" + +/** + * SGXEPCDevice: + * @addr: starting guest physical address, where @SGXEPCDevice is mapped. + * Default value: 0, means that address is auto-allocated. + * @hostmem: host memory backend providing memory for @SGXEPCDevice + */ +typedef struct SGXEPCDevice { + /* private */ + DeviceState parent_obj; + + /* public */ + uint64_t addr; + HostMemoryBackendEpc *hostmem; +} SGXEPCDevice; + +#endif From dfce81f1b931352af0fcfe966c115a09646bd15a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 28 Sep 2021 10:40:58 +0200 Subject: [PATCH 0111/1334] vl: Add sgx compound properties to expose SGX EPC sections to guest Because SGX EPC is enumerated through CPUID, EPC "devices" need to be realized prior to realizing the vCPUs themselves, i.e. long before generic devices are parsed and realized. From a virtualization perspective, the CPUID aspect also means that EPC sections cannot be hotplugged without paravirtualizing the guest kernel (hardware does not support hotplugging as EPC sections must be locked down during pre-boot to provide EPC's security properties). So even though EPC sections could be realized through the generic -devices command, they need to be created much earlier for them to actually be usable by the guest. Place all EPC sections in a contiguous block, somewhat arbitrarily starting after RAM above 4g. Ensuring EPC is in a contiguous region simplifies calculations, e.g. device memory base, PCI hole, etc..., allows dynamic calculation of the total EPC size, e.g. exposing EPC to guests does not require -maxmem, and last but not least allows all of EPC to be enumerated in a single ACPI entry, which is expected by some kernels, e.g. Windows 7 and 8. The new compound properties command for sgx like below: ...... -object memory-backend-epc,id=mem1,size=28M,prealloc=on \ -object memory-backend-epc,id=mem2,size=10M \ -M sgx-epc.0.memdev=mem1,sgx-epc.1.memdev=mem2 Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-6-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/sgx-epc.c | 20 ++++++++++++++------ hw/i386/x86.c | 29 +++++++++++++++++++++++++++++ include/hw/i386/pc.h | 3 +++ include/hw/i386/sgx-epc.h | 14 ++++++++++++++ include/hw/i386/x86.h | 1 + qapi/machine.json | 26 +++++++++++++++++++++++++- qemu-options.hx | 10 ++++++++-- 7 files changed, 94 insertions(+), 9 deletions(-) diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index c584acc17b..6677dc74b5 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -14,13 +14,8 @@ #include "hw/i386/sgx-epc.h" #include "hw/mem/memory-device.h" #include "hw/qdev-properties.h" -#include "monitor/qdev.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "qemu/config-file.h" -#include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/units.h" #include "target/i386/cpu.h" #include "exec/address-spaces.h" @@ -56,6 +51,8 @@ static void sgx_epc_realize(DeviceState *dev, Error **errp) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); X86MachineState *x86ms = X86_MACHINE(pcms); + MemoryDeviceState *md = MEMORY_DEVICE(dev); + SGXEPCState *sgx_epc = &pcms->sgx_epc; SGXEPCDevice *epc = SGX_EPC(dev); HostMemoryBackend *hostmem; const char *path; @@ -77,7 +74,18 @@ static void sgx_epc_realize(DeviceState *dev, Error **errp) return; } - error_setg(errp, "'" TYPE_SGX_EPC "' not supported"); + epc->addr = sgx_epc->base + sgx_epc->size; + + memory_region_add_subregion(&sgx_epc->mr, epc->addr - sgx_epc->base, + host_memory_backend_get_memory(hostmem)); + + host_memory_backend_set_mapped(hostmem, true); + + sgx_epc->sections = g_renew(SGXEPCDevice *, sgx_epc->sections, + sgx_epc->nr_sections + 1); + sgx_epc->sections[sgx_epc->nr_sections++] = epc; + + sgx_epc->size += memory_device_get_region_size(md, errp); } static void sgx_epc_unrealize(DeviceState *dev) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 00448ed55a..41ef9a84a9 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -30,6 +30,8 @@ #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qapi/qapi-visit-common.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-machine.h" #include "qapi/visitor.h" #include "sysemu/qtest.h" #include "sysemu/whpx.h" @@ -1263,6 +1265,27 @@ static void x86_machine_set_bus_lock_ratelimit(Object *obj, Visitor *v, visit_type_uint64(v, name, &x86ms->bus_lock_ratelimit, errp); } +static void machine_get_sgx_epc(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + SgxEPCList *list = x86ms->sgx_epc_list; + + visit_type_SgxEPCList(v, name, &list, errp); +} + +static void machine_set_sgx_epc(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + SgxEPCList *list; + + list = x86ms->sgx_epc_list; + visit_type_SgxEPCList(v, name, &x86ms->sgx_epc_list, errp); + + qapi_free_SgxEPCList(list); +} + static void x86_machine_initfn(Object *obj) { X86MachineState *x86ms = X86_MACHINE(obj); @@ -1322,6 +1345,12 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) x86_machine_set_bus_lock_ratelimit, NULL, NULL); object_class_property_set_description(oc, X86_MACHINE_BUS_LOCK_RATELIMIT, "Set the ratelimit for the bus locks acquired in VMs"); + + object_class_property_add(oc, "sgx-epc", "SgxEPC", + machine_get_sgx_epc, machine_set_sgx_epc, + NULL, NULL); + object_class_property_set_description(oc, "sgx-epc", + "SGX EPC device"); } static const TypeInfo x86_machine_info = { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 4d2e35a152..668e48be8a 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -12,6 +12,7 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/hotplug.h" #include "qom/object.h" +#include "hw/i386/sgx-epc.h" #define HPET_INTCAP "hpet-intcap" @@ -49,6 +50,8 @@ typedef struct PCMachineState { /* ACPI Memory hotplug IO base address */ hwaddr memhp_io_base; + + SGXEPCState sgx_epc; } PCMachineState; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h index cf3ed5c0cd..83269972e0 100644 --- a/include/hw/i386/sgx-epc.h +++ b/include/hw/i386/sgx-epc.h @@ -41,4 +41,18 @@ typedef struct SGXEPCDevice { HostMemoryBackendEpc *hostmem; } SGXEPCDevice; +/* + * @base: address in guest physical address space where EPC regions start + * @mr: address space container for memory devices + */ +typedef struct SGXEPCState { + uint64_t base; + uint64_t size; + + MemoryRegion mr; + + struct SGXEPCDevice **sections; + int nr_sections; +} SGXEPCState; + #endif diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 6e9244a82c..23267a3674 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -62,6 +62,7 @@ struct X86MachineState { unsigned pci_irq_mask; unsigned apic_id_limit; uint16_t boot_cpus; + SgxEPCList *sgx_epc_list; OnOffAuto smm; OnOffAuto acpi; diff --git a/qapi/machine.json b/qapi/machine.json index 32d47f4e35..26c539fe2c 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1225,7 +1225,6 @@ ## { 'struct': 'VirtioMEMDeviceInfoWrapper', 'data': { 'data': 'VirtioMEMDeviceInfo' } } - ## # @MemoryDeviceInfo: # @@ -1246,6 +1245,31 @@ } } +## +# @SgxEPC: +# +# Sgx EPC cmdline information +# +# @memdev: memory backend linked with device +# +# Since: 6.2 +## +{ 'struct': 'SgxEPC', + 'data': { 'memdev': 'str' } } + +## +# @SgxEPCProperties: +# +# SGX properties of machine types. +# +# @sgx-epc: list of ids of memory-backend-epc objects. +# +# Since: 6.2 +## +{ 'struct': 'SgxEPCProperties', + 'data': { 'sgx-epc': ['SgxEPC'] } +} + ## # @query-memory-devices: # diff --git a/qemu-options.hx b/qemu-options.hx index 8f603cc7e6..ceca52818a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -126,8 +126,14 @@ SRST -m 512M ERST -HXCOMM Deprecated by -machine -DEF("M", HAS_ARG, QEMU_OPTION_M, "", QEMU_ARCH_ALL) +DEF("M", HAS_ARG, QEMU_OPTION_M, + " sgx-epc.0.memdev=memid\n", + QEMU_ARCH_ALL) + +SRST +``sgx-epc.0.memdev=@var{memid}`` + Define an SGX EPC section. +ERST DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, "-cpu cpu select CPU ('-cpu help' for list)\n", QEMU_ARCH_ALL) From 5c76b651d0f6c420d31e51d22c380a0bdd04fb98 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:09 +0800 Subject: [PATCH 0112/1334] i386: Add primary SGX CPUID and MSR defines Add CPUID defines for SGX and SGX Launch Control (LC), as well as defines for their associated FEATURE_CONTROL MSR bits. Define the Launch Enclave Public Key Hash MSRs (LE Hash MSRs), which exist when SGX LC is present (in CPUID), and are writable when SGX LC is enabled (in FEATURE_CONTROL). Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-7-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ++-- target/i386/cpu.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6b029f1bdf..21d2a325ea 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -795,7 +795,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_7_0_EBX] = { .type = CPUID_FEATURE_WORD, .feat_names = { - "fsgsbase", "tsc-adjust", NULL, "bmi1", + "fsgsbase", "tsc-adjust", "sgx", "bmi1", "hle", "avx2", NULL, "smep", "bmi2", "erms", "invpcid", "rtm", NULL, NULL, "mpx", NULL, @@ -821,7 +821,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "la57", NULL, NULL, NULL, NULL, NULL, "rdpid", NULL, "bus-lock-detect", "cldemote", NULL, "movdiri", - "movdir64b", NULL, NULL, "pks", + "movdir64b", NULL, "sgxlc", "pks", }, .cpuid = { .eax = 7, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c2954c71ea..b6491df0f5 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -389,9 +389,17 @@ typedef enum X86Seg { #define MSR_IA32_PKRS 0x6e1 #define FEATURE_CONTROL_LOCKED (1<<0) +#define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1ULL << 1) #define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2) +#define FEATURE_CONTROL_SGX_LC (1ULL << 17) +#define FEATURE_CONTROL_SGX (1ULL << 18) #define FEATURE_CONTROL_LMCE (1<<20) +#define MSR_IA32_SGXLEPUBKEYHASH0 0x8c +#define MSR_IA32_SGXLEPUBKEYHASH1 0x8d +#define MSR_IA32_SGXLEPUBKEYHASH2 0x8e +#define MSR_IA32_SGXLEPUBKEYHASH3 0x8f + #define MSR_P6_PERFCTR0 0xc1 #define MSR_IA32_SMBASE 0x9e @@ -718,6 +726,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; /* Support RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE */ #define CPUID_7_0_EBX_FSGSBASE (1U << 0) +/* Support SGX */ +#define CPUID_7_0_EBX_SGX (1U << 2) /* 1st Group of Advanced Bit Manipulation Extensions */ #define CPUID_7_0_EBX_BMI1 (1U << 3) /* Hardware Lock Elision */ @@ -805,6 +815,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_ECX_MOVDIRI (1U << 27) /* Move 64 Bytes as Direct Store Instruction */ #define CPUID_7_0_ECX_MOVDIR64B (1U << 28) +/* Support SGX Launch Control */ +#define CPUID_7_0_ECX_SGX_LC (1U << 30) /* Protection Keys for Supervisor-mode Pages */ #define CPUID_7_0_ECX_PKS (1U << 31) From 4b841a793cd5c460b99312f80d306cec37a1b69d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:10 +0800 Subject: [PATCH 0113/1334] i386: Add SGX CPUID leaf FEAT_SGX_12_0_EAX CPUID leaf 12_0_EAX is an Intel-defined feature bits leaf enumerating the CPU's SGX capabilities, e.g. supported SGX instruction sets. Currently there are four enumerated capabilities: - SGX1 instruction set, i.e. "base" SGX - SGX2 instruction set for dynamic EPC management - ENCLV instruction set for VMM oversubscription of EPC - ENCLS-C instruction set for thread safe variants of ENCLS Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-8-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 20 ++++++++++++++++++++ target/i386/cpu.h | 1 + 2 files changed, 21 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 21d2a325ea..2cd1487bae 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -654,6 +654,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, /* missing: CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ #define TCG_14_0_ECX_FEATURES 0 +#define TCG_SGX_12_0_EAX_FEATURES 0 FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_1_EDX] = { @@ -1182,6 +1183,25 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .tcg_features = TCG_14_0_ECX_FEATURES, }, + [FEAT_SGX_12_0_EAX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + "sgx1", "sgx2", NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { + .eax = 0x12, + .needs_ecx = true, .ecx = 0, + .reg = R_EAX, + }, + .tcg_features = TCG_SGX_12_0_EAX_FEATURES, + }, }; typedef struct FeatureMask { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index b6491df0f5..2b199102ef 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -578,6 +578,7 @@ typedef enum FeatureWord { FEAT_VMX_BASIC, FEAT_VMX_VMFUNC, FEAT_14_0_ECX, + FEAT_SGX_12_0_EAX, /* CPUID[EAX=0x12,ECX=0].EAX (SGX) */ FEATURE_WORDS, } FeatureWord; From 120ca112ed0cb7c3c747f40d8380a5dc210e42ba Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:11 +0800 Subject: [PATCH 0114/1334] i386: Add SGX CPUID leaf FEAT_SGX_12_0_EBX CPUID leaf 12_0_EBX is an Intel-defined feature bits leaf enumerating the platform's SGX extended capabilities. Currently there is a single capabilitiy: - EXINFO: record information about #PFs and #GPs in the enclave's SSA Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-9-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 21 +++++++++++++++++++++ target/i386/cpu.h | 1 + 2 files changed, 22 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 2cd1487bae..c0d5c3c621 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -655,6 +655,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ #define TCG_14_0_ECX_FEATURES 0 #define TCG_SGX_12_0_EAX_FEATURES 0 +#define TCG_SGX_12_0_EBX_FEATURES 0 FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_1_EDX] = { @@ -1202,6 +1203,26 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, .tcg_features = TCG_SGX_12_0_EAX_FEATURES, }, + + [FEAT_SGX_12_0_EBX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + "sgx-exinfo" , NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { + .eax = 0x12, + .needs_ecx = true, .ecx = 0, + .reg = R_EBX, + }, + .tcg_features = TCG_SGX_12_0_EBX_FEATURES, + }, }; typedef struct FeatureMask { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 2b199102ef..e66ec85980 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -579,6 +579,7 @@ typedef enum FeatureWord { FEAT_VMX_VMFUNC, FEAT_14_0_ECX, FEAT_SGX_12_0_EAX, /* CPUID[EAX=0x12,ECX=0].EAX (SGX) */ + FEAT_SGX_12_0_EBX, /* CPUID[EAX=0x12,ECX=0].EBX (SGX MISCSELECT[31:0]) */ FEATURE_WORDS, } FeatureWord; From 165981a5e6baca05c99c1cdd5f7f6d89de77d5d7 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:12 +0800 Subject: [PATCH 0115/1334] i386: Add SGX CPUID leaf FEAT_SGX_12_1_EAX CPUID leaf 12_1_EAX is an Intel-defined feature bits leaf enumerating the platform's SGX capabilities that may be utilized by an enclave, e.g. whether or not an enclave can gain access to the provision key. Currently there are six capabilities: - INIT: set when the enclave has has been initialized by EINIT. Cannot be set by software, i.e. forced to zero in CPUID. - DEBUG: permits a debugger to read/write into the enclave. - MODE64BIT: the enclave runs in 64-bit mode - PROVISIONKEY: grants has access to the provision key - EINITTOKENKEY: grants access to the EINIT token key, i.e. the enclave can generate EINIT tokens - KSS: Key Separation and Sharing enabled for the enclave. Note that the entirety of CPUID.0x12.0x1, i.e. all registers, enumerates the allowed ATTRIBUTES (128 bits), but only bits 31:0 are directly exposed to the user (via FEAT_12_1_EAX). Bits 63:32 are currently all reserved and bits 127:64 correspond to the allowed XSAVE Feature Request Mask, which is calculated based on other CPU features, e.g. XSAVE, MPX, AVX, etc... and is not exposed to the user. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-10-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 21 +++++++++++++++++++++ target/i386/cpu.h | 1 + 2 files changed, 22 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c0d5c3c621..e9ecbf59e5 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -656,6 +656,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_14_0_ECX_FEATURES 0 #define TCG_SGX_12_0_EAX_FEATURES 0 #define TCG_SGX_12_0_EBX_FEATURES 0 +#define TCG_SGX_12_1_EAX_FEATURES 0 FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_1_EDX] = { @@ -1223,6 +1224,26 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, .tcg_features = TCG_SGX_12_0_EBX_FEATURES, }, + + [FEAT_SGX_12_1_EAX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, "sgx-debug", "sgx-mode64", NULL, + "sgx-provisionkey", "sgx-tokenkey", NULL, "sgx-kss", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { + .eax = 0x12, + .needs_ecx = true, .ecx = 1, + .reg = R_EAX, + }, + .tcg_features = TCG_SGX_12_1_EAX_FEATURES, + }, }; typedef struct FeatureMask { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e66ec85980..85a9eeeb2b 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -580,6 +580,7 @@ typedef enum FeatureWord { FEAT_14_0_ECX, FEAT_SGX_12_0_EAX, /* CPUID[EAX=0x12,ECX=0].EAX (SGX) */ FEAT_SGX_12_0_EBX, /* CPUID[EAX=0x12,ECX=0].EBX (SGX MISCSELECT[31:0]) */ + FEAT_SGX_12_1_EAX, /* CPUID[EAX=0x12,ECX=1].EAX (SGX ATTRIBUTES[31:0]) */ FEATURE_WORDS, } FeatureWord; From db888065233ab435a8ea9e589ce755668eef4f90 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:13 +0800 Subject: [PATCH 0116/1334] i386: Add get/set/migrate support for SGX_LEPUBKEYHASH MSRs On real hardware, on systems that supports SGX Launch Control, those MSRs are initialized to digest of Intel's signing key; on systems that don't support SGX Launch Control, those MSRs are not available but hardware always uses digest of Intel's signing key in EINIT. KVM advertises SGX LC via CPUID if and only if the MSRs are writable. Unconditionally initialize those MSRs to digest of Intel's signing key when CPU is realized and reset to reflect the fact. This avoids potential bug in case kvm_arch_put_registers() is called before kvm_arch_get_registers() is called, in which case guest's virtual SGX_LEPUBKEYHASH MSRs will be set to 0, although KVM initializes those to digest of Intel's signing key by default, since KVM allows those MSRs to be updated by Qemu to support live migration. Save/restore the SGX Launch Enclave Public Key Hash MSRs if SGX Launch Control (LC) is exposed to the guest. Likewise, migrate the MSRs if they are writable by the guest. Signed-off-by: Sean Christopherson Signed-off-by: Kai Huang Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-11-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 16 +++++++++++++++- target/i386/cpu.h | 1 + target/i386/kvm/kvm.c | 22 ++++++++++++++++++++++ target/i386/machine.c | 20 ++++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index e9ecbf59e5..af6cd73eed 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5700,6 +5700,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } +static void x86_cpu_set_sgxlepubkeyhash(CPUX86State *env) +{ +#ifndef CONFIG_USER_ONLY + /* Those default values are defined in Skylake HW */ + env->msr_ia32_sgxlepubkeyhash[0] = 0xa6053e051270b7acULL; + env->msr_ia32_sgxlepubkeyhash[1] = 0x6cfbe8ba8b3b413dULL; + env->msr_ia32_sgxlepubkeyhash[2] = 0xc4916d99f2b3735dULL; + env->msr_ia32_sgxlepubkeyhash[3] = 0xd4f8c05909f9bb3bULL; +#endif +} + static void x86_cpu_reset(DeviceState *dev) { CPUState *s = CPU(dev); @@ -5832,6 +5843,8 @@ static void x86_cpu_reset(DeviceState *dev) if (kvm_enabled()) { kvm_arch_reset_vcpu(cpu); } + + x86_cpu_set_sgxlepubkeyhash(env); #endif } @@ -6214,6 +6227,8 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) & CPUID_EXT2_AMD_ALIASES); } + x86_cpu_set_sgxlepubkeyhash(env); + /* * note: the call to the framework needs to happen after feature expansion, * but before the checks/modifications to ucode_rev, mwait, phys_bits. @@ -6901,7 +6916,6 @@ static const TypeInfo x86_cpu_type_info = { .class_init = x86_cpu_common_class_init, }; - /* "base" CPU model, used by query-cpu-model-expansion */ static void x86_cpu_base_class_init(ObjectClass *oc, void *data) { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 85a9eeeb2b..29552dc2a7 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1516,6 +1516,7 @@ typedef struct CPUX86State { uint64_t mcg_status; uint64_t msr_ia32_misc_enable; uint64_t msr_ia32_feature_control; + uint64_t msr_ia32_sgxlepubkeyhash[4]; uint64_t msr_fixed_ctr_ctrl; uint64_t msr_global_ctrl; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 500d2e0e68..11551648f9 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3107,6 +3107,17 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } } + if (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_SGX_LC) { + kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH0, + env->msr_ia32_sgxlepubkeyhash[0]); + kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH1, + env->msr_ia32_sgxlepubkeyhash[1]); + kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH2, + env->msr_ia32_sgxlepubkeyhash[2]); + kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH3, + env->msr_ia32_sgxlepubkeyhash[3]); + } + /* Note: MSR_IA32_FEATURE_CONTROL is written separately, see * kvm_put_msr_feature_control. */ } @@ -3446,6 +3457,13 @@ static int kvm_get_msrs(X86CPU *cpu) } } + if (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_SGX_LC) { + kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH0, 0); + kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH1, 0); + kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH2, 0); + kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH3, 0); + } + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, cpu->kvm_msr_buf); if (ret < 0) { return ret; @@ -3735,6 +3753,10 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: env->msr_rtit_addrs[index - MSR_IA32_RTIT_ADDR0_A] = msrs[i].data; break; + case MSR_IA32_SGXLEPUBKEYHASH0 ... MSR_IA32_SGXLEPUBKEYHASH3: + env->msr_ia32_sgxlepubkeyhash[index - MSR_IA32_SGXLEPUBKEYHASH0] = + msrs[i].data; + break; } } diff --git a/target/i386/machine.c b/target/i386/machine.c index b0943118d1..4367931623 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1415,6 +1415,25 @@ static const VMStateDescription vmstate_msr_tsx_ctrl = { } }; +static bool intel_sgx_msrs_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return !!(env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_SGX_LC); +} + +static const VMStateDescription vmstate_msr_intel_sgx = { + .name = "cpu/intel_sgx", + .version_id = 1, + .minimum_version_id = 1, + .needed = intel_sgx_msrs_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.msr_ia32_sgxlepubkeyhash, X86CPU, 4), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -1551,6 +1570,7 @@ const VMStateDescription vmstate_x86_cpu = { &vmstate_nested_state, #endif &vmstate_msr_tsx_ctrl, + &vmstate_msr_intel_sgx, NULL } }; From a04835414b8d1ba1af980692d5cf20f8fe4156a0 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:14 +0800 Subject: [PATCH 0117/1334] i386: Add feature control MSR dependency when SGX is enabled SGX adds multiple flags to FEATURE_CONTROL to enable SGX and Flexible Launch Control. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-12-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 11551648f9..6dc40161e0 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1877,6 +1877,11 @@ int kvm_arch_init_vcpu(CPUState *cs) !!(c->ecx & CPUID_EXT_SMX); } + c = cpuid_find_entry(&cpuid_data.cpuid, 7, 0); + if (c && (c->ebx & CPUID_7_0_EBX_SGX)) { + has_msr_feature_control = true; + } + if (env->mcg_cap & MCG_LMCE_P) { has_msr_mcg_ext_ctl = has_msr_feature_control = true; } From 1dec2e1f19fdb39a0340356ec2d77233837b3d68 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:15 +0800 Subject: [PATCH 0118/1334] i386: Update SGX CPUID info according to hardware/KVM/user input Expose SGX to the guest if and only if KVM is enabled and supports virtualization of SGX. While the majority of ENCLS can be emulated to some degree, because SGX uses a hardware-based root of trust, the attestation aspects of SGX cannot be emulated in software, i.e. ultimately emulation will fail as software cannot generate a valid quote/report. The complexity of partially emulating SGX in Qemu far outweighs the value added, e.g. an SGX specific simulator for userspace applications can emulate SGX for development and testing purposes. Note, access to the PROVISIONKEY is not yet advertised to the guest as KVM blocks access to the PROVISIONKEY by default and requires userspace to provide additional credentials (via ioctl()) to expose PROVISIONKEY. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-13-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/meson.build | 3 +- hw/i386/sgx-stub.c | 13 +++++++ hw/i386/sgx.c | 73 +++++++++++++++++++++++++++++++++++++ include/hw/i386/pc.h | 3 ++ include/hw/i386/sgx-epc.h | 2 + target/i386/cpu.c | 77 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 hw/i386/sgx-stub.c create mode 100644 hw/i386/sgx.c diff --git a/hw/i386/meson.build b/hw/i386/meson.build index b1862c83d4..c502965219 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -16,7 +16,8 @@ i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) i386_ss.add(when: 'CONFIG_VTD', if_true: files('intel_iommu.c')) -i386_ss.add(when: 'CONFIG_SGX', if_true: files('sgx-epc.c')) +i386_ss.add(when: 'CONFIG_SGX', if_true: files('sgx-epc.c','sgx.c'), + if_false: files('sgx-stub.c')) i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c')) i386_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device_x86.c')) diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c new file mode 100644 index 0000000000..483c72bba6 --- /dev/null +++ b/hw/i386/sgx-stub.c @@ -0,0 +1,13 @@ +#include "qemu/osdep.h" +#include "hw/i386/pc.h" +#include "hw/i386/sgx-epc.h" + +void pc_machine_init_sgx_epc(PCMachineState *pcms) +{ + memset(&pcms->sgx_epc, 0, sizeof(SGXEPCState)); +} + +int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) +{ + g_assert_not_reached(); +} diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c new file mode 100644 index 0000000000..8a18cddc3f --- /dev/null +++ b/hw/i386/sgx.c @@ -0,0 +1,73 @@ +/* + * SGX common code + * + * Copyright (C) 2021 Intel Corporation + * + * Authors: + * Yang Zhong + * Sean Christopherson + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "hw/i386/pc.h" +#include "hw/i386/sgx-epc.h" +#include "hw/mem/memory-device.h" +#include "monitor/qdev.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" + +int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) +{ + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + SGXEPCDevice *epc; + + if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) { + return 1; + } + + epc = pcms->sgx_epc.sections[section_nr]; + + *addr = epc->addr; + *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal); + + return 0; +} + +void pc_machine_init_sgx_epc(PCMachineState *pcms) +{ + SGXEPCState *sgx_epc = &pcms->sgx_epc; + X86MachineState *x86ms = X86_MACHINE(pcms); + SgxEPCList *list = NULL; + Object *obj; + + memset(sgx_epc, 0, sizeof(SGXEPCState)); + if (!x86ms->sgx_epc_list) { + return; + } + + sgx_epc->base = 0x100000000ULL + x86ms->above_4g_mem_size; + + memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX); + memory_region_add_subregion(get_system_memory(), sgx_epc->base, + &sgx_epc->mr); + + for (list = x86ms->sgx_epc_list; list; list = list->next) { + obj = object_new("sgx-epc"); + + /* set the memdev link with memory backend */ + object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev, + &error_fatal); + object_property_set_bool(obj, "realized", true, &error_fatal); + object_unref(obj); + } + + if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) { + error_report("Size of all 'sgx-epc' =0x%"PRIu64" causes EPC to wrap", + sgx_epc->size); + exit(EXIT_FAILURE); + } + + memory_region_set_size(&sgx_epc->mr, sgx_epc->size); +} diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 668e48be8a..5748d7c55f 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -195,6 +195,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, const CPUArchIdList *apic_ids, GArray *entry); +/* sgx.c */ +void pc_machine_init_sgx_epc(PCMachineState *pcms); + extern GlobalProperty pc_compat_6_1[]; extern const size_t pc_compat_6_1_len; diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h index 83269972e0..75b19f464c 100644 --- a/include/hw/i386/sgx-epc.h +++ b/include/hw/i386/sgx-epc.h @@ -55,4 +55,6 @@ typedef struct SGXEPCState { int nr_sections; } SGXEPCState; +int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size); + #endif diff --git a/target/i386/cpu.c b/target/i386/cpu.c index af6cd73eed..8a62986819 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -36,6 +36,7 @@ #ifndef CONFIG_USER_ONLY #include "exec/address-spaces.h" #include "hw/boards.h" +#include "hw/i386/sgx-epc.h" #endif #include "disas/capstone.h" @@ -5334,6 +5335,25 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx |= CPUID_7_0_ECX_OSPKE; } *edx = env->features[FEAT_7_0_EDX]; /* Feature flags */ + + /* + * SGX cannot be emulated in software. If hardware does not + * support enabling SGX and/or SGX flexible launch control, + * then we need to update the VM's CPUID values accordingly. + */ + if ((*ebx & CPUID_7_0_EBX_SGX) && + (!kvm_enabled() || + !(kvm_arch_get_supported_cpuid(cs->kvm_state, 0x7, 0, R_EBX) & + CPUID_7_0_EBX_SGX))) { + *ebx &= ~CPUID_7_0_EBX_SGX; + } + + if ((*ecx & CPUID_7_0_ECX_SGX_LC) && + (!(*ebx & CPUID_7_0_EBX_SGX) || !kvm_enabled() || + !(kvm_arch_get_supported_cpuid(cs->kvm_state, 0x7, 0, R_ECX) & + CPUID_7_0_ECX_SGX_LC))) { + *ecx &= ~CPUID_7_0_ECX_SGX_LC; + } } else if (count == 1) { *eax = env->features[FEAT_7_1_EAX]; *ebx = 0; @@ -5469,6 +5489,63 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x12: +#ifndef CONFIG_USER_ONLY + if (!kvm_enabled() || + !(env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SGX)) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + + /* + * SGX sub-leafs CPUID.0x12.{0x2..N} enumerate EPC sections. Retrieve + * the EPC properties, e.g. confidentiality and integrity, from the + * host's first EPC section, i.e. assume there is one EPC section or + * that all EPC sections have the same security properties. + */ + if (count > 1) { + uint64_t epc_addr, epc_size; + + if (sgx_epc_get_section(count - 2, &epc_addr, &epc_size)) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + host_cpuid(index, 2, eax, ebx, ecx, edx); + *eax = (uint32_t)(epc_addr & 0xfffff000) | 0x1; + *ebx = (uint32_t)(epc_addr >> 32); + *ecx = (uint32_t)(epc_size & 0xfffff000) | (*ecx & 0xf); + *edx = (uint32_t)(epc_size >> 32); + break; + } + + /* + * SGX sub-leafs CPUID.0x12.{0x0,0x1} are heavily dependent on hardware + * and KVM, i.e. QEMU cannot emulate features to override what KVM + * supports. Features can be further restricted by userspace, but not + * made more permissive. + */ + *eax = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EAX); + *ebx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EBX); + *ecx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_ECX); + *edx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EDX); + + if (count == 0) { + *eax &= env->features[FEAT_SGX_12_0_EAX]; + *ebx &= env->features[FEAT_SGX_12_0_EBX]; + } else { + *eax &= env->features[FEAT_SGX_12_1_EAX]; + *ebx &= 0; /* ebx reserve */ + *ecx &= env->features[FEAT_XSAVE_COMP_LO]; + *edx &= env->features[FEAT_XSAVE_COMP_HI]; + + /* FP and SSE are always allowed regardless of XSAVE/XCR0. */ + *ecx |= XSTATE_FP_MASK | XSTATE_SSE_MASK; + + /* Access to PROVISIONKEY requires additional credentials. */ + *eax &= ~(1U << 4); + } +#endif + break; case 0x14: { /* Intel Processor Trace Enumeration */ *eax = 0; From c22f5467856d7e7fa5ee4a1f0ee9edc3bb80bf5c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:16 +0800 Subject: [PATCH 0119/1334] i386: kvm: Add support for exposing PROVISIONKEY to guest If the guest want to fully use SGX, the guest needs to be able to access provisioning key. Add a new KVM_CAP_SGX_ATTRIBUTE to KVM to support provisioning key to KVM guests. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-14-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 5 ++++- target/i386/kvm/kvm.c | 29 +++++++++++++++++++++++++++++ target/i386/kvm/kvm_i386.h | 2 ++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 8a62986819..de58599a3d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5542,7 +5542,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx |= XSTATE_FP_MASK | XSTATE_SSE_MASK; /* Access to PROVISIONKEY requires additional credentials. */ - *eax &= ~(1U << 4); + if ((*eax & (1U << 4)) && + !kvm_enable_sgx_provisioning(cs->kvm_state)) { + *eax &= ~(1U << 4); + } } #endif break; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 6dc40161e0..488926a95f 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -4644,6 +4644,35 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) } } +static bool has_sgx_provisioning; + +static bool __kvm_enable_sgx_provisioning(KVMState *s) +{ + int fd, ret; + + if (!kvm_vm_check_extension(s, KVM_CAP_SGX_ATTRIBUTE)) { + return false; + } + + fd = qemu_open_old("/dev/sgx_provision", O_RDONLY); + if (fd < 0) { + return false; + } + + ret = kvm_vm_enable_cap(s, KVM_CAP_SGX_ATTRIBUTE, 0, fd); + if (ret) { + error_report("Could not enable SGX PROVISIONKEY: %s", strerror(-ret)); + exit(1); + } + close(fd); + return true; +} + +bool kvm_enable_sgx_provisioning(KVMState *s) +{ + return MEMORIZE(__kvm_enable_sgx_provisioning(s), has_sgx_provisioning); +} + static bool host_supports_vmx(void) { uint32_t ecx, unused; diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 54667b35f0..a978509d50 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -51,4 +51,6 @@ bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address); +bool kvm_enable_sgx_provisioning(KVMState *s); + #endif From b9edbadefb9ecbb1b1754c86ba7d1d7ce3e876aa Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:17 +0800 Subject: [PATCH 0120/1334] i386: Propagate SGX CPUID sub-leafs to KVM The SGX sub-leafs are enumerated at CPUID 0x12. Indices 0 and 1 are always present when SGX is supported, and enumerate SGX features and capabilities. Indices >=2 are directly correlated with the platform's EPC sections. Because the number of EPC sections is dynamic and user defined, the number of SGX sub-leafs is "NULL" terminated. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-15-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 488926a95f..f6bbf33bc1 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1703,6 +1703,25 @@ int kvm_arch_init_vcpu(CPUState *cs) } break; case 0x7: + case 0x12: + for (j = 0; ; j++) { + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (j > 1 && (c->eax & 0xf) != 1) { + break; + } + + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:0x12,ecx:0x%x)\n", j); + abort(); + } + c = &cpuid_data.entries[cpuid_i++]; + } + break; case 0x14: { uint32_t times; From dca6cffc550a3243ba8d106dd02b411342e58782 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:18 +0800 Subject: [PATCH 0121/1334] Adjust min CPUID level to 0x12 when SGX is enabled SGX capabilities are enumerated through CPUID_0x12. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-16-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index de58599a3d..cacec605bf 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6154,6 +6154,11 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) if (sev_enabled()) { x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000001F); } + + /* SGX requires CPUID[0x12] for EPC enumeration */ + if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SGX) { + x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x12); + } } /* Set cpuid_*level* based on cpuid_min_*level, if not explicitly set */ From e2560114cdb127985b75ec2554cb2f0d91dd03f3 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:19 +0800 Subject: [PATCH 0122/1334] hw/i386/fw_cfg: Set SGX bits in feature control fw_cfg accordingly Request SGX an SGX Launch Control to be enabled in FEATURE_CONTROL when the features are exposed to the guest. Our design is the SGX Launch Control bit will be unconditionally set in FEATURE_CONTROL, which is unlike host bios. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-17-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/fw_cfg.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 4e68d5dea4..a283785a8d 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -159,7 +159,7 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg) { X86CPU *cpu = X86_CPU(ms->possible_cpus->cpus[0].cpu); CPUX86State *env = &cpu->env; - uint32_t unused, ecx, edx; + uint32_t unused, ebx, ecx, edx; uint64_t feature_control_bits = 0; uint64_t *val; @@ -174,6 +174,16 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg) feature_control_bits |= FEATURE_CONTROL_LMCE; } + if (env->cpuid_level >= 7) { + cpu_x86_cpuid(env, 0x7, 0, &unused, &ebx, &ecx, &unused); + if (ebx & CPUID_7_0_EBX_SGX) { + feature_control_bits |= FEATURE_CONTROL_SGX; + } + if (ecx & CPUID_7_0_ECX_SGX_LC) { + feature_control_bits |= FEATURE_CONTROL_SGX_LC; + } + } + if (!feature_control_bits) { return; } From 0cf4ce00d2255ef48d560d36559e7a4cda41748b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:20 +0800 Subject: [PATCH 0123/1334] hw/i386/pc: Account for SGX EPC sections when calculating device memory Add helpers to detect if SGX EPC exists above 4g, and if so, where SGX EPC above 4g ends. Use the helpers to adjust the device memory range if SGX EPC exists above 4g. For multiple virtual EPC sections, we just put them together physically contiguous for the simplicity because we don't support EPC NUMA affinity now. Once the SGX EPC NUMA support in the kernel SGX driver, we will support this in the future. Note that SGX EPC is currently hardcoded to reside above 4g. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-18-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 11 ++++++++++- include/hw/i386/sgx-epc.h | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 557d49c9f8..e41c002539 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -919,8 +919,15 @@ void pc_memory_init(PCMachineState *pcms, exit(EXIT_FAILURE); } + if (pcms->sgx_epc.size != 0) { + machine->device_memory->base = sgx_epc_above_4g_end(&pcms->sgx_epc); + } else { + machine->device_memory->base = + 0x100000000ULL + x86ms->above_4g_mem_size; + } + machine->device_memory->base = - ROUND_UP(0x100000000ULL + x86ms->above_4g_mem_size, 1 * GiB); + ROUND_UP(machine->device_memory->base, 1 * GiB); if (pcmc->enforce_aligned_dimm) { /* size device region assuming 1G page max alignment per slot */ @@ -1005,6 +1012,8 @@ uint64_t pc_pci_hole64_start(void) if (!pcmc->broken_reserved_end) { hole64_start += memory_region_size(&ms->device_memory->mr); } + } else if (pcms->sgx_epc.size != 0) { + hole64_start = sgx_epc_above_4g_end(&pcms->sgx_epc); } else { hole64_start = 0x100000000ULL + x86ms->above_4g_mem_size; } diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h index 75b19f464c..65a68ca753 100644 --- a/include/hw/i386/sgx-epc.h +++ b/include/hw/i386/sgx-epc.h @@ -57,4 +57,11 @@ typedef struct SGXEPCState { int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size); +static inline uint64_t sgx_epc_above_4g_end(SGXEPCState *sgx_epc) +{ + assert(sgx_epc != NULL && sgx_epc->base >= 0x100000000ULL); + + return sgx_epc->base + sgx_epc->size; +} + #endif From 1ed1ccc5a429b3fb35d9756f911eb652a6ab00c5 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:21 +0800 Subject: [PATCH 0124/1334] i386/pc: Add e820 entry for SGX EPC section(s) Note that SGX EPC is currently guaranteed to reside in a single contiguous chunk of memory regardless of the number of EPC sections. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-19-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e41c002539..df457eceba 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -889,6 +889,10 @@ void pc_memory_init(PCMachineState *pcms, e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM); } + if (pcms->sgx_epc.size != 0) { + e820_add_entry(pcms->sgx_epc.base, pcms->sgx_epc.size, E820_RESERVED); + } + if (!pcmc->has_reserved_memory && (machine->ram_slots || (machine->maxram_size > machine->ram_size))) { From c8a9899c1adc66a0dcf8f9658a1290a41bc70c2a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:22 +0800 Subject: [PATCH 0125/1334] i386: acpi: Add SGX EPC entry to ACPI tables The ACPI Device entry for SGX EPC is essentially a hack whose primary purpose is to provide software with a way to autoprobe SGX support, e.g. to allow software to implement SGX support as a driver. Details on the individual EPC sections are not enumerated through ACPI tables, i.e. software must enumerate the EPC sections via CPUID. Furthermore, software expects to see only a single EPC Device in the ACPI tables regardless of the number of EPC sections in the system. However, several versions of Windows do rely on the ACPI tables to enumerate the address and size of the EPC. So, regardless of the number of EPC sections exposed to the guest, create exactly *one* EPC device with a _CRS entry that spans the entirety of all EPC sections (which are guaranteed to be contiguous in Qemu). Note, NUMA support for EPC memory is intentionally not considered as enumerating EPC NUMA information is not yet defined for bare metal. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-20-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/acpi-build.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index dfaa47cdc2..f4d6ae3d02 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1841,6 +1841,28 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } #endif + if (pcms->sgx_epc.size != 0) { + uint64_t epc_base = pcms->sgx_epc.base; + uint64_t epc_size = pcms->sgx_epc.size; + + dev = aml_device("EPC"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("INT0E0C"))); + aml_append(dev, aml_name_decl("_STR", + aml_unicode("Enclave Page Cache 1.0"))); + crs = aml_resource_template(); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, 0, epc_base, + epc_base + epc_size - 1, 0, epc_size)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0x0f))); + aml_append(dev, method); + + aml_append(sb_scope, dev); + } aml_append(dsdt, sb_scope); /* copy AML table into ACPI tables blob and patch header there */ From 97488c635e2785281baeeb31b1a3340efc848ad1 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:23 +0800 Subject: [PATCH 0126/1334] q35: Add support for SGX EPC Enable SGX EPC virtualization, which is currently only support by KVM. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-21-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc_q35.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 46cd542d17..5481d5c965 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -177,6 +177,7 @@ static void pc_q35_init(MachineState *machine) x86ms->below_4g_mem_size = machine->ram_size; } + pc_machine_init_sgx_epc(pcms); x86_cpus_init(x86ms, pcmc->default_cpu_version); kvmclock_create(pcmc->kvmclock_create_always); From fb6986a20eb0b6573bb90fabcd7cfbe69bc53b11 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:24 +0800 Subject: [PATCH 0127/1334] i440fx: Add support for SGX EPC Enable SGX EPC virtualization, which is currently only support by KVM. Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-22-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index c5da7739ce..6cc834aff6 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -153,6 +153,7 @@ static void pc_init1(MachineState *machine, } } + pc_machine_init_sgx_epc(pcms); x86_cpus_init(x86ms, pcmc->default_cpu_version); if (pcmc->kvmclock_enabled) { From a7c565a941b02a22f84509db797bd364c2b5716b Mon Sep 17 00:00:00 2001 From: Yang Zhong Date: Mon, 19 Jul 2021 19:21:35 +0800 Subject: [PATCH 0128/1334] sgx-epc: Add the fill_device_info() callback support Since there is no fill_device_info() callback support, and when we execute "info memory-devices" command in the monitor, the segfault will be found. This patch will add this callback support and "info memory-devices" will show sgx epc memory exposed to guest. The result as below: qemu) info memory-devices Memory device [sgx-epc]: "" memaddr: 0x180000000 size: 29360128 memdev: /objects/mem1 Memory device [sgx-epc]: "" memaddr: 0x181c00000 size: 10485760 memdev: /objects/mem2 Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-33-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/sgx-epc.c | 11 ++++++++++- monitor/hmp-cmds.c | 10 ++++++++++ qapi/machine.json | 39 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index 6677dc74b5..55e2217eae 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -133,7 +133,16 @@ static MemoryRegion *sgx_epc_md_get_memory_region(MemoryDeviceState *md, static void sgx_epc_md_fill_device_info(const MemoryDeviceState *md, MemoryDeviceInfo *info) { - /* TODO */ + SgxEPCDeviceInfo *se = g_new0(SgxEPCDeviceInfo, 1); + SGXEPCDevice *epc = SGX_EPC(md); + + se->memaddr = epc->addr; + se->size = object_property_get_uint(OBJECT(epc), SGX_EPC_SIZE_PROP, + NULL); + se->memdev = object_get_canonical_path(OBJECT(epc->hostmem)); + + info->u.sgx_epc.data = se; + info->type = MEMORY_DEVICE_INFO_KIND_SGX_EPC; } static void sgx_epc_class_init(ObjectClass *oc, void *data) diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index b5e71d9e6f..bcaa41350e 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -1823,6 +1823,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) VirtioMEMDeviceInfo *vmi; MemoryDeviceInfo *value; PCDIMMDeviceInfo *di; + SgxEPCDeviceInfo *se; for (info = info_list; info; info = info->next) { value = info->value; @@ -1870,6 +1871,15 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) vmi->block_size); monitor_printf(mon, " memdev: %s\n", vmi->memdev); break; + case MEMORY_DEVICE_INFO_KIND_SGX_EPC: + se = value->u.sgx_epc.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + se->id ? se->id : ""); + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", se->memaddr); + monitor_printf(mon, " size: %" PRIu64 "\n", se->size); + monitor_printf(mon, " memdev: %s\n", se->memdev); + break; default: g_assert_not_reached(); } diff --git a/qapi/machine.json b/qapi/machine.json index 26c539fe2c..e2f01e9c15 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1194,13 +1194,36 @@ } } +## +# @SgxEPCDeviceInfo: +# +# Sgx EPC state information +# +# @id: device's ID +# +# @memaddr: physical address in memory, where device is mapped +# +# @size: size of memory that the device provides +# +# @memdev: memory backend linked with device +# +# Since: 6.2 +## +{ 'struct': 'SgxEPCDeviceInfo', + 'data': { '*id': 'str', + 'memaddr': 'size', + 'size': 'size', + 'memdev': 'str' + } +} + ## # @MemoryDeviceInfoKind: # # Since: 2.1 ## { 'enum': 'MemoryDeviceInfoKind', - 'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem' ] } + 'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc' ] } ## # @PCDIMMDeviceInfoWrapper: @@ -1225,13 +1248,22 @@ ## { 'struct': 'VirtioMEMDeviceInfoWrapper', 'data': { 'data': 'VirtioMEMDeviceInfo' } } + +## +# @SgxEPCDeviceInfoWrapper: +# +# Since: 6.2 +## +{ 'struct': 'SgxEPCDeviceInfoWrapper', + 'data': { 'data': 'SgxEPCDeviceInfo' } } + ## # @MemoryDeviceInfo: # # Union containing information about a memory device # # nvdimm is included since 2.12. virtio-pmem is included since 4.1. -# virtio-mem is included since 5.1. +# virtio-mem is included since 5.1. sgx-epc is included since 6.2. # # Since: 2.1 ## @@ -1241,7 +1273,8 @@ 'data': { 'dimm': 'PCDIMMDeviceInfoWrapper', 'nvdimm': 'PCDIMMDeviceInfoWrapper', 'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper', - 'virtio-mem': 'VirtioMEMDeviceInfoWrapper' + 'virtio-mem': 'VirtioMEMDeviceInfoWrapper', + 'sgx-epc': 'SgxEPCDeviceInfoWrapper' } } From c5348c6a163f6956e7f640902b7401a1b4bad8c7 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 19 Jul 2021 19:21:36 +0800 Subject: [PATCH 0129/1334] docs/system: Add SGX documentation to the system manual Signed-off-by: Sean Christopherson Signed-off-by: Yang Zhong Message-Id: <20210719112136.57018-34-yang.zhong@intel.com> [Convert to reStructuredText, and adopt the standard === --- ~~~ headings suggested for example by Linux. - Paolo] Signed-off-by: Paolo Bonzini --- docs/system/i386/sgx.rst | 165 ++++++++++++++++++++++++++++++++++++ docs/system/target-i386.rst | 1 + 2 files changed, 166 insertions(+) create mode 100644 docs/system/i386/sgx.rst diff --git a/docs/system/i386/sgx.rst b/docs/system/i386/sgx.rst new file mode 100644 index 0000000000..f103ae2a2f --- /dev/null +++ b/docs/system/i386/sgx.rst @@ -0,0 +1,165 @@ +Software Guard eXtensions (SGX) +=============================== + +Overview +-------- + +Intel Software Guard eXtensions (SGX) is a set of instructions and mechanisms +for memory accesses in order to provide security accesses for sensitive +applications and data. SGX allows an application to use it's pariticular +address space as an *enclave*, which is a protected area provides confidentiality +and integrity even in the presence of privileged malware. Accesses to the +enclave memory area from any software not resident in the enclave are prevented, +including those from privileged software. + +Virtual SGX +----------- + +SGX feature is exposed to guest via SGX CPUID. Looking at SGX CPUID, we can +report the same CPUID info to guest as on host for most of SGX CPUID. With +reporting the same CPUID guest is able to use full capacity of SGX, and KVM +doesn't need to emulate those info. + +The guest's EPC base and size are determined by Qemu, and KVM needs Qemu to +notify such info to it before it can initialize SGX for guest. + +Virtual EPC +~~~~~~~~~~~ + +By default, Qemu does not assign EPC to a VM, i.e. fully enabling SGX in a VM +requires explicit allocation of EPC to the VM. Similar to other specialized +memory types, e.g. hugetlbfs, EPC is exposed as a memory backend. + +SGX EPC is enumerated through CPUID, i.e. EPC "devices" need to be realized +prior to realizing the vCPUs themselves, which occurs long before generic +devices are parsed and realized. This limitation means that EPC does not +require -maxmem as EPC is not treated as {cold,hot}plugged memory. + +Qemu does not artificially restrict the number of EPC sections exposed to a +guest, e.g. Qemu will happily allow you to create 64 1M EPC sections. Be aware +that some kernels may not recognize all EPC sections, e.g. the Linux SGX driver +is hardwired to support only 8 EPC sections. + +The following Qemu snippet creates two EPC sections, with 64M pre-allocated +to the VM and an additional 28M mapped but not allocated:: + + -object memory-backend-epc,id=mem1,size=64M,prealloc=on \ + -object memory-backend-epc,id=mem2,size=28M \ + -M sgx-epc.0.memdev=mem1,sgx-epc.1.memdev=mem2 + +Note: + +The size and location of the virtual EPC are far less restricted compared +to physical EPC. Because physical EPC is protected via range registers, +the size of the physical EPC must be a power of two (though software sees +a subset of the full EPC, e.g. 92M or 128M) and the EPC must be naturally +aligned. KVM SGX's virtual EPC is purely a software construct and only +requires the size and location to be page aligned. Qemu enforces the EPC +size is a multiple of 4k and will ensure the base of the EPC is 4k aligned. +To simplify the implementation, EPC is always located above 4g in the guest +physical address space. + +Migration +~~~~~~~~~ + +Qemu/KVM doesn't prevent live migrating SGX VMs, although from hardware's +perspective, SGX doesn't support live migration, since both EPC and the SGX +key hierarchy are bound to the physical platform. However live migration +can be supported in the sense if guest software stack can support recreating +enclaves when it suffers sudden lose of EPC; and if guest enclaves can detect +SGX keys being changed, and handle gracefully. For instance, when ERESUME fails +with #PF.SGX, guest software can gracefully detect it and recreate enclaves; +and when enclave fails to unseal sensitive information from outside, it can +detect such error and sensitive information can be provisioned to it again. + +CPUID +~~~~~ + +Due to its myriad dependencies, SGX is currently not listed as supported +in any of Qemu's built-in CPU configuration. To expose SGX (and SGX Launch +Control) to a guest, you must either use `-cpu host` to pass-through the +host CPU model, or explicitly enable SGX when using a built-in CPU model, +e.g. via `-cpu ,+sgx` or `-cpu ,+sgx,+sgxlc`. + +All SGX sub-features enumerated through CPUID, e.g. SGX2, MISCSELECT, +ATTRIBUTES, etc... can be restricted via CPUID flags. Be aware that enforcing +restriction of MISCSELECT, ATTRIBUTES and XFRM requires intercepting ECREATE, +i.e. may marginally reduce SGX performance in the guest. All SGX sub-features +controlled via -cpu are prefixed with "sgx", e.g.:: + + $ qemu-system-x86_64 -cpu help | xargs printf "%s\n" | grep sgx + sgx + sgx-debug + sgx-encls-c + sgx-enclv + sgx-exinfo + sgx-kss + sgx-mode64 + sgx-provisionkey + sgx-tokenkey + sgx1 + sgx2 + sgxlc + +The following Qemu snippet passes through the host CPU but restricts access to +the provision and EINIT token keys:: + + -cpu host,-sgx-provisionkey,-sgx-tokenkey + +SGX sub-features cannot be emulated, i.e. sub-features that are not present +in hardware cannot be forced on via '-cpu'. + +Virtualize SGX Launch Control +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Qemu SGX support for Launch Control (LC) is passive, in the sense that it +does not actively change the LC configuration. Qemu SGX provides the user +the ability to set/clear the CPUID flag (and by extension the associated +IA32_FEATURE_CONTROL MSR bit in fw_cfg) and saves/restores the LE Hash MSRs +when getting/putting guest state, but Qemu does not add new controls to +directly modify the LC configuration. Similar to hardware behavior, locking +the LC configuration to a non-Intel value is left to guest firmware. Unlike +host bios setting for SGX launch control(LC), there is no special bios setting +for SGX guest by our design. If host is in locked mode, we can still allow +creating VM with SGX. + +Feature Control +~~~~~~~~~~~~~~~ + +Qemu SGX updates the `etc/msr_feature_control` fw_cfg entry to set the SGX +(bit 18) and SGX LC (bit 17) flags based on their respective CPUID support, +i.e. existing guest firmware will automatically set SGX and SGX LC accordingly, +assuming said firmware supports fw_cfg.msr_feature_control. + +Launching a guest +----------------- + +To launch a SGX guest: + +.. parsed-literal:: + + |qemu_system_x86| \\ + -cpu host,+sgx-provisionkey \\ + -object memory-backend-epc,id=mem1,size=64M,prealloc=on \\ + -object memory-backend-epc,id=mem2,size=28M \\ + -M sgx-epc.0.memdev=mem1,sgx-epc.1.memdev=mem2 + +Utilizing SGX in the guest requires a kernel/OS with SGX support. +The support can be determined in guest by:: + + $ grep sgx /proc/cpuinfo + +and SGX epc info by:: + + $ dmesg | grep sgx + [ 1.242142] sgx: EPC section 0x180000000-0x181bfffff + [ 1.242319] sgx: EPC section 0x181c00000-0x1837fffff + +References +---------- + +- `SGX Homepage `__ + +- `SGX SDK `__ + +- SGX specification: Intel SDM Volume 3 diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst index c9720a8cd1..6a86d63863 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -26,6 +26,7 @@ Architectural features :maxdepth: 1 i386/cpu + i386/sgx .. _pcsys_005freq: From 57d874c4c7a0acbaa076a166e3da093b6edbdb0f Mon Sep 17 00:00:00 2001 From: Yang Zhong Date: Fri, 10 Sep 2021 18:22:56 +0800 Subject: [PATCH 0130/1334] target/i386: Add HMP and QMP interfaces for SGX The QMP and HMP interfaces can be used by monitor or QMP tools to retrieve the SGX information from VM side when SGX is enabled on Intel platform. Signed-off-by: Yang Zhong Message-Id: <20210910102258.46648-2-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hmp-commands-info.hx | 15 +++++++++++++ hw/i386/sgx-stub.c | 7 ++++++ hw/i386/sgx.c | 31 ++++++++++++++++++++++++++ include/hw/i386/sgx.h | 11 +++++++++ include/monitor/hmp-target.h | 1 + qapi/misc-target.json | 43 ++++++++++++++++++++++++++++++++++++ target/i386/monitor.c | 27 ++++++++++++++++++++++ tests/qtest/qmp-cmd-test.c | 1 + 8 files changed, 136 insertions(+) create mode 100644 include/hw/i386/sgx.h diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 27206ac049..4c966e8a6b 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -877,3 +877,18 @@ SRST ``info dirty_rate`` Display the vcpu dirty rate information. ERST + +#if defined(TARGET_I386) + { + .name = "sgx", + .args_type = "", + .params = "", + .help = "show intel SGX information", + .cmd = hmp_info_sgx, + }, +#endif + +SRST + ``info sgx`` + Show intel SGX information. +ERST diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 483c72bba6..485e16ecc1 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -1,6 +1,13 @@ #include "qemu/osdep.h" #include "hw/i386/pc.h" #include "hw/i386/sgx-epc.h" +#include "hw/i386/sgx.h" + +SGXInfo *sgx_get_info(Error **errp) +{ + error_setg(errp, "SGX support is not compiled in"); + return NULL; +} void pc_machine_init_sgx_epc(PCMachineState *pcms) { diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 8a18cddc3f..ea75398575 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -17,6 +17,37 @@ #include "monitor/qdev.h" #include "qapi/error.h" #include "exec/address-spaces.h" +#include "hw/i386/sgx.h" + +SGXInfo *sgx_get_info(Error **errp) +{ + SGXInfo *info = NULL; + X86MachineState *x86ms; + PCMachineState *pcms = + (PCMachineState *)object_dynamic_cast(qdev_get_machine(), + TYPE_PC_MACHINE); + if (!pcms) { + error_setg(errp, "SGX is only supported on PC machines"); + return NULL; + } + + x86ms = X86_MACHINE(pcms); + if (!x86ms->sgx_epc_list) { + error_setg(errp, "No EPC regions defined, SGX not available"); + return NULL; + } + + SGXEPCState *sgx_epc = &pcms->sgx_epc; + info = g_new0(SGXInfo, 1); + + info->sgx = true; + info->sgx1 = true; + info->sgx2 = true; + info->flc = true; + info->section_size = sgx_epc->size; + + return info; +} int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { diff --git a/include/hw/i386/sgx.h b/include/hw/i386/sgx.h new file mode 100644 index 0000000000..2bf90b3f4f --- /dev/null +++ b/include/hw/i386/sgx.h @@ -0,0 +1,11 @@ +#ifndef QEMU_SGX_H +#define QEMU_SGX_H + +#include "qom/object.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qapi/qapi-types-misc-target.h" + +SGXInfo *sgx_get_info(Error **errp); + +#endif diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h index 60fc92722a..dc53add7ee 100644 --- a/include/monitor/hmp-target.h +++ b/include/monitor/hmp-target.h @@ -49,5 +49,6 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict); void hmp_mce(Monitor *mon, const QDict *qdict); void hmp_info_local_apic(Monitor *mon, const QDict *qdict); void hmp_info_io_apic(Monitor *mon, const QDict *qdict); +void hmp_info_sgx(Monitor *mon, const QDict *qdict); #endif /* MONITOR_HMP_TARGET_H */ diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 3b05ad3dbf..e2a347cc23 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -333,3 +333,46 @@ { 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' }, 'returns': 'SevAttestationReport', 'if': 'TARGET_I386' } + +## +# @SGXInfo: +# +# Information about intel Safe Guard eXtension (SGX) support +# +# @sgx: true if SGX is supported +# +# @sgx1: true if SGX1 is supported +# +# @sgx2: true if SGX2 is supported +# +# @flc: true if FLC is supported +# +# @section-size: The EPC section size for guest +# +# Since: 6.2 +## +{ 'struct': 'SGXInfo', + 'data': { 'sgx': 'bool', + 'sgx1': 'bool', + 'sgx2': 'bool', + 'flc': 'bool', + 'section-size': 'uint64'}, + 'if': 'TARGET_I386' } + +## +# @query-sgx: +# +# Returns information about SGX +# +# Returns: @SGXInfo +# +# Since: 6.2 +# +# Example: +# +# -> { "execute": "query-sgx" } +# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, +# "flc": true, "section-size" : 0 } } +# +## +{ 'command': 'query-sgx', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 119211f0b0..d7384ba348 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -35,6 +35,7 @@ #include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" #include "hw/i386/pc.h" +#include "hw/i386/sgx.h" /* Perform linear address sign extension */ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr) @@ -763,3 +764,29 @@ qmp_query_sev_attestation_report(const char *mnonce, Error **errp) { return sev_get_attestation_report(mnonce, errp); } + +SGXInfo *qmp_query_sgx(Error **errp) +{ + return sgx_get_info(errp); +} + +void hmp_info_sgx(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + g_autoptr(SGXInfo) info = qmp_query_sgx(&err); + + if (err) { + error_report_err(err); + return; + } + monitor_printf(mon, "SGX support: %s\n", + info->sgx ? "enabled" : "disabled"); + monitor_printf(mon, "SGX1 support: %s\n", + info->sgx1 ? "enabled" : "disabled"); + monitor_printf(mon, "SGX2 support: %s\n", + info->sgx2 ? "enabled" : "disabled"); + monitor_printf(mon, "FLC support: %s\n", + info->flc ? "enabled" : "disabled"); + monitor_printf(mon, "size: %" PRIu64 "\n", + info->section_size); +} diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index c98b78d033..b75f3364f3 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -100,6 +100,7 @@ static bool query_is_ignored(const char *cmd) /* Success depends on Host or Hypervisor SEV support */ "query-sev", "query-sev-capabilities", + "query-sgx", NULL }; int i; From 0205c4fa1ea35d569b4c2f63adacef438c1e8f53 Mon Sep 17 00:00:00 2001 From: Yang Zhong Date: Fri, 10 Sep 2021 18:22:57 +0800 Subject: [PATCH 0131/1334] target/i386: Add the query-sgx-capabilities QMP command Libvirt can use query-sgx-capabilities to get the host sgx capabilities to decide how to allocate SGX EPC size to VM. Signed-off-by: Yang Zhong Message-Id: <20210910102258.46648-3-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/sgx-stub.c | 6 ++++ hw/i386/sgx.c | 66 ++++++++++++++++++++++++++++++++++++++ include/hw/i386/sgx.h | 1 + qapi/misc-target.json | 18 +++++++++++ target/i386/monitor.c | 5 +++ tests/qtest/qmp-cmd-test.c | 1 + 6 files changed, 97 insertions(+) diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 485e16ecc1..3be9f5ca32 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -9,6 +9,12 @@ SGXInfo *sgx_get_info(Error **errp) return NULL; } +SGXInfo *sgx_get_capabilities(Error **errp) +{ + error_setg(errp, "SGX support is not compiled in"); + return NULL; +} + void pc_machine_init_sgx_epc(PCMachineState *pcms) { memset(&pcms->sgx_epc, 0, sizeof(SGXEPCState)); diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index ea75398575..e481e9358f 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -18,6 +18,72 @@ #include "qapi/error.h" #include "exec/address-spaces.h" #include "hw/i386/sgx.h" +#include "sysemu/hw_accel.h" + +#define SGX_MAX_EPC_SECTIONS 8 +#define SGX_CPUID_EPC_INVALID 0x0 + +/* A valid EPC section. */ +#define SGX_CPUID_EPC_SECTION 0x1 +#define SGX_CPUID_EPC_MASK 0xF + +static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high) +{ + return (low & MAKE_64BIT_MASK(12, 20)) + + ((high & MAKE_64BIT_MASK(0, 20)) << 32); +} + +static uint64_t sgx_calc_host_epc_section_size(void) +{ + uint32_t i, type; + uint32_t eax, ebx, ecx, edx; + uint64_t size = 0; + + for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) { + host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx); + + type = eax & SGX_CPUID_EPC_MASK; + if (type == SGX_CPUID_EPC_INVALID) { + break; + } + + if (type != SGX_CPUID_EPC_SECTION) { + break; + } + + size += sgx_calc_section_metric(ecx, edx); + } + + return size; +} + +SGXInfo *sgx_get_capabilities(Error **errp) +{ + SGXInfo *info = NULL; + uint32_t eax, ebx, ecx, edx; + + int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); + if (fd < 0) { + error_setg(errp, "SGX is not enabled in KVM"); + return NULL; + } + + info = g_new0(SGXInfo, 1); + host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx); + + info->sgx = ebx & (1U << 2) ? true : false; + info->flc = ecx & (1U << 30) ? true : false; + + host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx); + info->sgx1 = eax & (1U << 0) ? true : false; + info->sgx2 = eax & (1U << 1) ? true : false; + + info->section_size = sgx_calc_host_epc_section_size(); + + close(fd); + + return info; +} SGXInfo *sgx_get_info(Error **errp) { diff --git a/include/hw/i386/sgx.h b/include/hw/i386/sgx.h index 2bf90b3f4f..16fc25725c 100644 --- a/include/hw/i386/sgx.h +++ b/include/hw/i386/sgx.h @@ -7,5 +7,6 @@ #include "qapi/qapi-types-misc-target.h" SGXInfo *sgx_get_info(Error **errp); +SGXInfo *sgx_get_capabilities(Error **errp); #endif diff --git a/qapi/misc-target.json b/qapi/misc-target.json index e2a347cc23..594fbd1577 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -376,3 +376,21 @@ # ## { 'command': 'query-sgx', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } + +## +# @query-sgx-capabilities: +# +# Returns information from host SGX capabilities +# +# Returns: @SGXInfo +# +# Since: 6.2 +# +# Example: +# +# -> { "execute": "query-sgx-capabilities" } +# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, +# "flc": true, "section-size" : 0 } } +# +## +{ 'command': 'query-sgx-capabilities', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } diff --git a/target/i386/monitor.c b/target/i386/monitor.c index d7384ba348..196c1c9e77 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -790,3 +790,8 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) monitor_printf(mon, "size: %" PRIu64 "\n", info->section_size); } + +SGXInfo *qmp_query_sgx_capabilities(Error **errp) +{ + return sgx_get_capabilities(errp); +} diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index b75f3364f3..1af2f74c28 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -101,6 +101,7 @@ static bool query_is_ignored(const char *cmd) "query-sev", "query-sev-capabilities", "query-sgx", + "query-sgx-capabilities", NULL }; int i; From e49c0ef6f1cca3943f3d61da6e3da7e03ddf2a22 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 23 Sep 2021 06:55:28 -0400 Subject: [PATCH 0132/1334] meson: unpack edk2 firmware even if --disable-blobs The edk2 firmware blobs are needed to run bios-tables-test. Unpack them if any UEFI-enabled target is selected, so that the test can run. This is a bit more than is actually necessary, since bios-tables-test does not run for all UEFI-enabled targets, but it is the easiest way to write this logic. Signed-off-by: Paolo Bonzini Message-Id: <20210923105529.3845741-1-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 16 ++++++++-------- pc-bios/descriptors/meson.build | 4 ++-- pc-bios/meson.build | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/meson.build b/meson.build index 7bdbbbdf02..fbdab3063b 100644 --- a/meson.build +++ b/meson.build @@ -106,14 +106,14 @@ if targetos != 'darwin' endif edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu' ] -install_edk2_blobs = false -if get_option('install_blobs') - foreach target : target_dirs - install_edk2_blobs = install_edk2_blobs or target in edk2_targets - endforeach -endif - -bzip2 = find_program('bzip2', required: install_edk2_blobs) +unpack_edk2_blobs = false +foreach target : edk2_targets + if target in target_dirs + bzip2 = find_program('bzip2', required: get_option('install_blobs')) + unpack_edk2_blobs = bzip2.found() + break + endif +endforeach ################## # Compiler flags # diff --git a/pc-bios/descriptors/meson.build b/pc-bios/descriptors/meson.build index 29efa16d99..66f85d01c4 100644 --- a/pc-bios/descriptors/meson.build +++ b/pc-bios/descriptors/meson.build @@ -1,4 +1,4 @@ -if install_edk2_blobs +if unpack_edk2_blobs and get_option('install_blobs') foreach f: [ '50-edk2-i386-secure.json', '50-edk2-x86_64-secure.json', @@ -10,7 +10,7 @@ if install_edk2_blobs configure_file(input: files(f), output: f, configuration: {'DATADIR': get_option('prefix') / qemu_datadir}, - install: get_option('install_blobs'), + install: true, install_dir: qemu_datadir / 'firmware') endforeach endif diff --git a/pc-bios/meson.build b/pc-bios/meson.build index f2b32598af..a3b3d87891 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -1,4 +1,4 @@ -if install_edk2_blobs +if unpack_edk2_blobs fds = [ 'edk2-aarch64-code.fd', 'edk2-arm-code.fd', From 809954efc279deff281ffad3d6a888760ea2b2d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 23 Sep 2021 06:55:29 -0400 Subject: [PATCH 0133/1334] tests: qtest: bios-tables-test depends on the unpacked edk2 ROMs Skip the test if bzip2 is not available, and run it after they are uncompressed. Signed-off-by: Paolo Bonzini Message-Id: <20210923105529.3845741-2-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- pc-bios/meson.build | 3 ++- tests/qtest/meson.build | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pc-bios/meson.build b/pc-bios/meson.build index a3b3d87891..a44c9bc127 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -1,3 +1,4 @@ +roms = [] if unpack_edk2_blobs fds = [ 'edk2-aarch64-code.fd', @@ -11,7 +12,7 @@ if unpack_edk2_blobs ] foreach f : fds - custom_target(f, + roms += custom_target(f, build_by_default: have_system, output: f, input: '@0@.bz2'.format(f), diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 757bb8499a..19444d4752 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -68,12 +68,12 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_RTL8139_PCI') ? ['rtl8139-test'] : []) + \ (config_all_devices.has_key('CONFIG_E1000E_PCI_EXPRESS') ? ['fuzz-e1000e-test'] : []) + \ (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \ + (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \ qtests_pci + \ ['fdc-test', 'ide-test', 'hd-geo-test', 'boot-order-test', - 'bios-tables-test', 'rtc-test', 'i440fx-test', 'fw_cfg-test', @@ -180,7 +180,7 @@ qtests_arm = \ # TODO: once aarch64 TCG is fixed on ARM 32 bit host, make bios-tables-test unconditional qtests_aarch64 = \ - (cpu != 'arm' ? ['bios-tables-test'] : []) + \ + (cpu != 'arm' and unpack_edk2_blobs ? ['bios-tables-test'] : []) + \ (config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? ['tpm-tis-device-test'] : []) + \ (config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? ['tpm-tis-device-swtpm-test'] : []) + \ ['arm-cpu-features', @@ -269,7 +269,7 @@ foreach dir : target_dirs qtest_emulator = emulators['qemu-system-' + target_base] target_qtests = get_variable('qtests_' + target_base, []) + qtests_generic - test_deps = [] + test_deps = roms qtest_env = environment() if have_tools qtest_env.set('QTEST_QEMU_IMG', './qemu-img') From 523a3d9524d5edd49dbec50c634b6e3872ad7d84 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Aug 2021 17:56:50 +0100 Subject: [PATCH 0134/1334] target/i386: Fix memory leak in sev_read_file_base64() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In sev_read_file_base64() we call g_file_get_contents(), which allocates memory for the file contents. We then base64-decode the contents (which allocates another buffer for the decoded data), but forgot to free the memory for the original file data. Use g_autofree to ensure that the file data is freed. Fixes: Coverity CID 1459997 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210820165650.2839-1-peter.maydell@linaro.org> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 0b2c8f594a..fa7210473a 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -565,7 +565,7 @@ static int sev_read_file_base64(const char *filename, guchar **data, gsize *len) { gsize sz; - gchar *base64; + g_autofree gchar *base64 = NULL; GError *error = NULL; if (!g_file_get_contents(filename, &base64, &sz, &error)) { From 142518bda515c132a46ce5826e73fbd2a5b154d9 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 16 Aug 2021 21:35:52 -0400 Subject: [PATCH 0135/1334] memory: Name all the memory listeners Provide a name field for all the memory listeners. It can be used to identify which memory listener is which. Signed-off-by: Peter Xu Reviewed-by: David Hildenbrand Message-Id: <20210817013553.30584-2-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- accel/hvf/hvf-accel-ops.c | 1 + accel/kvm/kvm-all.c | 7 +++++-- hw/i386/xen/xen-hvm.c | 2 ++ hw/intc/openpic_kvm.c | 1 + hw/remote/proxy-memory-listener.c | 1 + hw/vfio/common.c | 1 + hw/vfio/spapr.c | 1 + hw/virtio/vhost-vdpa.c | 1 + hw/virtio/vhost.c | 2 ++ hw/virtio/virtio.c | 1 + hw/xen/xen_pt.c | 2 ++ include/exec/memory.h | 8 ++++++++ include/sysemu/kvm_int.h | 2 +- softmmu/physmem.c | 1 + target/arm/kvm.c | 1 + target/i386/hax/hax-mem.c | 1 + target/i386/kvm/kvm.c | 2 +- target/i386/nvmm/nvmm-all.c | 1 + target/i386/whpx/whpx-all.c | 1 + 19 files changed, 33 insertions(+), 4 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 93976f4ece..6cbd2c3f97 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -295,6 +295,7 @@ static void hvf_region_del(MemoryListener *listener, } static MemoryListener hvf_memory_listener = { + .name = "hvf", .priority = 10, .region_add = hvf_region_add, .region_del = hvf_region_del, diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index cace5ffe64..db8d83b137 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1129,6 +1129,7 @@ static void kvm_coalesce_pio_del(MemoryListener *listener, } static MemoryListener kvm_coalesced_pio_listener = { + .name = "kvm-coalesced-pio", .coalesced_io_add = kvm_coalesce_pio_add, .coalesced_io_del = kvm_coalesce_pio_del, }; @@ -1633,7 +1634,7 @@ static void kvm_io_ioeventfd_del(MemoryListener *listener, } void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, - AddressSpace *as, int as_id) + AddressSpace *as, int as_id, const char *name) { int i; @@ -1649,6 +1650,7 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, kml->listener.log_start = kvm_log_start; kml->listener.log_stop = kvm_log_stop; kml->listener.priority = 10; + kml->listener.name = name; if (s->kvm_dirty_ring_size) { kml->listener.log_sync_global = kvm_log_sync_global; @@ -1669,6 +1671,7 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, } static MemoryListener kvm_io_listener = { + .name = "kvm-io", .eventfd_add = kvm_io_ioeventfd_add, .eventfd_del = kvm_io_ioeventfd_del, .priority = 10, @@ -2579,7 +2582,7 @@ static int kvm_init(MachineState *ms) s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; kvm_memory_listener_register(s, &s->memory_listener, - &address_space_memory, 0); + &address_space_memory, 0, "kvm-memory"); if (kvm_eventfds_allowed) { memory_listener_register(&kvm_io_listener, &address_space_io); diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 9b432773f0..e3d3d5cf89 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -721,6 +721,7 @@ static void xen_log_global_stop(MemoryListener *listener) } static MemoryListener xen_memory_listener = { + .name = "xen-memory", .region_add = xen_region_add, .region_del = xen_region_del, .log_start = xen_log_start, @@ -732,6 +733,7 @@ static MemoryListener xen_memory_listener = { }; static MemoryListener xen_io_listener = { + .name = "xen-io", .region_add = xen_io_add, .region_del = xen_io_del, .priority = 10, diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index 21da680389..557dd0c2bf 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -234,6 +234,7 @@ static void kvm_openpic_realize(DeviceState *dev, Error **errp) opp->mem_listener.region_add = kvm_openpic_region_add; opp->mem_listener.region_del = kvm_openpic_region_del; + opp->mem_listener.name = "openpic-kvm"; memory_listener_register(&opp->mem_listener, &address_space_memory); /* indicate pic capabilities */ diff --git a/hw/remote/proxy-memory-listener.c b/hw/remote/proxy-memory-listener.c index 901dbf1357..882c9b4854 100644 --- a/hw/remote/proxy-memory-listener.c +++ b/hw/remote/proxy-memory-listener.c @@ -219,6 +219,7 @@ void proxy_memory_listener_configure(ProxyMemoryListener *proxy_listener, proxy_listener->listener.region_add = proxy_memory_listener_region_addnop; proxy_listener->listener.region_nop = proxy_memory_listener_region_addnop; proxy_listener->listener.priority = 10; + proxy_listener->listener.name = "proxy"; memory_listener_register(&proxy_listener->listener, &address_space_memory); diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 1289cfa8be..a784b219e6 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1435,6 +1435,7 @@ static void vfio_listener_log_sync(MemoryListener *listener, } static const MemoryListener vfio_memory_listener = { + .name = "vfio", .region_add = vfio_listener_region_add, .region_del = vfio_listener_region_del, .log_global_start = vfio_listener_log_global_start, diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index ea3f70bd2f..04c6e67f8f 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -136,6 +136,7 @@ static void vfio_prereg_listener_region_del(MemoryListener *listener, } const MemoryListener vfio_prereg_listener = { + .name = "vfio-pre-reg", .region_add = vfio_prereg_listener_region_add, .region_del = vfio_prereg_listener_region_del, }; diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 7633ea66d1..47d7a5a23d 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -246,6 +246,7 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, * depends on the addnop(). */ static const MemoryListener vhost_vdpa_memory_listener = { + .name = "vhost-vdpa", .commit = vhost_vdpa_listener_commit, .region_add = vhost_vdpa_listener_region_add, .region_del = vhost_vdpa_listener_region_del, diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index b4b29413e6..437347ad01 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1366,6 +1366,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, hdev->features = features; hdev->memory_listener = (MemoryListener) { + .name = "vhost", .begin = vhost_begin, .commit = vhost_commit, .region_add = vhost_region_addnop, @@ -1381,6 +1382,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, }; hdev->iommu_listener = (MemoryListener) { + .name = "vhost-iommu", .region_add = vhost_iommu_region_add, .region_del = vhost_iommu_region_del, }; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 3a1f6c520c..240759ff0b 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3670,6 +3670,7 @@ static void virtio_device_realize(DeviceState *dev, Error **errp) } vdev->listener.commit = virtio_memory_listener_commit; + vdev->listener.name = "virtio"; memory_listener_register(&vdev->listener, vdev->dma_as); } diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 232482d65f..ca0a98187e 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -689,12 +689,14 @@ static void xen_pt_io_region_del(MemoryListener *l, MemoryRegionSection *sec) } static const MemoryListener xen_pt_memory_listener = { + .name = "xen-pt-mem", .region_add = xen_pt_region_add, .region_del = xen_pt_region_del, .priority = 10, }; static const MemoryListener xen_pt_io_listener = { + .name = "xen-pt-io", .region_add = xen_pt_io_region_add, .region_del = xen_pt_io_region_del, .priority = 10, diff --git a/include/exec/memory.h b/include/exec/memory.h index 9446874d21..a185b6dcb8 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -982,6 +982,14 @@ struct MemoryListener { */ unsigned priority; + /** + * @name: + * + * Name of the listener. It can be used in contexts where we'd like to + * identify one memory listener with the rest. + */ + const char *name; + /* private: */ AddressSpace *address_space; QTAILQ_ENTRY(MemoryListener) link; diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index c788452cd9..1f5487d9b7 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -37,7 +37,7 @@ typedef struct KVMMemoryListener { } KVMMemoryListener; void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, - AddressSpace *as, int as_id); + AddressSpace *as, int as_id, const char *name); void kvm_set_max_memslot_size(hwaddr max_slot_size); diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 088660d973..f67ad29981 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -756,6 +756,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx, if (tcg_enabled()) { newas->tcg_as_listener.log_global_after_sync = tcg_log_global_after_sync; newas->tcg_as_listener.commit = tcg_commit; + newas->tcg_as_listener.name = "tcg"; memory_listener_register(&newas->tcg_as_listener, as); } } diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 94b970bbf9..bbf1ce7ba3 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -335,6 +335,7 @@ static void kvm_arm_devlistener_del(MemoryListener *listener, } static MemoryListener devlistener = { + .name = "kvm-arm", .region_add = kvm_arm_devlistener_add, .region_del = kvm_arm_devlistener_del, }; diff --git a/target/i386/hax/hax-mem.c b/target/i386/hax/hax-mem.c index 8d44edbffd..a226d174d8 100644 --- a/target/i386/hax/hax-mem.c +++ b/target/i386/hax/hax-mem.c @@ -285,6 +285,7 @@ static void hax_log_sync(MemoryListener *listener, } static MemoryListener hax_memory_listener = { + .name = "hax", .begin = hax_transaction_begin, .commit = hax_transaction_commit, .region_add = hax_region_add, diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index f6bbf33bc1..7f1b060e6d 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2248,7 +2248,7 @@ static void register_smram_listener(Notifier *n, void *unused) address_space_init(&smram_address_space, &smram_as_root, "KVM-SMRAM"); kvm_memory_listener_register(kvm_state, &smram_listener, - &smram_address_space, 1); + &smram_address_space, 1, "kvm-smram"); } int kvm_arch_init(MachineState *ms, KVMState *s) diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index a488b00e90..14c996f968 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -1123,6 +1123,7 @@ nvmm_log_sync(MemoryListener *listener, MemoryRegionSection *section) } static MemoryListener nvmm_memory_listener = { + .name = "nvmm", .begin = nvmm_transaction_begin, .commit = nvmm_transaction_commit, .region_add = nvmm_region_add, diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 3e925b9da7..ef896da0a2 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1598,6 +1598,7 @@ static void whpx_log_sync(MemoryListener *listener, } static MemoryListener whpx_memory_listener = { + .name = "whpx", .begin = whpx_transaction_begin, .commit = whpx_transaction_commit, .region_add = whpx_region_add, From fcb3ab341accb8ed897f148238cae0324ed89d2c Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 16 Aug 2021 21:37:06 -0400 Subject: [PATCH 0136/1334] memory: Add tracepoint for dirty sync Trace at memory_region_sync_dirty_bitmap() for log_sync() or global_log_sync() on memory regions. One trace line should suffice when it finishes, so as to estimate the time used for each log sync process. Signed-off-by: Peter Xu Message-Id: <20210817013706.30986-1-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- softmmu/memory.c | 2 ++ softmmu/trace-events | 1 + 2 files changed, 3 insertions(+) diff --git a/softmmu/memory.c b/softmmu/memory.c index 54cd0e9824..db182e5d3d 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -2154,6 +2154,7 @@ static void memory_region_sync_dirty_bitmap(MemoryRegion *mr) } } flatview_unref(view); + trace_memory_region_sync_dirty(mr ? mr->name : "(all)", listener->name, 0); } else if (listener->log_sync_global) { /* * No matter whether MR is specified, what we can do here @@ -2161,6 +2162,7 @@ static void memory_region_sync_dirty_bitmap(MemoryRegion *mr) * sync in a finer granularity. */ listener->log_sync_global(listener); + trace_memory_region_sync_dirty(mr ? mr->name : "(all)", listener->name, 1); } } } diff --git a/softmmu/trace-events b/softmmu/trace-events index 7b278590a0..bf1469990e 100644 --- a/softmmu/trace-events +++ b/softmmu/trace-events @@ -15,6 +15,7 @@ memory_region_subpage_read(int cpu_index, void *mr, uint64_t offset, uint64_t va memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" +memory_region_sync_dirty(const char *mr, const char *listener, int global) "mr '%s' listener '%s' synced (global=%d)" flatview_new(void *view, void *root) "%p (root %p)" flatview_destroy(void *view, void *root) "%p (root %p)" flatview_destroy_rcu(void *view, void *root) "%p (root %p)" From 653163fcbcab72499ff1c2011f3552811d957729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 7 Sep 2021 16:19:13 +0400 Subject: [PATCH 0137/1334] build-sys: add HAVE_IPPROTO_MPTCP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QAPI schema shouldn't rely on C system headers #define, but on configure-time project #define, so we can express the build condition in a C-independent way. Signed-off-by: Marc-André Lureau Message-Id: <20210907121943.3498701-3-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini --- io/dns-resolver.c | 2 +- meson.build | 2 ++ qapi/sockets.json | 2 +- util/qemu-sockets.c | 6 +++--- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/io/dns-resolver.c b/io/dns-resolver.c index a5946a93bf..53b0e8407a 100644 --- a/io/dns-resolver.c +++ b/io/dns-resolver.c @@ -122,7 +122,7 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, .ipv4 = iaddr->ipv4, .has_ipv6 = iaddr->has_ipv6, .ipv6 = iaddr->ipv6, -#ifdef IPPROTO_MPTCP +#ifdef HAVE_IPPROTO_MPTCP .has_mptcp = iaddr->has_mptcp, .mptcp = iaddr->mptcp, #endif diff --git a/meson.build b/meson.build index fbdab3063b..60f4f45165 100644 --- a/meson.build +++ b/meson.build @@ -1374,6 +1374,8 @@ config_host_data.set('HAVE_OPTRESET', cc.has_header_symbol('getopt.h', 'optreset')) config_host_data.set('HAVE_UTMPX', cc.has_header_symbol('utmpx.h', 'struct utmpx')) +config_host_data.set('HAVE_IPPROTO_MPTCP', + cc.has_header_symbol('netinet/in.h', 'IPPROTO_MPTCP')) # has_member config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', diff --git a/qapi/sockets.json b/qapi/sockets.json index ef4b16d6f2..5773d9fcc4 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -69,7 +69,7 @@ '*ipv4': 'bool', '*ipv6': 'bool', '*keep-alive': 'bool', - '*mptcp': { 'type': 'bool', 'if': 'IPPROTO_MPTCP' } } } + '*mptcp': { 'type': 'bool', 'if': 'HAVE_IPPROTO_MPTCP' } } } ## # @UnixSocketAddress: diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 72216ef980..0585e7a629 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -278,7 +278,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr, /* create socket + bind/listen */ for (e = res; e != NULL; e = e->ai_next) { -#ifdef IPPROTO_MPTCP +#ifdef HAVE_IPPROTO_MPTCP if (saddr->has_mptcp && saddr->mptcp) { e->ai_protocol = IPPROTO_MPTCP; } @@ -462,7 +462,7 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) error_free(local_err); local_err = NULL; -#ifdef IPPROTO_MPTCP +#ifdef HAVE_IPPROTO_MPTCP if (saddr->has_mptcp && saddr->mptcp) { e->ai_protocol = IPPROTO_MPTCP; } @@ -699,7 +699,7 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) } addr->has_keep_alive = true; } -#ifdef IPPROTO_MPTCP +#ifdef HAVE_IPPROTO_MPTCP begin = strstr(optstr, ",mptcp"); if (begin) { if (inet_parse_flag("mptcp", begin + strlen(",mptcp"), From c1de5858bd39b299d3d8baec38b0376bed7f19e8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 Sep 2021 10:13:55 +0200 Subject: [PATCH 0138/1334] meson_options.txt: Switch the default value for the vnc option to 'auto' There is no reason why VNC should always be enabled and not be set to the default value. We already switched the setting in the "configure" script in commit 3a6a1256d4 ("configure: Allow vnc to get disabled with --without-default-features"), so let's do that in meson_options.txt now, too. Signed-off-by: Thomas Huth Reviewed-by: Eric Blake Message-Id: <20210903081358.956267-3-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- meson_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index a9a9b8f4c6..2c89e79e8b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -120,7 +120,7 @@ option('usb_redir', type : 'feature', value : 'auto', description: 'libusbredir support') option('virglrenderer', type : 'feature', value : 'auto', description: 'virgl rendering support') -option('vnc', type : 'feature', value : 'enabled', +option('vnc', type : 'feature', value : 'auto', description: 'VNC server') option('vnc_jpeg', type : 'feature', value : 'auto', description: 'JPEG lossy compression for VNC server') From db2af69d6ba836a264878dbf2cf676d3c1fc46b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:28 -0400 Subject: [PATCH 0139/1334] linux-user: Add infrastructure for a signal trampoline page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allocate a page to hold the signal trampoline(s). Invoke a guest-specific hook to fill in the contents of the page before marking it read-execute again. Reviewed-by: Max Filippov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-2-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 18 ++++++++++++++++++ linux-user/signal-common.h | 6 ++++++ linux-user/signal.c | 3 +++ 3 files changed, 27 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5f9e2141ad..459a26ef1d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -7,6 +7,7 @@ #include "qemu.h" #include "user-internals.h" +#include "signal-common.h" #include "loader.h" #include "user-mmap.h" #include "disas/disas.h" @@ -17,6 +18,7 @@ #include "qemu/units.h" #include "qemu/selfmap.h" #include "qapi/error.h" +#include "target_signal.h" #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -28,6 +30,10 @@ #undef ELF_ARCH #endif +#ifndef TARGET_ARCH_HAS_SIGTRAMP_PAGE +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 +#endif + #define ELF_OSABI ELFOSABI_SYSV /* from personality.h */ @@ -3249,6 +3255,18 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) #endif } + /* + * TODO: load a vdso, which would also contain the signal trampolines. + * Otherwise, allocate a private page to hold them. + */ + if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) { + abi_ulong tramp_page = target_mmap(0, TARGET_PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + setup_sigtramp(tramp_page); + target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC); + } + bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex, info, (elf_interpreter ? &interp_info : NULL)); info->start_stack = bprm->p; diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 79511becb4..7457f8025c 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -20,6 +20,12 @@ #ifndef SIGNAL_COMMON_H #define SIGNAL_COMMON_H +/* Fallback addresses into sigtramp page. */ +extern abi_ulong default_sigreturn; +extern abi_ulong default_rt_sigreturn; + +void setup_sigtramp(abi_ulong tramp_page); + int on_sig_stack(unsigned long sp); int sas_ss_flags(unsigned long sp); abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka); diff --git a/linux-user/signal.c b/linux-user/signal.c index 2038216455..14d8fdfde1 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -35,6 +35,9 @@ static struct target_sigaction sigact_table[TARGET_NSIG]; static void host_signal_handler(int host_signum, siginfo_t *info, void *puc); +/* Fallback addresses into sigtramp page. */ +abi_ulong default_sigreturn; +abi_ulong default_rt_sigreturn; /* * System includes define _NSIG as SIGRTMAX + 1, From c70887a38212fd8ba0141d61502a33cda0a38d2d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:29 -0400 Subject: [PATCH 0140/1334] linux-user/aarch64: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the rt signal trampoline. Use it when the guest does not use SA_RESTORER. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-3-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/aarch64/signal.c | 34 ++++++++++++++++++------------ linux-user/aarch64/target_signal.h | 2 ++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 49025648cb..29c52db3f1 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -109,7 +109,6 @@ struct target_rt_sigframe { struct target_rt_frame_record { uint64_t fp; uint64_t lr; - uint32_t tramp[2]; }; static void target_setup_general_frame(struct target_rt_sigframe *sf, @@ -461,9 +460,9 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, layout.total_size = MAX(layout.total_size, sizeof(struct target_rt_sigframe)); - /* Reserve space for the return code. On a real system this would - * be within the VDSO. So, despite the name this is not a "real" - * record within the frame. + /* + * Reserve space for the standard frame unwind pair: fp, lr. + * Despite the name this is not a "real" record within the frame. */ fr_ofs = layout.total_size; layout.total_size += sizeof(struct target_rt_frame_record); @@ -496,15 +495,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, if (ka->sa_flags & TARGET_SA_RESTORER) { return_addr = ka->sa_restorer; } else { - /* - * mov x8,#__NR_rt_sigreturn; svc #0 - * Since these are instructions they need to be put as little-endian - * regardless of target default or current CPU endianness. - */ - __put_user_e(0xd2801168, &fr->tramp[0], le); - __put_user_e(0xd4000001, &fr->tramp[1], le); - return_addr = frame_addr + fr_ofs - + offsetof(struct target_rt_frame_record, tramp); + return_addr = default_rt_sigreturn; } env->xregs[0] = usig; env->xregs[29] = frame_addr + fr_ofs; @@ -577,3 +568,20 @@ long do_sigreturn(CPUARMState *env) { return do_rt_sigreturn(env); } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0); + assert(tramp != NULL); + + /* + * mov x8,#__NR_rt_sigreturn; svc #0 + * Since these are instructions they need to be put as little-endian + * regardless of target default or current CPU endianness. + */ + __put_user_e(0xd2801168, &tramp[0], le); + __put_user_e(0xd4000001, &tramp[1], le); + + default_rt_sigreturn = sigtramp_page; + unlock_user(tramp, sigtramp_page, 8); +} diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h index 18013e1b23..7580d99403 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -25,4 +25,6 @@ typedef struct target_sigaltstack { #define TARGET_SEGV_MTESERR 9 /* Synchronous ARM MTE exception */ #define TARGET_ARCH_HAS_SETUP_FRAME +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* AARCH64_TARGET_SIGNAL_H */ From eef9790007e4282b808558f93089ad0b204487f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:30 -0400 Subject: [PATCH 0141/1334] linux-user/arm: Drop v1 signal frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Version 2 signal frames are used from 2.6.12 and since cbc14e6f286, we have set UNAME_MINIMUM_RELEASE to 2.6.32. Suggested-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-4-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/arm/signal.c | 220 +--------------------------------------- 1 file changed, 4 insertions(+), 216 deletions(-) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index ed144f9455..d0940bab47 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -46,14 +46,6 @@ struct target_sigcontext { abi_ulong fault_address; }; -struct target_ucontext_v1 { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - struct target_ucontext_v2 { abi_ulong tuc_flags; abi_ulong tuc_link; @@ -98,28 +90,12 @@ struct target_iwmmxt_sigframe { #define TARGET_VFP_MAGIC 0x56465001 #define TARGET_IWMMXT_MAGIC 0x12ef842a -struct sigframe_v1 -{ - struct target_sigcontext sc; - abi_ulong extramask[TARGET_NSIG_WORDS-1]; - abi_ulong retcode[4]; -}; - struct sigframe_v2 { struct target_ucontext_v2 uc; abi_ulong retcode[4]; }; -struct rt_sigframe_v1 -{ - abi_ulong pinfo; - abi_ulong puc; - struct target_siginfo info; - struct target_ucontext_v1 uc; - abi_ulong retcode[4]; -}; - struct rt_sigframe_v2 { struct target_siginfo info; @@ -363,37 +339,6 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc, } } -/* compare linux/arch/arm/kernel/signal.c:setup_frame() */ -static void setup_frame_v1(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) -{ - struct sigframe_v1 *frame; - abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); - int i; - - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - setup_sigcontext(&frame->sc, regs, set->sig[0]); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - if (setup_return(regs, ka, frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v1, retcode))) { - goto sigsegv; - } - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(usig); -} - static void setup_frame_v2(int usig, struct target_sigaction *ka, target_sigset_t *set, CPUARMState *regs) { @@ -422,60 +367,7 @@ sigsegv: void setup_frame(int usig, struct target_sigaction *ka, target_sigset_t *set, CPUARMState *regs) { - if (get_osversion() >= 0x020612) { - setup_frame_v2(usig, ka, set, regs); - } else { - setup_frame_v1(usig, ka, set, regs); - } -} - -/* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */ -static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env) -{ - struct rt_sigframe_v1 *frame; - abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); - struct target_sigaltstack stack; - int i; - abi_ulong info_addr, uc_addr; - - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info); - __put_user(info_addr, &frame->pinfo); - uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc); - __put_user(uc_addr, &frame->puc); - tswap_siginfo(&frame->info, info); - - /* Clear all the bits of the ucontext we don't use. */ - memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); - - memset(&stack, 0, sizeof(stack)); - target_save_altstack(&stack, env); - memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack)); - - setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]); - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - if (setup_return(env, ka, frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v1, retcode))) { - goto sigsegv; - } - - env->regs[1] = info_addr; - env->regs[2] = uc_addr; - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(usig); + setup_frame_v2(usig, ka, set, regs); } static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, @@ -516,11 +408,7 @@ void setup_rt_frame(int usig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUARMState *env) { - if (get_osversion() >= 0x020612) { - setup_rt_frame_v2(usig, ka, info, set, env); - } else { - setup_rt_frame_v1(usig, ka, info, set, env); - } + setup_rt_frame_v2(usig, ka, info, set, env); } static int @@ -553,54 +441,6 @@ restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) return err; } -static long do_sigreturn_v1(CPUARMState *env) -{ - abi_ulong frame_addr; - struct sigframe_v1 *frame = NULL; - target_sigset_t set; - sigset_t host_set; - int i; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - __get_user(set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(set.sig[i], &frame->extramask[i - 1]); - } - - target_to_host_sigset_internal(&host_set, &set); - set_sigmask(&host_set); - - if (restore_sigcontext(env, &frame->sc)) { - goto badframe; - } - -#if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); -#endif - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) { int i; @@ -733,55 +573,7 @@ badframe: long do_sigreturn(CPUARMState *env) { - if (get_osversion() >= 0x020612) { - return do_sigreturn_v2(env); - } else { - return do_sigreturn_v1(env); - } -} - -static long do_rt_sigreturn_v1(CPUARMState *env) -{ - abi_ulong frame_addr; - struct rt_sigframe_v1 *frame = NULL; - sigset_t host_set; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_rt_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); - set_sigmask(&host_set); - - if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { - goto badframe; - } - - target_restore_altstack(&frame->uc.tuc_stack, env); - -#if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); -#endif - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; + return do_sigreturn_v2(env); } static long do_rt_sigreturn_v2(CPUARMState *env) @@ -822,9 +614,5 @@ badframe: long do_rt_sigreturn(CPUARMState *env) { - if (get_osversion() >= 0x020612) { - return do_rt_sigreturn_v2(env); - } else { - return do_rt_sigreturn_v1(env); - } + return do_rt_sigreturn_v2(env); } From b807a1087ed864f6c37ccbf6ea416387f00a8b3c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:31 -0400 Subject: [PATCH 0142/1334] linux-user/arm: Drop "_v2" from symbols in signal.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we no longer support "v1", there's no need to distinguish "v2". Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-5-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/arm/signal.c | 155 +++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 90 deletions(-) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index d0940bab47..ed7d1d80bb 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -46,7 +46,7 @@ struct target_sigcontext { abi_ulong fault_address; }; -struct target_ucontext_v2 { +struct target_ucontext { abi_ulong tuc_flags; abi_ulong tuc_link; target_stack_t tuc_stack; @@ -90,16 +90,16 @@ struct target_iwmmxt_sigframe { #define TARGET_VFP_MAGIC 0x56465001 #define TARGET_IWMMXT_MAGIC 0x12ef842a -struct sigframe_v2 +struct sigframe { - struct target_ucontext_v2 uc; + struct target_ucontext uc; abi_ulong retcode[4]; }; -struct rt_sigframe_v2 +struct rt_sigframe { struct target_siginfo info; - struct target_ucontext_v2 uc; + struct target_ucontext uc; abi_ulong retcode[4]; }; @@ -270,7 +270,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, return 0; } -static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) +static abi_ulong *setup_sigframe_vfp(abi_ulong *regspace, CPUARMState *env) { int i; struct target_vfp_sigframe *vfpframe; @@ -287,8 +287,7 @@ static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) return (abi_ulong*)(vfpframe+1); } -static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, - CPUARMState *env) +static abi_ulong *setup_sigframe_iwmmxt(abi_ulong *regspace, CPUARMState *env) { int i; struct target_iwmmxt_sigframe *iwmmxtframe; @@ -307,15 +306,15 @@ static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, return (abi_ulong*)(iwmmxtframe+1); } -static void setup_sigframe_v2(struct target_ucontext_v2 *uc, - target_sigset_t *set, CPUARMState *env) +static void setup_sigframe(struct target_ucontext *uc, + target_sigset_t *set, CPUARMState *env) { struct target_sigaltstack stack; int i; abi_ulong *regspace; /* Clear all the bits of the ucontext we don't use. */ - memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); + memset(uc, 0, offsetof(struct target_ucontext, tuc_mcontext)); memset(&stack, 0, sizeof(stack)); target_save_altstack(&stack, env); @@ -325,10 +324,10 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc, /* Save coprocessor signal frame. */ regspace = uc->tuc_regspace; if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { - regspace = setup_sigframe_v2_vfp(regspace, env); + regspace = setup_sigframe_vfp(regspace, env); } if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = setup_sigframe_v2_iwmmxt(regspace, env); + regspace = setup_sigframe_iwmmxt(regspace, env); } /* Write terminating magic word */ @@ -339,10 +338,10 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc, } } -static void setup_frame_v2(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) +void setup_frame(int usig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *regs) { - struct sigframe_v2 *frame; + struct sigframe *frame; abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); trace_user_setup_frame(regs, frame_addr); @@ -350,10 +349,10 @@ static void setup_frame_v2(int usig, struct target_sigaction *ka, goto sigsegv; } - setup_sigframe_v2(&frame->uc, set, regs); + setup_sigframe(&frame->uc, set, regs); if (setup_return(regs, ka, frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v2, retcode))) { + frame_addr + offsetof(struct sigframe, retcode))) { goto sigsegv; } @@ -364,51 +363,38 @@ sigsegv: force_sigsegv(usig); } -void setup_frame(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) -{ - setup_frame_v2(usig, ka, set, regs); -} - -static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env) -{ - struct rt_sigframe_v2 *frame; - abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); - abi_ulong info_addr, uc_addr; - - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); - uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc); - tswap_siginfo(&frame->info, info); - - setup_sigframe_v2(&frame->uc, set, env); - - if (setup_return(env, ka, frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v2, retcode))) { - goto sigsegv; - } - - env->regs[1] = info_addr; - env->regs[2] = uc_addr; - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(usig); -} - void setup_rt_frame(int usig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUARMState *env) { - setup_rt_frame_v2(usig, ka, info, set, env); + struct rt_sigframe *frame; + abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); + abi_ulong info_addr, uc_addr; + + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + info_addr = frame_addr + offsetof(struct rt_sigframe, info); + uc_addr = frame_addr + offsetof(struct rt_sigframe, uc); + tswap_siginfo(&frame->info, info); + + setup_sigframe(&frame->uc, set, env); + + if (setup_return(env, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct rt_sigframe, retcode))) { + goto sigsegv; + } + + env->regs[1] = info_addr; + env->regs[2] = uc_addr; + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(usig); } static int @@ -441,7 +427,7 @@ restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) return err; } -static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) +static abi_ulong *restore_sigframe_vfp(CPUARMState *env, abi_ulong *regspace) { int i; abi_ulong magic, sz; @@ -471,8 +457,8 @@ static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) return (abi_ulong*)(vfpframe + 1); } -static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, - abi_ulong *regspace) +static abi_ulong *restore_sigframe_iwmmxt(CPUARMState *env, + abi_ulong *regspace) { int i; abi_ulong magic, sz; @@ -496,9 +482,9 @@ static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, return (abi_ulong*)(iwmmxtframe + 1); } -static int do_sigframe_return_v2(CPUARMState *env, - target_ulong context_addr, - struct target_ucontext_v2 *uc) +static int do_sigframe_return(CPUARMState *env, + target_ulong context_addr, + struct target_ucontext *uc) { sigset_t host_set; abi_ulong *regspace; @@ -506,19 +492,20 @@ static int do_sigframe_return_v2(CPUARMState *env, target_to_host_sigset(&host_set, &uc->tuc_sigmask); set_sigmask(&host_set); - if (restore_sigcontext(env, &uc->tuc_mcontext)) + if (restore_sigcontext(env, &uc->tuc_mcontext)) { return 1; + } /* Restore coprocessor signal frame */ regspace = uc->tuc_regspace; if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { - regspace = restore_sigframe_v2_vfp(env, regspace); + regspace = restore_sigframe_vfp(env, regspace); if (!regspace) { return 1; } } if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = restore_sigframe_v2_iwmmxt(env, regspace); + regspace = restore_sigframe_iwmmxt(env, regspace); if (!regspace) { return 1; } @@ -535,10 +522,10 @@ static int do_sigframe_return_v2(CPUARMState *env, return 0; } -static long do_sigreturn_v2(CPUARMState *env) +long do_sigreturn(CPUARMState *env) { abi_ulong frame_addr; - struct sigframe_v2 *frame = NULL; + struct sigframe *frame = NULL; /* * Since we stacked the signal on a 64-bit boundary, @@ -555,10 +542,9 @@ static long do_sigreturn_v2(CPUARMState *env) goto badframe; } - if (do_sigframe_return_v2(env, - frame_addr - + offsetof(struct sigframe_v2, uc), - &frame->uc)) { + if (do_sigframe_return(env, + frame_addr + offsetof(struct sigframe, uc), + &frame->uc)) { goto badframe; } @@ -571,15 +557,10 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -long do_sigreturn(CPUARMState *env) -{ - return do_sigreturn_v2(env); -} - -static long do_rt_sigreturn_v2(CPUARMState *env) +long do_rt_sigreturn(CPUARMState *env) { abi_ulong frame_addr; - struct rt_sigframe_v2 *frame = NULL; + struct rt_sigframe *frame = NULL; /* * Since we stacked the signal on a 64-bit boundary, @@ -596,10 +577,9 @@ static long do_rt_sigreturn_v2(CPUARMState *env) goto badframe; } - if (do_sigframe_return_v2(env, - frame_addr - + offsetof(struct rt_sigframe_v2, uc), - &frame->uc)) { + if (do_sigframe_return(env, + frame_addr + offsetof(struct rt_sigframe, uc), + &frame->uc)) { goto badframe; } @@ -611,8 +591,3 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } - -long do_rt_sigreturn(CPUARMState *env) -{ - return do_rt_sigreturn_v2(env); -} From b5d66e0dd8dda3d58e58dafa425372d158bed3e8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:32 -0400 Subject: [PATCH 0143/1334] linux-user/arm: Implement setup_sigtramp Mirror what the kernel does in arch/arm/kernel/signal.h, using the old sigframe struct in the rt sigframe struct. Update the trampoline code to match the kernel: this uses sp-relative accesses rather than pc-relative. Copy the code into frame->retcode from the trampoline page. This minimises the different cases wrt arm vs thumb vs fdpic. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-Id: <20210929130553.121567-6-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/arm/signal.c | 181 ++++++++++++++++++++------------- linux-user/arm/target_signal.h | 2 + 2 files changed, 111 insertions(+), 72 deletions(-) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index ed7d1d80bb..df9f8e8eb2 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -99,43 +99,21 @@ struct sigframe struct rt_sigframe { struct target_siginfo info; - struct target_ucontext uc; - abi_ulong retcode[4]; + struct sigframe sig; }; +static abi_ptr sigreturn_fdpic_tramp; + /* - * For ARM syscalls, we encode the syscall number into the instruction. + * Up to 3 words of 'retcode' in the sigframe are code, + * with retcode[3] being used by fdpic for the function descriptor. + * This code is not actually executed, but is retained for ABI compat. + * + * We will create a table of 8 retcode variants in the sigtramp page. + * Let each table entry use 3 words. */ -#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE)) -#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE)) - -/* - * For Thumb syscalls, we pass the syscall number via r7. We therefore - * need two 16-bit instructions. - */ -#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn)) -#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn)) - -static const abi_ulong retcodes[4] = { - SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, - SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN -}; - -/* - * Stub needed to make sure the FD register (r9) contains the right - * value. - */ -static const unsigned long sigreturn_fdpic_codes[3] = { - 0xe59fc004, /* ldr r12, [pc, #4] to read function descriptor */ - 0xe59c9004, /* ldr r9, [r12, #4] to setup GOT */ - 0xe59cf000 /* ldr pc, [r12] to jump into restorer */ -}; - -static const unsigned long sigreturn_fdpic_thumb_codes[3] = { - 0xc008f8df, /* ldr r12, [pc, #8] to read function descriptor */ - 0x9004f8dc, /* ldr r9, [r12, #4] to setup GOT */ - 0xf000f8dc /* ldr pc, [r12] to jump into restorer */ -}; +#define RETCODE_WORDS 3 +#define RETCODE_BYTES (RETCODE_WORDS * 4) static inline int valid_user_regs(CPUARMState *regs) { @@ -183,15 +161,15 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) } static int -setup_return(CPUARMState *env, struct target_sigaction *ka, - abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) +setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, + struct sigframe *frame, abi_ulong sp_addr) { abi_ulong handler = 0; abi_ulong handler_fdpic_GOT = 0; abi_ulong retcode; - - int thumb; + int thumb, retcode_idx; int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); + bool copy_retcode; if (is_fdpic) { /* In FDPIC mode, ka->_sa_handler points to a function @@ -208,6 +186,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, } thumb = handler & 1; + retcode_idx = thumb + (ka->sa_flags & TARGET_SA_SIGINFO ? 2 : 0); uint32_t cpsr = cpsr_read(env); @@ -225,44 +204,29 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, if (ka->sa_flags & TARGET_SA_RESTORER) { if (is_fdpic) { - /* For FDPIC we ensure that the restorer is called with a - * correct r9 value. For that we need to write code on - * the stack that sets r9 and jumps back to restorer - * value. - */ - if (thumb) { - __put_user(sigreturn_fdpic_thumb_codes[0], rc); - __put_user(sigreturn_fdpic_thumb_codes[1], rc + 1); - __put_user(sigreturn_fdpic_thumb_codes[2], rc + 2); - __put_user((abi_ulong)ka->sa_restorer, rc + 3); - } else { - __put_user(sigreturn_fdpic_codes[0], rc); - __put_user(sigreturn_fdpic_codes[1], rc + 1); - __put_user(sigreturn_fdpic_codes[2], rc + 2); - __put_user((abi_ulong)ka->sa_restorer, rc + 3); - } - - retcode = rc_addr + thumb; + __put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]); + retcode = (sigreturn_fdpic_tramp + + retcode_idx * RETCODE_BYTES + thumb); + copy_retcode = true; } else { retcode = ka->sa_restorer; + copy_retcode = false; } } else { - unsigned int idx = thumb; + retcode = default_sigreturn + retcode_idx * RETCODE_BYTES + thumb; + copy_retcode = true; + } - if (ka->sa_flags & TARGET_SA_SIGINFO) { - idx += 2; - } - - __put_user(retcodes[idx], rc); - - retcode = rc_addr + thumb; + /* Copy the code to the stack slot for ABI compatibility. */ + if (copy_retcode) { + memcpy(frame->retcode, g2h_untagged(retcode & ~1), RETCODE_BYTES); } env->regs[0] = usig; if (is_fdpic) { env->regs[9] = handler_fdpic_GOT; } - env->regs[13] = frame_addr; + env->regs[13] = sp_addr; env->regs[14] = retcode; env->regs[15] = handler & (thumb ? ~1 : ~3); cpsr_write(env, cpsr, CPSR_IT | CPSR_T | CPSR_E, CPSRWriteByInstr); @@ -351,8 +315,7 @@ void setup_frame(int usig, struct target_sigaction *ka, setup_sigframe(&frame->uc, set, regs); - if (setup_return(regs, ka, frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe, retcode))) { + if (setup_return(regs, ka, usig, frame, frame_addr)) { goto sigsegv; } @@ -377,13 +340,12 @@ void setup_rt_frame(int usig, struct target_sigaction *ka, } info_addr = frame_addr + offsetof(struct rt_sigframe, info); - uc_addr = frame_addr + offsetof(struct rt_sigframe, uc); + uc_addr = frame_addr + offsetof(struct rt_sigframe, sig.uc); tswap_siginfo(&frame->info, info); - setup_sigframe(&frame->uc, set, env); + setup_sigframe(&frame->sig.uc, set, env); - if (setup_return(env, ka, frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe, retcode))) { + if (setup_return(env, ka, usig, &frame->sig, frame_addr)) { goto sigsegv; } @@ -578,8 +540,8 @@ long do_rt_sigreturn(CPUARMState *env) } if (do_sigframe_return(env, - frame_addr + offsetof(struct rt_sigframe, uc), - &frame->uc)) { + frame_addr + offsetof(struct rt_sigframe, sig.uc), + &frame->sig.uc)) { goto badframe; } @@ -591,3 +553,78 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +/* + * EABI syscalls pass the number via r7. + * Note that the kernel still adds the OABI syscall number to the trap, + * presumably for backward ABI compatibility with unwinders. + */ +#define ARM_MOV_R7_IMM(X) (0xe3a07000 | (X)) +#define ARM_SWI_SYS(X) (0xef000000 | (X) | ARM_SYSCALL_BASE) + +#define THUMB_MOVS_R7_IMM(X) (0x2700 | (X)) +#define THUMB_SWI_SYS 0xdf00 + +static void write_arm_sigreturn(uint32_t *rc, int syscall) +{ + __put_user(ARM_MOV_R7_IMM(syscall), rc); + __put_user(ARM_SWI_SYS(syscall), rc + 1); + /* Wrote 8 of 12 bytes */ +} + +static void write_thm_sigreturn(uint32_t *rc, int syscall) +{ + __put_user(THUMB_SWI_SYS << 16 | THUMB_MOVS_R7_IMM(syscall), rc); + /* Wrote 4 of 12 bytes */ +} + +/* + * Stub needed to make sure the FD register (r9) contains the right value. + * Use the same instruction sequence as the kernel. + */ +static void write_arm_fdpic_sigreturn(uint32_t *rc, int ofs) +{ + assert(ofs <= 0xfff); + __put_user(0xe59d3000 | ofs, rc + 0); /* ldr r3, [sp, #ofs] */ + __put_user(0xe8930908, rc + 1); /* ldm r3, { r3, r9 } */ + __put_user(0xe12fff13, rc + 2); /* bx r3 */ + /* Wrote 12 of 12 bytes */ +} + +static void write_thm_fdpic_sigreturn(void *vrc, int ofs) +{ + uint16_t *rc = vrc; + + assert((ofs & ~0x3fc) == 0); + __put_user(0x9b00 | (ofs >> 2), rc + 0); /* ldr r3, [sp, #ofs] */ + __put_user(0xcb0c, rc + 1); /* ldm r3, { r2, r3 } */ + __put_user(0x4699, rc + 2); /* mov r9, r3 */ + __put_user(0x4710, rc + 3); /* bx r2 */ + /* Wrote 8 of 12 bytes */ +} + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t total_size = 8 * RETCODE_BYTES; + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, total_size, 0); + + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + write_arm_sigreturn(&tramp[0 * RETCODE_WORDS], TARGET_NR_sigreturn); + write_thm_sigreturn(&tramp[1 * RETCODE_WORDS], TARGET_NR_sigreturn); + write_arm_sigreturn(&tramp[2 * RETCODE_WORDS], TARGET_NR_rt_sigreturn); + write_thm_sigreturn(&tramp[3 * RETCODE_WORDS], TARGET_NR_rt_sigreturn); + + sigreturn_fdpic_tramp = sigtramp_page + 4 * RETCODE_BYTES; + write_arm_fdpic_sigreturn(tramp + 4 * RETCODE_WORDS, + offsetof(struct sigframe, retcode[3])); + write_thm_fdpic_sigreturn(tramp + 5 * RETCODE_WORDS, + offsetof(struct sigframe, retcode[3])); + write_arm_fdpic_sigreturn(tramp + 6 * RETCODE_WORDS, + offsetof(struct rt_sigframe, sig.retcode[3])); + write_thm_fdpic_sigreturn(tramp + 7 * RETCODE_WORDS, + offsetof(struct rt_sigframe, sig.retcode[3])); + + unlock_user(tramp, sigtramp_page, total_size); +} diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h index 0998dd6dfa..1e7fb0cecb 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -22,4 +22,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" #define TARGET_ARCH_HAS_SETUP_FRAME +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* ARM_TARGET_SIGNAL_H */ From dd55f1c8b217e14293f42d8531371b7c626c61dd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:33 -0400 Subject: [PATCH 0144/1334] linux-user/alpha: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the two signal trampolines. Use them when the guest does not use ka_restorer. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-7-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/alpha/signal.c | 34 +++++++++++++++++++------------- linux-user/alpha/target_signal.h | 1 + 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c index 3a820f616b..bbe3dd175a 100644 --- a/linux-user/alpha/signal.c +++ b/linux-user/alpha/signal.c @@ -55,13 +55,11 @@ struct target_ucontext { struct target_sigframe { struct target_sigcontext sc; - unsigned int retcode[3]; }; struct target_rt_sigframe { target_siginfo_t info; struct target_ucontext uc; - unsigned int retcode[3]; }; #define INSN_MOV_R30_R16 0x47fe0410 @@ -142,12 +140,7 @@ void setup_frame(int sig, struct target_sigaction *ka, if (ka->ka_restorer) { r26 = ka->ka_restorer; } else { - __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); - __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, - &frame->retcode[1]); - __put_user(INSN_CALLSYS, &frame->retcode[2]); - /* imb() */ - r26 = frame_addr + offsetof(struct target_sigframe, retcode); + r26 = default_sigreturn; } unlock_user_struct(frame, frame_addr, 1); @@ -196,12 +189,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, if (ka->ka_restorer) { r26 = ka->ka_restorer; } else { - __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); - __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, - &frame->retcode[1]); - __put_user(INSN_CALLSYS, &frame->retcode[2]); - /* imb(); */ - r26 = frame_addr + offsetof(struct target_rt_sigframe, retcode); + r26 = default_rt_sigreturn; } if (err) { @@ -269,3 +257,21 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6 * 4, 0); + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + __put_user(INSN_MOV_R30_R16, &tramp[0]); + __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, &tramp[1]); + __put_user(INSN_CALLSYS, &tramp[2]); + + default_rt_sigreturn = sigtramp_page + 3 * 4; + __put_user(INSN_MOV_R30_R16, &tramp[3]); + __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, &tramp[4]); + __put_user(INSN_CALLSYS, &tramp[5]); + + unlock_user(tramp, sigtramp_page, 6 * 4); +} diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h index 250642913e..0b6a39de65 100644 --- a/linux-user/alpha/target_signal.h +++ b/linux-user/alpha/target_signal.h @@ -93,6 +93,7 @@ typedef struct target_sigaltstack { #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_KA_RESTORER +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 /* bit-flags */ #define TARGET_SS_AUTODISARM (1U << 31) /* disable sas during sighandling */ From e281c2bafe22135c47caf7e1fa9db19647e49c3d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:34 -0400 Subject: [PATCH 0145/1334] linux-user/cris: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out setup_sigreturn so that we can continue to initialize the words on the stack, as documented. However, use the off-stack trampoline. Cc: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-8-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/cris/signal.c | 29 +++++++++++++++++++++-------- linux-user/cris/target_signal.h | 2 ++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c index 2c39bdf727..7f6aca934e 100644 --- a/linux-user/cris/signal.c +++ b/linux-user/cris/signal.c @@ -97,6 +97,14 @@ static abi_ulong get_sigframe(CPUCRISState *env, int framesize) return sp - framesize; } +static void setup_sigreturn(uint16_t *retcode) +{ + /* This is movu.w __NR_sigreturn, r9; break 13; */ + __put_user(0x9c5f, retcode + 0); + __put_user(TARGET_NR_sigreturn, retcode + 1); + __put_user(0xe93d, retcode + 2); +} + void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUCRISState *env) { @@ -112,14 +120,8 @@ void setup_frame(int sig, struct target_sigaction *ka, /* * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't * use this trampoline anymore but it sets it up for GDB. - * In QEMU, using the trampoline simplifies things a bit so we use it. - * - * This is movu.w __NR_sigreturn, r9; break 13; */ - __put_user(0x9c5f, frame->retcode+0); - __put_user(TARGET_NR_sigreturn, - frame->retcode + 1); - __put_user(0xe93d, frame->retcode + 2); + setup_sigreturn(frame->retcode); /* Save the mask. */ __put_user(set->sig[0], &frame->sc.oldmask); @@ -135,7 +137,7 @@ void setup_frame(int sig, struct target_sigaction *ka, env->regs[10] = sig; env->pc = (unsigned long) ka->_sa_handler; /* Link SRP so the guest returns through the trampoline. */ - env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode); + env->pregs[PR_SRP] = default_sigreturn; unlock_user_struct(frame, frame_addr, 1); return; @@ -187,3 +189,14 @@ long do_rt_sigreturn(CPUCRISState *env) qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6, 0); + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + setup_sigreturn(tramp); + + unlock_user(tramp, sigtramp_page, 6); +} diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h index 495a142896..83a5155507 100644 --- a/linux-user/cris/target_signal.h +++ b/linux-user/cris/target_signal.h @@ -22,4 +22,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" #define TARGET_ARCH_HAS_SETUP_FRAME +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* CRIS_TARGET_SIGNAL_H */ From c8ef02b1acf0d78e909c6cbd38ed60ecc5ce52ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:35 -0400 Subject: [PATCH 0146/1334] linux-user/hexagon: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Continue to initialize the words on the stack, as documented. However, use the off-stack trampoline. Reviewed-by: Taylor Simpson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-9-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/hexagon/signal.c | 19 +++++++++++++++++-- linux-user/hexagon/target_signal.h | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/linux-user/hexagon/signal.c b/linux-user/hexagon/signal.c index c7f0bf6b92..74e61739a0 100644 --- a/linux-user/hexagon/signal.c +++ b/linux-user/hexagon/signal.c @@ -162,6 +162,11 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, setup_ucontext(&frame->uc, env, set); tswap_siginfo(&frame->info, info); + /* + * The on-stack signal trampoline is no longer executed; + * however, the libgcc signal frame unwinding code checks + * for the presence of these two numeric magic values. + */ install_sigtramp(frame->tramp); env->gpr[HEX_REG_PC] = ka->_sa_handler; @@ -171,8 +176,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, frame_addr + offsetof(struct target_rt_sigframe, info); env->gpr[HEX_REG_R02] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->gpr[HEX_REG_LR] = - frame_addr + offsetof(struct target_rt_sigframe, tramp); + env->gpr[HEX_REG_LR] = default_rt_sigreturn; return; @@ -271,3 +275,14 @@ badframe: force_sig(TARGET_SIGSEGV); return 0; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 4 * 2, 0); + assert(tramp != NULL); + + default_rt_sigreturn = sigtramp_page; + install_sigtramp(tramp); + + unlock_user(tramp, sigtramp_page, 4 * 2); +} diff --git a/linux-user/hexagon/target_signal.h b/linux-user/hexagon/target_signal.h index 345cf1cbb8..9e0223d322 100644 --- a/linux-user/hexagon/target_signal.h +++ b/linux-user/hexagon/target_signal.h @@ -31,4 +31,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* TARGET_SIGNAL_H */ From a22fccd8b7de9e21fc31cc048e2bf9c15947f4a4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:36 -0400 Subject: [PATCH 0147/1334] linux-user/hppa: Document non-use of setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We cannot use a raw sigtramp page for hppa, but must wait for full vdso support. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-10-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/hppa/target_signal.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h index 7f525362e9..d558119ee7 100644 --- a/linux-user/hppa/target_signal.h +++ b/linux-user/hppa/target_signal.h @@ -71,4 +71,18 @@ typedef struct target_sigaltstack { /* mask for all SS_xxx flags */ #define TARGET_SS_FLAG_BITS TARGET_SS_AUTODISARM +/* + * We cannot use a bare sigtramp page for hppa-linux. + * + * Unlike other guests where we use the instructions at PC to validate + * an offset from SP, the hppa libgcc signal frame fallback unwinding uses + * the PC address itself to find the frame. This is due to the fact that + * the hppa grows the stack upward, and the frame is of unknown size. + * + * TODO: We should be able to use a VDSO to address this, by providing + * proper unwind info for the sigtramp code, at which point the fallback + * unwinder will not be used. + */ +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 + #endif /* HPPA_TARGET_SIGNAL_H */ From 8ee8a104807f67595c1a1963dbee208a52cc513b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:37 -0400 Subject: [PATCH 0148/1334] linux-user/i386: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the two signal trampolines. Use them when the guest does not use SA_RESTORER. Note that x86_64 does not use this code. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-11-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/i386/signal.c | 56 +++++++++++++++++++++---------- linux-user/i386/target_signal.h | 2 ++ linux-user/x86_64/target_signal.h | 3 ++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 3b4b55fc0a..b38b5f108e 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -310,6 +310,22 @@ get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) } #ifndef TARGET_X86_64 +static void install_sigtramp(void *tramp) +{ + /* This is popl %eax ; movl $syscall,%eax ; int $0x80 */ + __put_user(0xb858, (uint16_t *)(tramp + 0)); + __put_user(TARGET_NR_sigreturn, (int32_t *)(tramp + 2)); + __put_user(0x80cd, (uint16_t *)(tramp + 6)); +} + +static void install_rt_sigtramp(void *tramp) +{ + /* This is movl $syscall,%eax ; int $0x80 */ + __put_user(0xb8, (uint8_t *)(tramp + 0)); + __put_user(TARGET_NR_rt_sigreturn, (int32_t *)(tramp + 1)); + __put_user(0x80cd, (uint16_t *)(tramp + 5)); +} + /* compare linux/arch/i386/kernel/signal.c:setup_frame() */ void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUX86State *env) @@ -338,16 +354,9 @@ void setup_frame(int sig, struct target_sigaction *ka, if (ka->sa_flags & TARGET_SA_RESTORER) { __put_user(ka->sa_restorer, &frame->pretcode); } else { - uint16_t val16; - abi_ulong retcode_addr; - retcode_addr = frame_addr + offsetof(struct sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - /* This is popl %eax ; movl $,%eax ; int $0x80 */ - val16 = 0xb858; - __put_user(val16, (uint16_t *)(frame->retcode+0)); - __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2)); - val16 = 0x80cd; - __put_user(val16, (uint16_t *)(frame->retcode+6)); + /* This is no longer used, but is retained for ABI compatibility. */ + install_sigtramp(frame->retcode); + __put_user(default_sigreturn, &frame->pretcode); } /* Set up registers for signal handler */ @@ -416,14 +425,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, if (ka->sa_flags & TARGET_SA_RESTORER) { __put_user(ka->sa_restorer, &frame->pretcode); } else { - uint16_t val16; - addr = frame_addr + offsetof(struct rt_sigframe, retcode); - __put_user(addr, &frame->pretcode); - /* This is movl $,%eax ; int $0x80 */ - __put_user(0xb8, (char *)(frame->retcode+0)); - __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1)); - val16 = 0x80cd; - __put_user(val16, (uint16_t *)(frame->retcode+5)); + /* This is no longer used, but is retained for ABI compatibility. */ + install_rt_sigtramp(frame->retcode); + __put_user(default_rt_sigreturn, &frame->pretcode); } #else /* XXX: Would be slightly better to return -EFAULT here if test fails @@ -592,3 +596,19 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +#ifndef TARGET_X86_64 +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0); + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + install_sigtramp(tramp); + + default_rt_sigreturn = sigtramp_page + 8; + install_rt_sigtramp(tramp + 8); + + unlock_user(tramp, sigtramp_page, 2 * 8); +} +#endif diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h index 50361af874..64d09f2e75 100644 --- a/linux-user/i386/target_signal.h +++ b/linux-user/i386/target_signal.h @@ -22,4 +22,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" #define TARGET_ARCH_HAS_SETUP_FRAME +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* I386_TARGET_SIGNAL_H */ diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h index 4ea74f20dd..4673c5a886 100644 --- a/linux-user/x86_64/target_signal.h +++ b/linux-user/x86_64/target_signal.h @@ -21,4 +21,7 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" +/* For x86_64, use of SA_RESTORER is mandatory. */ +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 + #endif /* X86_64_TARGET_SIGNAL_H */ From db2055415959bcc81cd6c2f0aa2c23e5f12c1eb6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:38 -0400 Subject: [PATCH 0149/1334] linux-user/x86_64: Raise SIGSEGV if SA_RESTORER not set This has been a fixme for some time. The effect of returning -EFAULT from the kernel code is to raise SIGSEGV. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-12-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/i386/signal.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index b38b5f108e..433efa3d69 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -421,19 +421,18 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Set up to return from userspace. If provided, use a stub already in userspace. */ -#ifndef TARGET_X86_64 if (ka->sa_flags & TARGET_SA_RESTORER) { __put_user(ka->sa_restorer, &frame->pretcode); } else { +#ifdef TARGET_X86_64 + /* For x86_64, SA_RESTORER is required ABI. */ + goto give_sigsegv; +#else /* This is no longer used, but is retained for ABI compatibility. */ install_rt_sigtramp(frame->retcode); __put_user(default_rt_sigreturn, &frame->pretcode); - } -#else - /* XXX: Would be slightly better to return -EFAULT here if test fails - assert(ka->sa_flags & TARGET_SA_RESTORER); */ - __put_user(ka->sa_restorer, &frame->pretcode); #endif + } /* Set up registers for signal handler */ env->regs[R_ESP] = frame_addr; From 5125aced7cd291ba232944bf980612f905e524a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:39 -0400 Subject: [PATCH 0150/1334] linux-user/m68k: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the two signal trampolines. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-13-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/m68k/signal.c | 47 +++++++++++++++------------------ linux-user/m68k/target_signal.h | 2 ++ 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c index 4f8eb6f727..ec33482e14 100644 --- a/linux-user/m68k/signal.c +++ b/linux-user/m68k/signal.c @@ -39,7 +39,6 @@ struct target_sigframe int sig; int code; abi_ulong psc; - char retcode[8]; abi_ulong extramask[TARGET_NSIG_WORDS-1]; struct target_sigcontext sc; }; @@ -76,7 +75,6 @@ struct target_rt_sigframe int sig; abi_ulong pinfo; abi_ulong puc; - char retcode[8]; struct target_siginfo info; struct target_ucontext uc; }; @@ -130,7 +128,6 @@ void setup_frame(int sig, struct target_sigaction *ka, { struct target_sigframe *frame; abi_ulong frame_addr; - abi_ulong retcode_addr; abi_ulong sc_addr; int i; @@ -152,16 +149,7 @@ void setup_frame(int sig, struct target_sigaction *ka, } /* Set up to return from userspace. */ - - retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - - /* moveq #,d0; trap #0 */ - - __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), - (uint32_t *)(frame->retcode)); - - /* Set up to return from userspace */ + __put_user(default_sigreturn, &frame->pretcode); env->aregs[7] = frame_addr; env->pc = ka->_sa_handler; @@ -288,7 +276,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, { struct target_rt_sigframe *frame; abi_ulong frame_addr; - abi_ulong retcode_addr; abi_ulong info_addr; abi_ulong uc_addr; int err = 0; @@ -325,17 +312,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, } /* Set up to return from userspace. */ - - retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - - /* moveq #,d0; notb d0; trap #0 */ - - __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16), - (uint32_t *)(frame->retcode + 0)); - __put_user(0x4e40, (uint16_t *)(frame->retcode + 4)); - - /* Set up to return from userspace */ + __put_user(default_rt_sigreturn, &frame->pretcode); env->aregs[7] = frame_addr; env->pc = ka->_sa_handler; @@ -411,3 +388,23 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + void *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 4 + 6, 0); + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + + /* moveq #,d0; trap #0 */ + __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), (uint32_t *)tramp); + + default_rt_sigreturn = sigtramp_page + 4; + + /* moveq #,d0; notb d0; trap #0 */ + __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16), + (uint32_t *)(tramp + 4)); + __put_user(0x4e40, (uint16_t *)(tramp + 8)); + + unlock_user(tramp, sigtramp_page, 4 + 6); +} diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h index d096544ef8..94157bf1f4 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -22,4 +22,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" #define TARGET_ARCH_HAS_SETUP_FRAME +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* M68K_TARGET_SIGNAL_H */ From 8004316d81c15681143c3b8d5e77f911b08cfc8b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:40 -0400 Subject: [PATCH 0151/1334] linux-user/microblaze: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the rt signal trampoline. Cc: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-14-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/microblaze/signal.c | 24 +++++++++++++++++------- linux-user/microblaze/target_signal.h | 2 ++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index b822679d18..8ebb6a1b7d 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -161,17 +161,11 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Kernel does not use SA_RESTORER. */ - /* addi r12, r0, __NR_sigreturn */ - __put_user(0x31800000U | TARGET_NR_rt_sigreturn, frame->tramp + 0); - /* brki r14, 0x8 */ - __put_user(0xb9cc0008U, frame->tramp + 1); - /* * Return from sighandler will jump to the tramp. * Negative 8 offset because return is rtsd r15, 8 */ - env->regs[15] = - frame_addr + offsetof(struct target_rt_sigframe, tramp) - 8; + env->regs[15] = default_rt_sigreturn - 8; /* Set up registers for signal handler */ env->regs[1] = frame_addr; @@ -220,3 +214,19 @@ long do_rt_sigreturn(CPUMBState *env) force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0); + assert(tramp != NULL); + + /* + * addi r12, r0, __NR_rt_sigreturn + * brki r14, 0x8 + */ + __put_user(0x31800000U | TARGET_NR_rt_sigreturn, tramp); + __put_user(0xb9cc0008U, tramp + 1); + + default_rt_sigreturn = sigtramp_page; + unlock_user(tramp, sigtramp_page, 8); +} diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h index 1c326296de..e8b510f6b1 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -21,4 +21,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* MICROBLAZE_TARGET_SIGNAL_H */ From 4f7a0a4c866fde385e65395f870d042c217d3c8b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:41 -0400 Subject: [PATCH 0152/1334] linux-user/mips: Tidy install_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return value is constant 0, and unused as well -- change to void. Drop inline marker. Change tramp type to uint32_t* for clarity. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-15-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/mips/signal.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c index d174b3453c..64072779b9 100644 --- a/linux-user/mips/signal.c +++ b/linux-user/mips/signal.c @@ -87,10 +87,8 @@ struct target_rt_sigframe { }; /* Install trampoline to jump back from signal handler */ -static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) +static void install_sigtramp(uint32_t *tramp, unsigned int syscall) { - int err = 0; - /* * Set up the return code ... * @@ -100,7 +98,6 @@ static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) __put_user(0x24020000 + syscall, tramp + 0); __put_user(0x0000000c , tramp + 1); - return err; } static inline void setup_sigcontext(CPUMIPSState *regs, From 317a33b6ebad8a9e94109b4421804f7945eb8935 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:42 -0400 Subject: [PATCH 0153/1334] linux-user/mips: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the two signal trampolines. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-16-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/mips/signal.c | 34 ++++++++++++++++++++++--------- linux-user/mips/target_signal.h | 1 + linux-user/mips64/target_signal.h | 2 ++ 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c index 64072779b9..8f79e405ec 100644 --- a/linux-user/mips/signal.c +++ b/linux-user/mips/signal.c @@ -209,8 +209,6 @@ void setup_frame(int sig, struct target_sigaction * ka, goto give_sigsegv; } - install_sigtramp(frame->sf_code, TARGET_NR_sigreturn); - setup_sigcontext(regs, &frame->sf_sc); for(i = 0; i < TARGET_NSIG_WORDS; i++) { @@ -231,7 +229,7 @@ void setup_frame(int sig, struct target_sigaction * ka, regs->active_tc.gpr[ 5] = 0; regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc); regs->active_tc.gpr[29] = frame_addr; - regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code); + regs->active_tc.gpr[31] = default_sigreturn; /* The original kernel code sets CP0_EPC to the handler * since it returns to userland using eret * we cannot do this here, and we must set PC directly */ @@ -305,8 +303,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, goto give_sigsegv; } - install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn); - tswap_siginfo(&frame->rs_info, info); __put_user(0, &frame->rs_uc.tuc_flags); @@ -335,11 +331,13 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->active_tc.gpr[ 6] = frame_addr + offsetof(struct target_rt_sigframe, rs_uc); env->active_tc.gpr[29] = frame_addr; - env->active_tc.gpr[31] = frame_addr - + offsetof(struct target_rt_sigframe, rs_code); - /* The original kernel code sets CP0_EPC to the handler - * since it returns to userland using eret - * we cannot do this here, and we must set PC directly */ + env->active_tc.gpr[31] = default_rt_sigreturn; + + /* + * The original kernel code sets CP0_EPC to the handler + * since it returns to userland using eret + * we cannot do this here, and we must set PC directly + */ env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler; mips_set_hflags_isa_mode_from_pc(env); unlock_user_struct(frame, frame_addr, 1); @@ -379,3 +377,19 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0); + assert(tramp != NULL); + +#ifdef TARGET_ARCH_HAS_SETUP_FRAME + default_sigreturn = sigtramp_page; + install_sigtramp(tramp, TARGET_NR_sigreturn); +#endif + + default_rt_sigreturn = sigtramp_page + 8; + install_sigtramp(tramp + 2, TARGET_NR_rt_sigreturn); + + unlock_user(tramp, sigtramp_page, 2 * 8); +} diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h index d521765f6b..780a4ddf29 100644 --- a/linux-user/mips/target_signal.h +++ b/linux-user/mips/target_signal.h @@ -73,6 +73,7 @@ typedef struct target_sigaltstack { /* compare linux/arch/mips/kernel/signal.c:setup_frame() */ #define TARGET_ARCH_HAS_SETUP_FRAME #endif +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 /* bit-flags */ #define TARGET_SS_AUTODISARM (1U << 31) /* disable sas during sighandling */ diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h index d857c55e4c..275e9b7f9a 100644 --- a/linux-user/mips64/target_signal.h +++ b/linux-user/mips64/target_signal.h @@ -76,4 +76,6 @@ typedef struct target_sigaltstack { /* compare linux/arch/mips/kernel/signal.c:setup_frame() */ #define TARGET_ARCH_HAS_SETUP_FRAME #endif +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* MIPS64_TARGET_SIGNAL_H */ From f32d3b67938a42e320016fd15f5c34afa0fb105a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:43 -0400 Subject: [PATCH 0154/1334] linux-user/nios2: Document non-use of setup_sigtramp Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-17-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/nios2/target_signal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h index aebf749f12..fe266c4c51 100644 --- a/linux-user/nios2/target_signal.h +++ b/linux-user/nios2/target_signal.h @@ -19,4 +19,7 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" +/* Nios2 uses a fixed address on the kuser page for sigreturn. */ +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 + #endif /* NIOS2_TARGET_SIGNAL_H */ From 9ce3ad4452539723d9204dbfc137f57124eb8e36 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:44 -0400 Subject: [PATCH 0155/1334] linux-user/openrisc: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the rt signal trampoline. Reviewed-by: Stafford Horne Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-18-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/openrisc/signal.c | 22 ++++++++++++++-------- linux-user/openrisc/target_signal.h | 2 ++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index ca2532bf50..be8b68784a 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -38,7 +38,6 @@ typedef struct target_ucontext { typedef struct target_rt_sigframe { struct target_siginfo info; target_ucontext uc; - uint32_t retcode[4]; /* trampoline code */ } target_rt_sigframe; static void restore_sigcontext(CPUOpenRISCState *env, target_sigcontext *sc) @@ -116,14 +115,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } - /* This is l.ori r11,r0,__NR_sigreturn; l.sys 1; l.nop; l.nop */ - __put_user(0xa9600000 | TARGET_NR_rt_sigreturn, frame->retcode + 0); - __put_user(0x20000001, frame->retcode + 1); - __put_user(0x15000000, frame->retcode + 2); - __put_user(0x15000000, frame->retcode + 3); - /* Set up registers for signal handler */ - cpu_set_gpr(env, 9, frame_addr + offsetof(target_rt_sigframe, retcode)); + cpu_set_gpr(env, 9, default_rt_sigreturn); cpu_set_gpr(env, 3, sig); cpu_set_gpr(env, 4, frame_addr + offsetof(target_rt_sigframe, info)); cpu_set_gpr(env, 5, frame_addr + offsetof(target_rt_sigframe, uc)); @@ -169,3 +162,16 @@ long do_rt_sigreturn(CPUOpenRISCState *env) force_sig(TARGET_SIGSEGV); return 0; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0); + assert(tramp != NULL); + + /* This is l.ori r11,r0,__NR_sigreturn; l.sys 1 */ + __put_user(0xa9600000 | TARGET_NR_rt_sigreturn, tramp + 0); + __put_user(0x20000001, tramp + 1); + + default_rt_sigreturn = sigtramp_page; + unlock_user(tramp, sigtramp_page, 8); +} diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index 8283eaf544..077ec3d5e8 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -26,4 +26,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* OPENRISC_TARGET_SIGNAL_H */ From 5d2fc70f57916abc6afbe739636dffed6a5009c7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:45 -0400 Subject: [PATCH 0156/1334] linux-user/ppc: Simplify encode_trampoline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sigret parameter is never 0, and even if it was the encoding of the LI instruction would still work. Reported-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-19-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/ppc/signal.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index e4d0dfa3bf..77f37b9f01 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -309,10 +309,8 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) static void encode_trampoline(int sigret, uint32_t *tramp) { /* Set up the sigreturn trampoline: li r0,sigret; sc. */ - if (sigret) { - __put_user(0x38000000 | sigret, &tramp[0]); - __put_user(0x44000002, &tramp[1]); - } + __put_user(0x38000000 | sigret, &tramp[0]); + __put_user(0x44000002, &tramp[1]); } static void restore_user_regs(CPUPPCState *env, From c790e4ebfed8174f65d685f5a4f3262873c237a5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:46 -0400 Subject: [PATCH 0157/1334] linux-user/ppc: Implement setup_sigtramp Create and record the two signal trampolines. Cc: qemu-ppc@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-20-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/ppc/signal.c | 34 ++++++++++++++++++---------------- linux-user/ppc/target_signal.h | 2 ++ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 77f37b9f01..c37744c8fc 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -203,9 +203,6 @@ struct target_func_ptr { #endif -/* We use the mc_pad field for the signal return trampoline. */ -#define tramp mc_pad - /* See arch/powerpc/kernel/signal.c. */ static target_ulong get_sigframe(struct target_sigaction *ka, CPUPPCState *env, @@ -436,12 +433,7 @@ void setup_frame(int sig, struct target_sigaction *ka, /* Save user regs. */ save_user_regs(env, &frame->mctx); - /* Construct the trampoline code on the stack. */ - encode_trampoline(TARGET_NR_sigreturn, (uint32_t *)&frame->mctx.tramp); - - /* The kernel checks for the presence of a VDSO here. We don't - emulate a vdso, so use a sigreturn system call. */ - env->lr = (target_ulong) h2g(frame->mctx.tramp); + env->lr = default_sigreturn; /* Turn off all fp exceptions. */ env->fpscr = 0; @@ -477,7 +469,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUPPCState *env) { struct target_rt_sigframe *rt_sf; - uint32_t *trampptr = 0; struct target_mcontext *mctx = 0; target_ulong rt_sf_addr, newsp = 0; int i, err = 0; @@ -507,22 +498,17 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, #if defined(TARGET_PPC64) mctx = &rt_sf->uc.tuc_sigcontext.mcontext; - trampptr = &rt_sf->trampoline[0]; sc = &rt_sf->uc.tuc_sigcontext; __put_user(h2g(mctx), &sc->regs); __put_user(sig, &sc->signal); #else mctx = &rt_sf->uc.tuc_mcontext; - trampptr = (uint32_t *)&rt_sf->uc.tuc_mcontext.tramp; #endif save_user_regs(env, mctx); - encode_trampoline(TARGET_NR_rt_sigreturn, trampptr); - /* The kernel checks for the presence of a VDSO here. We don't - emulate a vdso, so use a sigreturn system call. */ - env->lr = (target_ulong) h2g(trampptr); + env->lr = default_rt_sigreturn; /* Turn off all fp exceptions. */ env->fpscr = 0; @@ -720,3 +706,19 @@ abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx, return 0; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0); + assert(tramp != NULL); + +#ifdef TARGET_ARCH_HAS_SETUP_FRAME + default_sigreturn = sigtramp_page; + encode_trampoline(TARGET_NR_sigreturn, tramp + 0); +#endif + + default_rt_sigreturn = sigtramp_page + 8; + encode_trampoline(TARGET_NR_rt_sigreturn, tramp + 2); + + unlock_user(tramp, sigtramp_page, 2 * 8); +} diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h index 72fcdd9bfa..82184ab8f2 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -24,4 +24,6 @@ typedef struct target_sigaltstack { #if !defined(TARGET_PPC64) #define TARGET_ARCH_HAS_SETUP_FRAME #endif +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* PPC_TARGET_SIGNAL_H */ From 3c62b5d2015b8292d4453f8174abf5fadbc6cc0f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:47 -0400 Subject: [PATCH 0158/1334] linux-user/riscv: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the rt signal trampoline. This fixes a bug wrt libgcc fallback unwinding. It expects the stack pointer to point to the siginfo_t, whereas we had inexplicably placed our private signal trampoline at the start of the signal frame instead of the end. Now moot because we have removed it from the stack frame entirely. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-21-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/riscv/signal.c | 22 +++++++++++++--------- linux-user/riscv/target_signal.h | 2 ++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index f7f33bc90a..a0f9542ce3 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -47,7 +47,6 @@ struct target_ucontext { }; struct target_rt_sigframe { - uint32_t tramp[2]; /* not in kernel, which uses VDSO instead */ struct target_siginfo info; struct target_ucontext uc; }; @@ -105,12 +104,6 @@ static void setup_ucontext(struct target_ucontext *uc, setup_sigcontext(&uc->uc_mcontext, env); } -static inline void install_sigtramp(uint32_t *tramp) -{ - __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */ - __put_user(0x00000073, tramp + 1); /* ecall */ -} - void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPURISCVState *env) @@ -127,14 +120,13 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, setup_ucontext(&frame->uc, env, set); tswap_siginfo(&frame->info, info); - install_sigtramp(frame->tramp); env->pc = ka->_sa_handler; env->gpr[xSP] = frame_addr; env->gpr[xA0] = sig; env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info); env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->gpr[xRA] = frame_addr + offsetof(struct target_rt_sigframe, tramp); + env->gpr[xRA] = default_rt_sigreturn; return; @@ -203,3 +195,15 @@ badframe: force_sig(TARGET_SIGSEGV); return 0; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0); + assert(tramp != NULL); + + __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */ + __put_user(0x00000073, tramp + 1); /* ecall */ + + default_rt_sigreturn = sigtramp_page; + unlock_user(tramp, sigtramp_page, 8); +} diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h index f113ba9a55..3e36fddc9d 100644 --- a/linux-user/riscv/target_signal.h +++ b/linux-user/riscv/target_signal.h @@ -15,4 +15,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* RISCV_TARGET_SIGNAL_H */ From 31330e6cecfdf2c4617ecb95c7e2804a8dae2488 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:48 -0400 Subject: [PATCH 0159/1334] linux-user/s390x: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the two signal trampolines. Use them when the guest does not use SA_RESTORER. Cc: qemu-s390x@nongnu.org Tested-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-22-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/s390x/signal.c | 24 ++++++++++++++++-------- linux-user/s390x/target_signal.h | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index 80f34086d7..676b948147 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -68,7 +68,6 @@ typedef struct { target_sigregs sregs; int signo; target_sigregs_ext sregs_ext; - uint16_t retcode; } sigframe; #define TARGET_UC_VXRS 2 @@ -85,7 +84,6 @@ struct target_ucontext { typedef struct { uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; - uint16_t retcode; struct target_siginfo info; struct target_ucontext uc; } rt_sigframe; @@ -209,9 +207,7 @@ void setup_frame(int sig, struct target_sigaction *ka, if (ka->sa_flags & TARGET_SA_RESTORER) { restorer = ka->sa_restorer; } else { - restorer = frame_addr + offsetof(sigframe, retcode); - __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, - &frame->retcode); + restorer = default_sigreturn; } /* Set up registers for signal handler */ @@ -262,9 +258,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, if (ka->sa_flags & TARGET_SA_RESTORER) { restorer = ka->sa_restorer; } else { - restorer = frame_addr + offsetof(typeof(*frame), retcode); - __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, - &frame->retcode); + restorer = default_rt_sigreturn; } /* Create siginfo on the signal stack. */ @@ -405,3 +399,17 @@ long do_rt_sigreturn(CPUS390XState *env) unlock_user_struct(frame, frame_addr, 0); return -TARGET_QEMU_ESIGRETURN; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 + 2, 0); + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, &tramp[0]); + + default_rt_sigreturn = sigtramp_page + 2; + __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, &tramp[1]); + + unlock_user(tramp, sigtramp_page, 2 + 2); +} diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h index bbfc464d44..64f5f42201 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -19,4 +19,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" #define TARGET_ARCH_HAS_SETUP_FRAME +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* S390X_TARGET_SIGNAL_H */ From b9188f9ccc2af655666cfaac12ff9fb4db3c6009 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:49 -0400 Subject: [PATCH 0160/1334] linux-user/sh4: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the two signal trampolines. Use them when the guest does not use SA_RESTORER. Cc: Yoshinori Sato Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-23-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/sh4/signal.c | 40 +++++++++++++++++++--------------- linux-user/sh4/target_signal.h | 2 ++ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index d70d744bef..faa869fb19 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -52,7 +52,6 @@ struct target_sigframe { struct target_sigcontext sc; target_ulong extramask[TARGET_NSIG_WORDS-1]; - uint16_t retcode[3]; }; @@ -68,7 +67,6 @@ struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; - uint16_t retcode[3]; }; @@ -190,15 +188,9 @@ void setup_frame(int sig, struct target_sigaction *ka, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { - regs->pr = (unsigned long) ka->sa_restorer; + regs->pr = ka->sa_restorer; } else { - /* Generate return code (system call to sigreturn) */ - abi_ulong retcode_addr = frame_addr + - offsetof(struct target_sigframe, retcode); - __put_user(MOVW(2), &frame->retcode[0]); - __put_user(TRAP_NOARG, &frame->retcode[1]); - __put_user((TARGET_NR_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) retcode_addr; + regs->pr = default_sigreturn; } /* Set up registers for signal handler */ @@ -248,15 +240,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { - regs->pr = (unsigned long) ka->sa_restorer; + regs->pr = ka->sa_restorer; } else { - /* Generate return code (system call to sigreturn) */ - abi_ulong retcode_addr = frame_addr + - offsetof(struct target_rt_sigframe, retcode); - __put_user(MOVW(2), &frame->retcode[0]); - __put_user(TRAP_NOARG, &frame->retcode[1]); - __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) retcode_addr; + regs->pr = default_rt_sigreturn; } /* Set up registers for signal handler */ @@ -334,3 +320,21 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 6, 0); + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + __put_user(MOVW(2), &tramp[0]); + __put_user(TRAP_NOARG, &tramp[1]); + __put_user(TARGET_NR_sigreturn, &tramp[2]); + + default_rt_sigreturn = sigtramp_page + 6; + __put_user(MOVW(2), &tramp[3]); + __put_user(TRAP_NOARG, &tramp[4]); + __put_user(TARGET_NR_rt_sigreturn, &tramp[5]); + + unlock_user(tramp, sigtramp_page, 2 * 6); +} diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index d7309b7136..04069cba66 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -22,4 +22,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" #define TARGET_ARCH_HAS_SETUP_FRAME +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif /* SH4_TARGET_SIGNAL_H */ From 3f7685eaf90b36d0dc39d4719840e736c019a05c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:50 -0400 Subject: [PATCH 0161/1334] linux-user/sparc: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the two signal trampolines. Use them when the guest does not use SA_RESTORER. Acked-by: Mark Cave-Ayland Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-24-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/sparc/signal.c | 40 +++++++++++++++++++++----------- linux-user/sparc/target_signal.h | 4 ++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 3bc023d281..23e1e761de 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -242,6 +242,12 @@ static void restore_fpu(struct target_siginfo_fpu *fpu, CPUSPARCState *env) } #ifdef TARGET_ARCH_HAS_SETUP_FRAME +static void install_sigtramp(uint32_t *tramp, int syscall) +{ + __put_user(0x82102000u + syscall, &tramp[0]); /* mov syscall, %g1 */ + __put_user(0x91d02010u, &tramp[1]); /* t 0x10 */ +} + void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUSPARCState *env) { @@ -291,13 +297,9 @@ void setup_frame(int sig, struct target_sigaction *ka, if (ka->ka_restorer) { env->regwptr[WREG_O7] = ka->ka_restorer; } else { - env->regwptr[WREG_O7] = sf_addr + - offsetof(struct target_signal_frame, insns) - 2 * 4; - - /* mov __NR_sigreturn, %g1 */ - __put_user(0x821020d8u, &sf->insns[0]); - /* t 0x10 */ - __put_user(0x91d02010u, &sf->insns[1]); + /* Not used, but retain for ABI compatibility. */ + install_sigtramp(sf->insns, TARGET_NR_sigreturn); + env->regwptr[WREG_O7] = default_sigreturn; } unlock_user(sf, sf_addr, sf_size); } @@ -358,13 +360,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, if (ka->ka_restorer) { env->regwptr[WREG_O7] = ka->ka_restorer; } else { - env->regwptr[WREG_O7] = - sf_addr + offsetof(struct target_rt_signal_frame, insns) - 2 * 4; - - /* mov __NR_rt_sigreturn, %g1 */ - __put_user(0x82102065u, &sf->insns[0]); - /* t 0x10 */ - __put_user(0x91d02010u, &sf->insns[1]); + /* Not used, but retain for ABI compatibility. */ + install_sigtramp(sf->insns, TARGET_NR_rt_sigreturn); + env->regwptr[WREG_O7] = default_rt_sigreturn; } #else env->regwptr[WREG_O7] = ka->ka_restorer; @@ -775,4 +773,18 @@ do_sigsegv: unlock_user_struct(ucp, ucp_addr, 1); force_sig(TARGET_SIGSEGV); } +#else +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0); + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + install_sigtramp(tramp, TARGET_NR_sigreturn); + + default_rt_sigreturn = sigtramp_page + 8; + install_sigtramp(tramp + 2, TARGET_NR_rt_sigreturn); + + unlock_user(tramp, sigtramp_page, 2 * 8); +} #endif diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index 34f9a12519..e661ddd6ab 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -69,6 +69,10 @@ typedef struct target_sigaltstack { #ifdef TARGET_ABI32 #define TARGET_ARCH_HAS_SETUP_FRAME +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 +#else +/* For sparc64, use of KA_RESTORER is mandatory. */ +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 #endif /* bit-flags */ From 55e83c200594b5c3ab412dec2646898571b08b40 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:51 -0400 Subject: [PATCH 0162/1334] linux-user/xtensa: Implement setup_sigtramp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and record the rt signal trampoline. Use it when the guest does not use SA_RESTORER. Reviewed-by: Max Filippov Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-25-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/xtensa/signal.c | 56 ++++++++++++++++++++----------- linux-user/xtensa/target_signal.h | 2 ++ 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index 7a3bfb92ca..81572a5fc7 100644 --- a/linux-user/xtensa/signal.c +++ b/linux-user/xtensa/signal.c @@ -128,6 +128,29 @@ static int setup_sigcontext(struct target_rt_sigframe *frame, return 1; } +static void install_sigtramp(uint8_t *tramp) +{ +#ifdef TARGET_WORDS_BIGENDIAN + /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ + __put_user(0x22, &tramp[0]); + __put_user(0x0a, &tramp[1]); + __put_user(TARGET_NR_rt_sigreturn, &tramp[2]); + /* Generate instruction: SYSCALL */ + __put_user(0x00, &tramp[3]); + __put_user(0x05, &tramp[4]); + __put_user(0x00, &tramp[5]); +#else + /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ + __put_user(0x22, &tramp[0]); + __put_user(0xa0, &tramp[1]); + __put_user(TARGET_NR_rt_sigreturn, &tramp[2]); + /* Generate instruction: SYSCALL */ + __put_user(0x00, &tramp[3]); + __put_user(0x50, &tramp[4]); + __put_user(0x00, &tramp[5]); +#endif +} + void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUXtensaState *env) @@ -164,26 +187,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, if (ka->sa_flags & TARGET_SA_RESTORER) { ra = ka->sa_restorer; } else { - ra = frame_addr + offsetof(struct target_rt_sigframe, retcode); -#ifdef TARGET_WORDS_BIGENDIAN - /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ - __put_user(0x22, &frame->retcode[0]); - __put_user(0x0a, &frame->retcode[1]); - __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); - /* Generate instruction: SYSCALL */ - __put_user(0x00, &frame->retcode[3]); - __put_user(0x05, &frame->retcode[4]); - __put_user(0x00, &frame->retcode[5]); -#else - /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ - __put_user(0x22, &frame->retcode[0]); - __put_user(0xa0, &frame->retcode[1]); - __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); - /* Generate instruction: SYSCALL */ - __put_user(0x00, &frame->retcode[3]); - __put_user(0x50, &frame->retcode[4]); - __put_user(0x00, &frame->retcode[5]); -#endif + /* Not used, but retain for ABI compatibility. */ + install_sigtramp(frame->retcode); + ra = default_rt_sigreturn; } memset(env->regs, 0, sizeof(env->regs)); env->pc = ka->_sa_handler; @@ -264,3 +270,13 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint8_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6, 0); + assert(tramp != NULL); + + default_rt_sigreturn = sigtramp_page; + install_sigtramp(tramp); + unlock_user(tramp, sigtramp_page, 6); +} diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h index c60bf656f6..1c7ee73154 100644 --- a/linux-user/xtensa/target_signal.h +++ b/linux-user/xtensa/target_signal.h @@ -20,4 +20,6 @@ typedef struct target_sigaltstack { #include "../generic/signal.h" +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + #endif From 525c4670c5612336cc50fa4190bdc72c1e3ee270 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:52 -0400 Subject: [PATCH 0163/1334] linux-user: Remove default for TARGET_ARCH_HAS_SIGTRAMP_PAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All targets now define TARGET_ARCH_HAS_SIGTRAMP_PAGE. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-26-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 459a26ef1d..2404d482ba 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -30,10 +30,6 @@ #undef ELF_ARCH #endif -#ifndef TARGET_ARCH_HAS_SIGTRAMP_PAGE -#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 -#endif - #define ELF_OSABI ELFOSABI_SYSV /* from personality.h */ From efee71c8ca181d4f5b2211736b38a74a2a223375 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:05:53 -0400 Subject: [PATCH 0164/1334] tests/tcg/multiarch: Re-enable signals test for most guests With signal trampolines safely off the stack for all guests besides hppa, we can re-enable this test. It does show up a problem with sh4 (unrelated?), so leave that test disabled for now. Signed-off-by: Richard Henderson Message-Id: <20210929130553.121567-27-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- tests/tcg/hppa/Makefile.target | 7 +++++++ tests/tcg/i386/Makefile.target | 3 --- tests/tcg/multiarch/Makefile.target | 8 -------- tests/tcg/sh4/Makefile.target | 7 +++++++ 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/tcg/hppa/Makefile.target b/tests/tcg/hppa/Makefile.target index 473864d1d4..d0d5e0e257 100644 --- a/tests/tcg/hppa/Makefile.target +++ b/tests/tcg/hppa/Makefile.target @@ -5,3 +5,10 @@ # On parisc Linux supports 4K/16K/64K (but currently only 4k works) EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-16384 run-test-mmap-65536 +# This triggers failures for hppa-linux about 1% of the time +# HPPA is the odd target that can't use the sigtramp page; +# it requires the full vdso with dwarf2 unwind info. +run-signals: signals + $(call skip-test, $<, "BROKEN awaiting vdso support") +run-plugin-signals-with-%: + $(call skip-test, $<, "BROKEN awaiting vdso support") diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index a053ca3f15..38c10379af 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -65,9 +65,6 @@ run-plugin-%-with-libinsn.so: -d plugin -D $*-with-libinsn.so.pout $*, \ "$* (inline) on $(TARGET_NAME)") -run-plugin-signals-with-libinsn.so: - $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups and vdso support") - # Update TESTS I386_TESTS:=$(filter-out $(SKIP_I386_TESTS), $(ALL_X86_TESTS)) TESTS=$(MULTIARCH_TESTS) $(I386_TESTS) diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 85a6fb7a2e..3f283eabe6 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -32,14 +32,6 @@ threadcount: LDFLAGS+=-lpthread signals: LDFLAGS+=-lrt -lpthread -# This triggers failures on s390x hosts about 4% of the time -# This triggers failures for hppa-linux about 1% of the time -run-signals: signals - $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups and vdso support") - -run-plugin-signals-with-%: - $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups and vdso support") - # We define the runner for test-mmap after the individual # architectures have defined their supported pages sizes. If no # additional page sizes are defined we only run the default test. diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 9d18d44612..47c39a44b6 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -5,3 +5,10 @@ # On sh Linux supports 4k, 8k, 16k and 64k pages (but only 4k currently works) EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 run-test-mmap-16384 run-test-mmap-65536 + +# This triggers failures for sh4-linux about 10% of the time. +# Random SIGSEGV at unpredictable guest address, cause unknown. +run-signals: signals + $(call skip-test, $<, "BROKEN") +run-plugin-signals-with-%: + $(call skip-test, $<, "BROKEN") From 7237c7ce772794d6be10b8b987fe4d02dfd76562 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:01 +0800 Subject: [PATCH 0165/1334] qapi/machine: Fix an incorrect comment of SMPConfiguration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The explanation of @cores should be "number of cores per die" but not "number of cores per thread". Let's fix it. Fixes: 1e63fe685804 ("machine: pass QAPI struct to mc->smp_parse") Signed-off-by: Yanan Wang Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-2-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- qapi/machine.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/machine.json b/qapi/machine.json index f1c4983b64..0e91a57a76 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1393,7 +1393,7 @@ # # @dies: number of dies per socket in the CPU topology # -# @cores: number of cores per thread in the CPU topology +# @cores: number of cores per die in the CPU topology # # @threads: number of threads per core in the CPU topology # From c2511b1632e109130df524121dfb7d2413216d3c Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:02 +0800 Subject: [PATCH 0166/1334] machine: Deprecate "parameter=0" SMP configurations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the SMP configuration, we should either provide a topology parameter with a reasonable value (greater than zero) or just omit it and QEMU will compute the missing value. The users shouldn't provide a configuration with any parameter of it specified as zero (e.g. -smp 8,sockets=0) which could possibly cause unexpected results in the -smp parsing. So we deprecate this kind of configurations since 6.2 by adding the explicit sanity check. Signed-off-by: Yanan Wang Reviewed-by: Cornelia Huck Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-3-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- docs/about/deprecated.rst | 15 +++++++++++++++ hw/core/machine.c | 14 ++++++++++++++ qemu-options.hx | 12 +++++++----- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 2f7db9a98d..0bed6ecb1d 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -160,6 +160,21 @@ Use ``-display sdl`` instead. Use ``-display curses`` instead. +``-smp`` ("parameter=0" SMP configurations) (since 6.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Specified CPU topology parameters must be greater than zero. + +In the SMP configuration, users should either provide a CPU topology +parameter with a reasonable value (greater than zero) or just omit it +and QEMU will compute the missing value. + +However, historically it was implicitly allowed for users to provide +a parameter with zero value, which is meaningless and could also possibly +cause unexpected results in the -smp parsing. So support for this kind of +configurations (e.g. -smp 8,sockets=0) is deprecated since 6.2 and will +be removed in the near future, users have to ensure that all the topology +members described with -smp are greater than zero. Plugin argument passing through ``arg=`` (since 6.1) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/hw/core/machine.c b/hw/core/machine.c index 067f42b528..4e409261c9 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -835,6 +835,20 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, return; } + /* + * Specified CPU topology parameters must be greater than zero, + * explicit configuration like "cpus=0" is not allowed. + */ + if ((config->has_cpus && config->cpus == 0) || + (config->has_sockets && config->sockets == 0) || + (config->has_dies && config->dies == 0) || + (config->has_cores && config->cores == 0) || + (config->has_threads && config->threads == 0) || + (config->has_maxcpus && config->maxcpus == 0)) { + warn_report("Deprecated CPU topology (considered invalid): " + "CPU topology parameters must be greater than zero"); + } + mc->smp_parse(ms, config, errp); if (*errp) { goto out_free; diff --git a/qemu-options.hx b/qemu-options.hx index ceca52818a..4a092a092a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -233,11 +233,13 @@ SRST of computing the CPU maximum count. Either the initial CPU count, or at least one of the topology parameters - must be specified. Values for any omitted parameters will be computed - from those which are given. Historically preference was given to the - coarsest topology parameters when computing missing values (ie sockets - preferred over cores, which were preferred over threads), however, this - behaviour is considered liable to change. + must be specified. The specified parameters must be greater than zero, + explicit configuration like "cpus=0" is not allowed. Values for any + omitted parameters will be computed from those which are given. + Historically preference was given to the coarsest topology parameters + when computing missing values (ie sockets preferred over cores, which + were preferred over threads), however, this behaviour is considered + liable to change. ERST DEF("numa", HAS_ARG, QEMU_OPTION_numa, From 5d8b5a505571b7927095015c805646f78fc56578 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:03 +0800 Subject: [PATCH 0167/1334] machine: Minor refactor/fix for the smp parsers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To pave the way for the functional improvement in later patches, make some refactor/cleanup for the smp parsers, including using local maxcpus instead of ms->smp.max_cpus in the calculation, defaulting dies to 0 initially like other members, cleanup the sanity check for dies. We actually also fix a hidden defect by avoiding directly using the provided *zero value* in the calculation, which could cause a segment fault (e.g. using dies=0 in the calculation). Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-4-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 18 ++++++++++-------- hw/i386/pc.c | 23 ++++++++++++++--------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 4e409261c9..8e719e2932 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -752,8 +752,9 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) unsigned sockets = config->has_sockets ? config->sockets : 0; unsigned cores = config->has_cores ? config->cores : 0; unsigned threads = config->has_threads ? config->threads : 0; + unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; - if (config->has_dies && config->dies != 0 && config->dies != 1) { + if (config->has_dies && config->dies > 1) { error_setg(errp, "dies not supported by this machine's CPU topology"); return; } @@ -766,8 +767,8 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) sockets = sockets > 0 ? sockets : 1; cpus = cores * threads * sockets; } else { - ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus; - sockets = ms->smp.max_cpus / (cores * threads); + maxcpus = maxcpus > 0 ? maxcpus : cpus; + sockets = maxcpus / (cores * threads); } } else if (cores == 0) { threads = threads > 0 ? threads : 1; @@ -784,26 +785,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) return; } - ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus; + maxcpus = maxcpus > 0 ? maxcpus : cpus; - if (ms->smp.max_cpus < cpus) { + if (maxcpus < cpus) { error_setg(errp, "maxcpus must be equal to or greater than smp"); return; } - if (sockets * cores * threads != ms->smp.max_cpus) { + if (sockets * cores * threads != maxcpus) { error_setg(errp, "Invalid CPU topology: " "sockets (%u) * cores (%u) * threads (%u) " "!= maxcpus (%u)", sockets, cores, threads, - ms->smp.max_cpus); + maxcpus); return; } ms->smp.cpus = cpus; + ms->smp.sockets = sockets; ms->smp.cores = cores; ms->smp.threads = threads; - ms->smp.sockets = sockets; + ms->smp.max_cpus = maxcpus; } static void machine_get_smp(Object *obj, Visitor *v, const char *name, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index df457eceba..92c78d9933 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -718,9 +718,13 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err { unsigned cpus = config->has_cpus ? config->cpus : 0; unsigned sockets = config->has_sockets ? config->sockets : 0; - unsigned dies = config->has_dies ? config->dies : 1; + unsigned dies = config->has_dies ? config->dies : 0; unsigned cores = config->has_cores ? config->cores : 0; unsigned threads = config->has_threads ? config->threads : 0; + unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; + + /* directly default dies to 1 if it's omitted */ + dies = dies > 0 ? dies : 1; /* compute missing values, prefer sockets over cores over threads */ if (cpus == 0 || sockets == 0) { @@ -730,8 +734,8 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err sockets = sockets > 0 ? sockets : 1; cpus = cores * threads * dies * sockets; } else { - ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus; - sockets = ms->smp.max_cpus / (cores * threads * dies); + maxcpus = maxcpus > 0 ? maxcpus : cpus; + sockets = maxcpus / (dies * cores * threads); } } else if (cores == 0) { threads = threads > 0 ? threads : 1; @@ -748,27 +752,28 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err return; } - ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus; + maxcpus = maxcpus > 0 ? maxcpus : cpus; - if (ms->smp.max_cpus < cpus) { + if (maxcpus < cpus) { error_setg(errp, "maxcpus must be equal to or greater than smp"); return; } - if (sockets * dies * cores * threads != ms->smp.max_cpus) { + if (sockets * dies * cores * threads != maxcpus) { error_setg(errp, "Invalid CPU topology deprecated: " "sockets (%u) * dies (%u) * cores (%u) * threads (%u) " "!= maxcpus (%u)", sockets, dies, cores, threads, - ms->smp.max_cpus); + maxcpus); return; } ms->smp.cpus = cpus; - ms->smp.cores = cores; - ms->smp.threads = threads; ms->smp.sockets = sockets; ms->smp.dies = dies; + ms->smp.cores = cores; + ms->smp.threads = threads; + ms->smp.max_cpus = maxcpus; } static From 9a52b508061163df4dae05e708cd2a9cd790ad04 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:04 +0800 Subject: [PATCH 0168/1334] machine: Uniformly use maxcpus to calculate the omitted parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are currently using maxcpus to calculate the omitted sockets but using cpus to calculate the omitted cores/threads. This makes cmdlines like: -smp cpus=8,maxcpus=16 -smp cpus=8,cores=4,maxcpus=16 -smp cpus=8,threads=2,maxcpus=16 work fine but the ones like: -smp cpus=8,sockets=2,maxcpus=16 -smp cpus=8,sockets=2,cores=4,maxcpus=16 -smp cpus=8,sockets=2,threads=2,maxcpus=16 break the sanity check. Since we require for a valid config that the product of "sockets * cores * threads" should equal to the maxcpus, we should uniformly use maxcpus to calculate their omitted values. Also the if-branch of "cpus == 0 || sockets == 0" was split into two branches of "cpus == 0" and "sockets == 0" so that we can clearly read that we are parsing the configuration with a preference on cpus over sockets over cores over threads. Note: change in this patch won't affect any existing working cmdlines but improves consistency and allows more incomplete configs to be valid. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Pankaj Gupta Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-5-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 30 +++++++++++++++--------------- hw/i386/pc.c | 30 +++++++++++++++--------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 8e719e2932..596e758133 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -760,24 +760,26 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) } /* compute missing values, prefer sockets over cores over threads */ - if (cpus == 0 || sockets == 0) { + maxcpus = maxcpus > 0 ? maxcpus : cpus; + + if (cpus == 0) { + sockets = sockets > 0 ? sockets : 1; cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; - if (cpus == 0) { - sockets = sockets > 0 ? sockets : 1; - cpus = cores * threads * sockets; - } else { - maxcpus = maxcpus > 0 ? maxcpus : cpus; - sockets = maxcpus / (cores * threads); - } + cpus = sockets * cores * threads; + maxcpus = maxcpus > 0 ? maxcpus : cpus; + } else if (sockets == 0) { + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (cores * threads); } else if (cores == 0) { threads = threads > 0 ? threads : 1; - cores = cpus / (sockets * threads); - cores = cores > 0 ? cores : 1; + cores = maxcpus / (sockets * threads); } else if (threads == 0) { - threads = cpus / (cores * sockets); - threads = threads > 0 ? threads : 1; - } else if (sockets * cores * threads < cpus) { + threads = maxcpus / (sockets * cores); + } + + if (sockets * cores * threads < cpus) { error_setg(errp, "cpu topology: " "sockets (%u) * cores (%u) * threads (%u) < " "smp_cpus (%u)", @@ -785,8 +787,6 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) return; } - maxcpus = maxcpus > 0 ? maxcpus : cpus; - if (maxcpus < cpus) { error_setg(errp, "maxcpus must be equal to or greater than smp"); return; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 92c78d9933..e37e84cc7b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -727,24 +727,26 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err dies = dies > 0 ? dies : 1; /* compute missing values, prefer sockets over cores over threads */ - if (cpus == 0 || sockets == 0) { + maxcpus = maxcpus > 0 ? maxcpus : cpus; + + if (cpus == 0) { + sockets = sockets > 0 ? sockets : 1; cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; - if (cpus == 0) { - sockets = sockets > 0 ? sockets : 1; - cpus = cores * threads * dies * sockets; - } else { - maxcpus = maxcpus > 0 ? maxcpus : cpus; - sockets = maxcpus / (dies * cores * threads); - } + cpus = sockets * dies * cores * threads; + maxcpus = maxcpus > 0 ? maxcpus : cpus; + } else if (sockets == 0) { + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (dies * cores * threads); } else if (cores == 0) { threads = threads > 0 ? threads : 1; - cores = cpus / (sockets * dies * threads); - cores = cores > 0 ? cores : 1; + cores = maxcpus / (sockets * dies * threads); } else if (threads == 0) { - threads = cpus / (cores * dies * sockets); - threads = threads > 0 ? threads : 1; - } else if (sockets * dies * cores * threads < cpus) { + threads = maxcpus / (sockets * dies * cores); + } + + if (sockets * dies * cores * threads < cpus) { error_setg(errp, "cpu topology: " "sockets (%u) * dies (%u) * cores (%u) * threads (%u) < " "smp_cpus (%u)", @@ -752,8 +754,6 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err return; } - maxcpus = maxcpus > 0 ? maxcpus : cpus; - if (maxcpus < cpus) { error_setg(errp, "maxcpus must be equal to or greater than smp"); return; From 7d8c5a39628820f6927b8b70c8f54872f5d1a196 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:05 +0800 Subject: [PATCH 0169/1334] machine: Set the value of cpus to match maxcpus if it's omitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we directly calculate the omitted cpus based on the given incomplete collection of parameters. This makes some cmdlines like: -smp maxcpus=16 -smp sockets=2,maxcpus=16 -smp sockets=2,dies=2,maxcpus=16 -smp sockets=2,cores=4,maxcpus=16 not work. We should probably set the value of cpus to match maxcpus if it's omitted, which will make above configs start to work. So the calculation logic of cpus/maxcpus after this patch will be: When both maxcpus and cpus are omitted, maxcpus will be calculated from the given parameters and cpus will be set equal to maxcpus. When only one of maxcpus and cpus is given then the omitted one will be set to its counterpart's value. Both maxcpus and cpus may be specified, but maxcpus must be equal to or greater than cpus. Note: change in this patch won't affect any existing working cmdlines but allows more incomplete configs to be valid. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-6-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 29 ++++++++++++++++------------- hw/i386/pc.c | 29 ++++++++++++++++------------- qemu-options.hx | 11 ++++++++--- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 596e758133..d8f458db60 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -760,25 +760,28 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) } /* compute missing values, prefer sockets over cores over threads */ - maxcpus = maxcpus > 0 ? maxcpus : cpus; - - if (cpus == 0) { + if (cpus == 0 && maxcpus == 0) { sockets = sockets > 0 ? sockets : 1; cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; - cpus = sockets * cores * threads; + } else { maxcpus = maxcpus > 0 ? maxcpus : cpus; - } else if (sockets == 0) { - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - sockets = maxcpus / (cores * threads); - } else if (cores == 0) { - threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * threads); - } else if (threads == 0) { - threads = maxcpus / (sockets * cores); + + if (sockets == 0) { + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (cores * threads); + } else if (cores == 0) { + threads = threads > 0 ? threads : 1; + cores = maxcpus / (sockets * threads); + } else if (threads == 0) { + threads = maxcpus / (sockets * cores); + } } + maxcpus = maxcpus > 0 ? maxcpus : sockets * cores * threads; + cpus = cpus > 0 ? cpus : maxcpus; + if (sockets * cores * threads < cpus) { error_setg(errp, "cpu topology: " "sockets (%u) * cores (%u) * threads (%u) < " diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e37e84cc7b..f24a1d72ad 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -727,25 +727,28 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err dies = dies > 0 ? dies : 1; /* compute missing values, prefer sockets over cores over threads */ - maxcpus = maxcpus > 0 ? maxcpus : cpus; - - if (cpus == 0) { + if (cpus == 0 && maxcpus == 0) { sockets = sockets > 0 ? sockets : 1; cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; - cpus = sockets * dies * cores * threads; + } else { maxcpus = maxcpus > 0 ? maxcpus : cpus; - } else if (sockets == 0) { - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * cores * threads); - } else if (cores == 0) { - threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * threads); - } else if (threads == 0) { - threads = maxcpus / (sockets * dies * cores); + + if (sockets == 0) { + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (dies * cores * threads); + } else if (cores == 0) { + threads = threads > 0 ? threads : 1; + cores = maxcpus / (sockets * dies * threads); + } else if (threads == 0) { + threads = maxcpus / (sockets * dies * cores); + } } + maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads; + cpus = cpus > 0 ? cpus : maxcpus; + if (sockets * dies * cores * threads < cpus) { error_setg(errp, "cpu topology: " "sockets (%u) * dies (%u) * cores (%u) * threads (%u) < " diff --git a/qemu-options.hx b/qemu-options.hx index 4a092a092a..2eac830fdb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -220,9 +220,14 @@ SRST Simulate a SMP system with '\ ``n``\ ' CPUs initially present on the machine type board. On boards supporting CPU hotplug, the optional '\ ``maxcpus``\ ' parameter can be set to enable further CPUs to be - added at runtime. If omitted the maximum number of CPUs will be - set to match the initial CPU count. Both parameters are subject to - an upper limit that is determined by the specific machine type chosen. + added at runtime. When both parameters are omitted, the maximum number + of CPUs will be calculated from the provided topology members and the + initial CPU count will match the maximum number. When only one of them + is given then the omitted one will be set to its counterpart's value. + Both parameters may be specified, but the maximum number of CPUs must + be equal to or greater than the initial CPU count. Both parameters are + subject to an upper limit that is determined by the specific machine + type chosen. To control reporting of CPU topology information, the number of sockets, dies per socket, cores per die, and threads per core can be specified. From 52082d3ba4e81bf9df23d3cd5ddfb2a620e9b267 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:06 +0800 Subject: [PATCH 0170/1334] machine: Improve the error reporting of smp parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have two requirements for a valid SMP configuration: the product of "sockets * cores * threads" must represent all the possible cpus, i.e., max_cpus, and then must include the initially present cpus, i.e., smp_cpus. So we only need to ensure 1) "sockets * cores * threads == maxcpus" at first and then ensure 2) "maxcpus >= cpus". With a reasonable order of the sanity check, we can simplify the error reporting code. When reporting an error message we also report the exact value of each topology member to make users easily see what's going on. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Pankaj Gupta Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210929025816.21076-7-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 22 +++++++++------------- hw/i386/pc.c | 24 ++++++++++-------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index d8f458db60..e38ab760e6 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -782,25 +782,21 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) maxcpus = maxcpus > 0 ? maxcpus : sockets * cores * threads; cpus = cpus > 0 ? cpus : maxcpus; - if (sockets * cores * threads < cpus) { - error_setg(errp, "cpu topology: " - "sockets (%u) * cores (%u) * threads (%u) < " - "smp_cpus (%u)", - sockets, cores, threads, cpus); + if (sockets * cores * threads != maxcpus) { + error_setg(errp, "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "sockets (%u) * cores (%u) * threads (%u) " + "!= maxcpus (%u)", + sockets, cores, threads, maxcpus); return; } if (maxcpus < cpus) { - error_setg(errp, "maxcpus must be equal to or greater than smp"); - return; - } - - if (sockets * cores * threads != maxcpus) { error_setg(errp, "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " "sockets (%u) * cores (%u) * threads (%u) " - "!= maxcpus (%u)", - sockets, cores, threads, - maxcpus); + "== maxcpus (%u) < smp_cpus (%u)", + sockets, cores, threads, maxcpus, cpus); return; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f24a1d72ad..9216ad163d 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -749,25 +749,21 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads; cpus = cpus > 0 ? cpus : maxcpus; - if (sockets * dies * cores * threads < cpus) { - error_setg(errp, "cpu topology: " - "sockets (%u) * dies (%u) * cores (%u) * threads (%u) < " - "smp_cpus (%u)", - sockets, dies, cores, threads, cpus); + if (sockets * dies * cores * threads != maxcpus) { + error_setg(errp, "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "sockets (%u) * dies (%u) * cores (%u) * threads (%u) " + "!= maxcpus (%u)", + sockets, dies, cores, threads, maxcpus); return; } if (maxcpus < cpus) { - error_setg(errp, "maxcpus must be equal to or greater than smp"); - return; - } - - if (sockets * dies * cores * threads != maxcpus) { - error_setg(errp, "Invalid CPU topology deprecated: " + error_setg(errp, "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " "sockets (%u) * dies (%u) * cores (%u) * threads (%u) " - "!= maxcpus (%u)", - sockets, dies, cores, threads, - maxcpus); + "== maxcpus (%u) < smp_cpus (%u)", + sockets, dies, cores, threads, maxcpus, cpus); return; } From afc8e9aaa7915ebfe1af8eba6994a38b3e556ba9 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:07 +0800 Subject: [PATCH 0171/1334] qtest/numa-test: Use detailed -smp CLIs in pc_dynamic_cpu_cfg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 80d7835749 (qemu-options: rewrite help for -smp options), the preference of sockets/cores in -smp parsing is considered liable to change, and actually we are going to change it in a coming commit. So it'll be more stable to use detailed -smp CLIs in testing if we have strong dependency on the parsing results. pc_dynamic_cpu_cfg currently assumes/needs that there will be 2 CPU sockets with "-smp 2". To avoid breaking the test because of parsing logic change, now explicitly use "-smp 2,sockets=2". Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210929025816.21076-8-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- tests/qtest/numa-test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index c677cd63c4..fd7a2e80a0 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -265,7 +265,8 @@ static void pc_dynamic_cpu_cfg(const void *data) QTestState *qs; g_autofree char *cli = NULL; - cli = make_cli(data, "-nodefaults --preconfig -machine smp.cpus=2"); + cli = make_cli(data, "-nodefaults --preconfig " + "-machine smp.cpus=2,smp.sockets=2"); qs = qtest_init(cli); /* create 2 numa nodes */ From bbb0c0ec6da8a739b02fbff941b44afc5513d6e4 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:08 +0800 Subject: [PATCH 0172/1334] qtest/numa-test: Use detailed -smp CLIs in test_def_cpu_split MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 80d7835749 (qemu-options: rewrite help for -smp options), the preference of sockets/cores in -smp parsing is considered liable to change, and actually we are going to change it in a coming commit. So it'll be more stable to use detailed -smp CLIs in the testcases that have strong dependency on the parsing results. Currently, test_def_cpu_split use "-smp 8" and will get 8 CPU sockets based on current parsing rule. But if we change to prefer cores over sockets we will get one CPU socket with 8 cores, and this testcase will not get expected numa set by default on x86_64 (Ok on aarch64). So now explicitly use "-smp 8,sockets=8" to avoid affect from parsing logic change. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210929025816.21076-9-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- tests/qtest/numa-test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index fd7a2e80a0..90bf68a5b3 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -42,7 +42,8 @@ static void test_def_cpu_split(const void *data) g_autofree char *s = NULL; g_autofree char *cli = NULL; - cli = make_cli(data, "-machine smp.cpus=8 -numa node,memdev=ram -numa node"); + cli = make_cli(data, "-machine smp.cpus=8,smp.sockets=8 " + "-numa node,memdev=ram -numa node"); qts = qtest_init(cli); s = qtest_hmp(qts, "info numa"); From 4a0af2930a4e4f64ce551152fdb4b9e7be106408 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:09 +0800 Subject: [PATCH 0173/1334] machine: Prefer cores over sockets in smp parsing since 6.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the real SMP hardware topology world, it's much more likely that we have high cores-per-socket counts and few sockets totally. While the current preference of sockets over cores in smp parsing results in a virtual cpu topology with low cores-per-sockets counts and a large number of sockets, which is just contrary to the real world. Given that it is better to make the virtual cpu topology be more reflective of the real world and also for the sake of compatibility, we start to prefer cores over sockets over threads in smp parsing since machine type 6.2 for different arches. In this patch, a boolean "smp_prefer_sockets" is added, and we only enable the old preference on older machines and enable the new one since type 6.2 for all arches by using the machine compat mechanism. Suggested-by: Daniel P. Berrange Signed-off-by: Yanan Wang Acked-by: David Gibson Acked-by: Cornelia Huck Reviewed-by: Andrew Jones Reviewed-by: Pankaj Gupta Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-10-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/arm/virt.c | 1 + hw/core/machine.c | 35 ++++++++++++++++++++++++++--------- hw/i386/pc.c | 35 ++++++++++++++++++++++++++--------- hw/i386/pc_piix.c | 1 + hw/i386/pc_q35.c | 1 + hw/ppc/spapr.c | 1 + hw/s390x/s390-virtio-ccw.c | 1 + include/hw/boards.h | 1 + qemu-options.hx | 3 ++- 9 files changed, 60 insertions(+), 19 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 1d59f0e59f..8c13deb5db 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2815,6 +2815,7 @@ static void virt_machine_6_1_options(MachineClass *mc) virt_machine_6_2_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); + mc->smp_prefer_sockets = true; /* qemu ITS was introduced with 6.2 */ vmc->no_tcg_its = true; diff --git a/hw/core/machine.c b/hw/core/machine.c index e38ab760e6..f2a34d98c0 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -748,6 +748,7 @@ void machine_set_cpu_numa_node(MachineState *machine, static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) { + MachineClass *mc = MACHINE_GET_CLASS(ms); unsigned cpus = config->has_cpus ? config->cpus : 0; unsigned sockets = config->has_sockets ? config->sockets : 0; unsigned cores = config->has_cores ? config->cores : 0; @@ -759,7 +760,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) return; } - /* compute missing values, prefer sockets over cores over threads */ + /* compute missing values based on the provided ones */ if (cpus == 0 && maxcpus == 0) { sockets = sockets > 0 ? sockets : 1; cores = cores > 0 ? cores : 1; @@ -767,14 +768,30 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) } else { maxcpus = maxcpus > 0 ? maxcpus : cpus; - if (sockets == 0) { - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - sockets = maxcpus / (cores * threads); - } else if (cores == 0) { - threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * threads); - } else if (threads == 0) { + if (mc->smp_prefer_sockets) { + /* prefer sockets over cores before 6.2 */ + if (sockets == 0) { + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (cores * threads); + } else if (cores == 0) { + threads = threads > 0 ? threads : 1; + cores = maxcpus / (sockets * threads); + } + } else { + /* prefer cores over sockets since 6.2 */ + if (cores == 0) { + sockets = sockets > 0 ? sockets : 1; + threads = threads > 0 ? threads : 1; + cores = maxcpus / (sockets * threads); + } else if (sockets == 0) { + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (cores * threads); + } + } + + /* try to calculate omitted threads at last */ + if (threads == 0) { threads = maxcpus / (sockets * cores); } } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 9216ad163d..6cc32f4048 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -716,6 +716,7 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) */ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) { + MachineClass *mc = MACHINE_GET_CLASS(ms); unsigned cpus = config->has_cpus ? config->cpus : 0; unsigned sockets = config->has_sockets ? config->sockets : 0; unsigned dies = config->has_dies ? config->dies : 0; @@ -726,7 +727,7 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err /* directly default dies to 1 if it's omitted */ dies = dies > 0 ? dies : 1; - /* compute missing values, prefer sockets over cores over threads */ + /* compute missing values based on the provided ones */ if (cpus == 0 && maxcpus == 0) { sockets = sockets > 0 ? sockets : 1; cores = cores > 0 ? cores : 1; @@ -734,14 +735,30 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err } else { maxcpus = maxcpus > 0 ? maxcpus : cpus; - if (sockets == 0) { - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * cores * threads); - } else if (cores == 0) { - threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * threads); - } else if (threads == 0) { + if (mc->smp_prefer_sockets) { + /* prefer sockets over cores before 6.2 */ + if (sockets == 0) { + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (dies * cores * threads); + } else if (cores == 0) { + threads = threads > 0 ? threads : 1; + cores = maxcpus / (sockets * dies * threads); + } + } else { + /* prefer cores over sockets since 6.2 */ + if (cores == 0) { + sockets = sockets > 0 ? sockets : 1; + threads = threads > 0 ? threads : 1; + cores = maxcpus / (sockets * dies * threads); + } else if (sockets == 0) { + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (dies * cores * threads); + } + } + + /* try to calculate omitted threads at last */ + if (threads == 0) { threads = maxcpus / (sockets * dies * cores); } } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 6cc834aff6..932e6e2cfe 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -432,6 +432,7 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m) m->is_default = false; compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); + m->smp_prefer_sockets = true; } DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 5481d5c965..57ab07e5b7 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -372,6 +372,7 @@ static void pc_q35_6_1_machine_options(MachineClass *m) m->alias = NULL; compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); + m->smp_prefer_sockets = true; } DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL, diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b7bee5f4ff..2986108b5a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4685,6 +4685,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc) spapr_machine_6_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); smc->pre_6_2_numa_affinity = true; + mc->smp_prefer_sockets = true; } DEFINE_SPAPR_MACHINE(6_1, "6.1", false); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 61aeccb163..5401c985cf 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -814,6 +814,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc) { ccw_machine_6_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); + mc->smp_prefer_sockets = true; } DEFINE_CCW_MACHINE(6_1, "6.1", false); diff --git a/include/hw/boards.h b/include/hw/boards.h index 463a5514f9..2ae039b74f 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -247,6 +247,7 @@ struct MachineClass { bool nvdimm_supported; bool numa_mem_supported; bool auto_enable_numa; + bool smp_prefer_sockets; const char *default_ram_id; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, diff --git a/qemu-options.hx b/qemu-options.hx index 2eac830fdb..8ef178180d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -244,7 +244,8 @@ SRST Historically preference was given to the coarsest topology parameters when computing missing values (ie sockets preferred over cores, which were preferred over threads), however, this behaviour is considered - liable to change. + liable to change. Prior to 6.2 the preference was sockets over cores + over threads. Since 6.2 the preference is cores over sockets over threads. ERST DEF("numa", HAS_ARG, QEMU_OPTION_numa, From 69fc28a78dbff2d3414bbafce38320b4433ed583 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:10 +0800 Subject: [PATCH 0174/1334] machine: Use ms instead of global current_machine in sanity-check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the sanity-check of smp_cpus and max_cpus against mc in function machine_set_smp(), we are now using ms->smp.max_cpus for the check but using current_machine->smp.max_cpus in the error message. Tweak this by uniformly using the local ms. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Pankaj Gupta Reviewed-by: Cornelia Huck Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210929025816.21076-11-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index f2a34d98c0..12d7416053 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -881,7 +881,7 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, } else if (ms->smp.max_cpus > mc->max_cpus) { error_setg(errp, "Invalid SMP CPUs %d. The max CPUs " "supported by machine '%s' is %d", - current_machine->smp.max_cpus, + ms->smp.max_cpus, mc->name, mc->max_cpus); } From 003f230e37d724ac52004d7f8270159da105780f Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:11 +0800 Subject: [PATCH 0175/1334] machine: Tweak the order of topology members in struct CpuTopology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all the possible topology parameters are integrated in struct CpuTopology, tweak the order of topology members to be "cpus/sockets/ dies/cores/threads/maxcpus" for readability and consistency. We also tweak the comment by adding explanation of dies parameter. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Pankaj Gupta Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210929025816.21076-12-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 10 +++++----- include/hw/boards.h | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 12d7416053..83cbdcce47 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -829,11 +829,11 @@ static void machine_get_smp(Object *obj, Visitor *v, const char *name, { MachineState *ms = MACHINE(obj); SMPConfiguration *config = &(SMPConfiguration){ - .has_cores = true, .cores = ms->smp.cores, + .has_cpus = true, .cpus = ms->smp.cpus, .has_sockets = true, .sockets = ms->smp.sockets, .has_dies = true, .dies = ms->smp.dies, + .has_cores = true, .cores = ms->smp.cores, .has_threads = true, .threads = ms->smp.threads, - .has_cpus = true, .cpus = ms->smp.cpus, .has_maxcpus = true, .maxcpus = ms->smp.max_cpus, }; if (!visit_type_SMPConfiguration(v, name, &config, &error_abort)) { @@ -1060,10 +1060,10 @@ static void machine_initfn(Object *obj) /* default to mc->default_cpus */ ms->smp.cpus = mc->default_cpus; ms->smp.max_cpus = mc->default_cpus; - ms->smp.cores = 1; - ms->smp.dies = 1; - ms->smp.threads = 1; ms->smp.sockets = 1; + ms->smp.dies = 1; + ms->smp.cores = 1; + ms->smp.threads = 1; } static void machine_finalize(Object *obj) diff --git a/include/hw/boards.h b/include/hw/boards.h index 2ae039b74f..2a1bba86c0 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -275,17 +275,18 @@ typedef struct DeviceMemoryState { /** * CpuTopology: * @cpus: the number of present logical processors on the machine - * @cores: the number of cores in one package - * @threads: the number of threads in one core * @sockets: the number of sockets on the machine + * @dies: the number of dies in one socket + * @cores: the number of cores in one die + * @threads: the number of threads in one core * @max_cpus: the maximum number of logical processors on the machine */ typedef struct CpuTopology { unsigned int cpus; + unsigned int sockets; unsigned int dies; unsigned int cores; unsigned int threads; - unsigned int sockets; unsigned int max_cpus; } CpuTopology; From e4a97a893bcd7511aba812969d1fa6fe42dc1931 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:12 +0800 Subject: [PATCH 0176/1334] machine: Make smp_parse generic enough for all arches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the only difference between smp_parse and pc_smp_parse is the support of dies parameter and the related error reporting. With some arch compat variables like "bool dies_supported", we can make smp_parse generic enough for all arches and the PC specific one can be removed. Making smp_parse() generic enough can reduce code duplication and ease the code maintenance, and also allows extending the topology with more arch specific members (e.g., clusters) in the future. Suggested-by: Andrew Jones Suggested-by: Daniel P. Berrangé Signed-off-by: Yanan Wang Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-13-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 91 +++++++++++++++++++++++++++++++++++---------- hw/i386/pc.c | 84 +---------------------------------------- include/hw/boards.h | 9 +++++ 3 files changed, 81 insertions(+), 103 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 83cbdcce47..12872d7715 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -746,20 +746,69 @@ void machine_set_cpu_numa_node(MachineState *machine, } } +/* + * Report information of a machine's supported CPU topology hierarchy. + * Topology members will be ordered from the largest to the smallest + * in the string. + */ +static char *cpu_hierarchy_to_string(MachineState *ms) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + GString *s = g_string_new(NULL); + + g_string_append_printf(s, "sockets (%u)", ms->smp.sockets); + + if (mc->smp_props.dies_supported) { + g_string_append_printf(s, " * dies (%u)", ms->smp.dies); + } + + g_string_append_printf(s, " * cores (%u)", ms->smp.cores); + g_string_append_printf(s, " * threads (%u)", ms->smp.threads); + + return g_string_free(s, false); +} + +/* + * smp_parse - Generic function used to parse the given SMP configuration + * + * Any missing parameter in "cpus/maxcpus/sockets/cores/threads" will be + * automatically computed based on the provided ones. + * + * In the calculation of omitted sockets/cores/threads: we prefer sockets + * over cores over threads before 6.2, while preferring cores over sockets + * over threads since 6.2. + * + * In the calculation of cpus/maxcpus: When both maxcpus and cpus are omitted, + * maxcpus will be computed from the given parameters and cpus will be set + * equal to maxcpus. When only one of maxcpus and cpus is given then the + * omitted one will be set to its given counterpart's value. Both maxcpus and + * cpus may be specified, but maxcpus must be equal to or greater than cpus. + * + * For compatibility, apart from the parameters that will be computed, newly + * introduced topology members which are likely to be target specific should + * be directly set as 1 if they are omitted (e.g. dies for PC since 4.1). + */ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) { MachineClass *mc = MACHINE_GET_CLASS(ms); unsigned cpus = config->has_cpus ? config->cpus : 0; unsigned sockets = config->has_sockets ? config->sockets : 0; + unsigned dies = config->has_dies ? config->dies : 0; unsigned cores = config->has_cores ? config->cores : 0; unsigned threads = config->has_threads ? config->threads : 0; unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; - if (config->has_dies && config->dies > 1) { + /* + * If not supported by the machine, a topology parameter must be + * omitted or specified equal to 1. + */ + if (!mc->smp_props.dies_supported && dies > 1) { error_setg(errp, "dies not supported by this machine's CPU topology"); return; } + dies = dies > 0 ? dies : 1; + /* compute missing values based on the provided ones */ if (cpus == 0 && maxcpus == 0) { sockets = sockets > 0 ? sockets : 1; @@ -773,55 +822,57 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) if (sockets == 0) { cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; - sockets = maxcpus / (cores * threads); + sockets = maxcpus / (dies * cores * threads); } else if (cores == 0) { threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * threads); + cores = maxcpus / (sockets * dies * threads); } } else { /* prefer cores over sockets since 6.2 */ if (cores == 0) { sockets = sockets > 0 ? sockets : 1; threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * threads); + cores = maxcpus / (sockets * dies * threads); } else if (sockets == 0) { threads = threads > 0 ? threads : 1; - sockets = maxcpus / (cores * threads); + sockets = maxcpus / (dies * cores * threads); } } /* try to calculate omitted threads at last */ if (threads == 0) { - threads = maxcpus / (sockets * cores); + threads = maxcpus / (sockets * dies * cores); } } - maxcpus = maxcpus > 0 ? maxcpus : sockets * cores * threads; + maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads; cpus = cpus > 0 ? cpus : maxcpus; - if (sockets * cores * threads != maxcpus) { + ms->smp.cpus = cpus; + ms->smp.sockets = sockets; + ms->smp.dies = dies; + ms->smp.cores = cores; + ms->smp.threads = threads; + ms->smp.max_cpus = maxcpus; + + /* sanity-check of the computed topology */ + if (sockets * dies * cores * threads != maxcpus) { + g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); error_setg(errp, "Invalid CPU topology: " "product of the hierarchy must match maxcpus: " - "sockets (%u) * cores (%u) * threads (%u) " - "!= maxcpus (%u)", - sockets, cores, threads, maxcpus); + "%s != maxcpus (%u)", + topo_msg, maxcpus); return; } if (maxcpus < cpus) { + g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); error_setg(errp, "Invalid CPU topology: " "maxcpus must be equal to or greater than smp: " - "sockets (%u) * cores (%u) * threads (%u) " - "== maxcpus (%u) < smp_cpus (%u)", - sockets, cores, threads, maxcpus, cpus); + "%s == maxcpus (%u) < smp_cpus (%u)", + topo_msg, maxcpus, cpus); return; } - - ms->smp.cpus = cpus; - ms->smp.sockets = sockets; - ms->smp.cores = cores; - ms->smp.threads = threads; - ms->smp.max_cpus = maxcpus; } static void machine_get_smp(Object *obj, Visitor *v, const char *name, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 6cc32f4048..28e1b83b9d 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -710,88 +710,6 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) } } -/* - * This function is very similar to smp_parse() - * in hw/core/machine.c but includes CPU die support. - */ -static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) -{ - MachineClass *mc = MACHINE_GET_CLASS(ms); - unsigned cpus = config->has_cpus ? config->cpus : 0; - unsigned sockets = config->has_sockets ? config->sockets : 0; - unsigned dies = config->has_dies ? config->dies : 0; - unsigned cores = config->has_cores ? config->cores : 0; - unsigned threads = config->has_threads ? config->threads : 0; - unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; - - /* directly default dies to 1 if it's omitted */ - dies = dies > 0 ? dies : 1; - - /* compute missing values based on the provided ones */ - if (cpus == 0 && maxcpus == 0) { - sockets = sockets > 0 ? sockets : 1; - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - } else { - maxcpus = maxcpus > 0 ? maxcpus : cpus; - - if (mc->smp_prefer_sockets) { - /* prefer sockets over cores before 6.2 */ - if (sockets == 0) { - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * cores * threads); - } else if (cores == 0) { - threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * threads); - } - } else { - /* prefer cores over sockets since 6.2 */ - if (cores == 0) { - sockets = sockets > 0 ? sockets : 1; - threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * threads); - } else if (sockets == 0) { - threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * cores * threads); - } - } - - /* try to calculate omitted threads at last */ - if (threads == 0) { - threads = maxcpus / (sockets * dies * cores); - } - } - - maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads; - cpus = cpus > 0 ? cpus : maxcpus; - - if (sockets * dies * cores * threads != maxcpus) { - error_setg(errp, "Invalid CPU topology: " - "product of the hierarchy must match maxcpus: " - "sockets (%u) * dies (%u) * cores (%u) * threads (%u) " - "!= maxcpus (%u)", - sockets, dies, cores, threads, maxcpus); - return; - } - - if (maxcpus < cpus) { - error_setg(errp, "Invalid CPU topology: " - "maxcpus must be equal to or greater than smp: " - "sockets (%u) * dies (%u) * cores (%u) * threads (%u) " - "== maxcpus (%u) < smp_cpus (%u)", - sockets, dies, cores, threads, maxcpus, cpus); - return; - } - - ms->smp.cpus = cpus; - ms->smp.sockets = sockets; - ms->smp.dies = dies; - ms->smp.cores = cores; - ms->smp.threads = threads; - ms->smp.max_cpus = maxcpus; -} - static void pc_machine_done(Notifier *notifier, void *data) { @@ -1755,7 +1673,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->auto_enable_numa_with_memdev = true; mc->has_hotpluggable_cpus = true; mc->default_boot_order = "cad"; - mc->smp_parse = pc_smp_parse; mc->block_default_type = IF_IDE; mc->max_cpus = 255; mc->reset = pc_machine_reset; @@ -1766,6 +1683,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) hc->unplug = pc_machine_device_unplug_cb; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; mc->nvdimm_supported = true; + mc->smp_props.dies_supported = true; mc->default_ram_id = "pc.ram"; object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", diff --git a/include/hw/boards.h b/include/hw/boards.h index 2a1bba86c0..72a23e4e0f 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -108,6 +108,14 @@ typedef struct { CPUArchId cpus[]; } CPUArchIdList; +/** + * SMPCompatProps: + * @dies_supported - whether dies are supported by the machine + */ +typedef struct { + bool dies_supported; +} SMPCompatProps; + /** * MachineClass: * @deprecation_reason: If set, the machine is marked as deprecated. The @@ -248,6 +256,7 @@ struct MachineClass { bool numa_mem_supported; bool auto_enable_numa; bool smp_prefer_sockets; + SMPCompatProps smp_props; const char *default_ram_id; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, From 7687b2b3edc3f29ad58e8d6593d5c10dde406c34 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:13 +0800 Subject: [PATCH 0177/1334] machine: Remove smp_parse callback from MachineClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have a generic smp parser for all arches, and there will not be any other arch specific ones, so let's remove the callback from MachineClass and call the parser directly. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-14-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 3 +-- include/hw/boards.h | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 12872d7715..8b0f1aed83 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -918,7 +918,7 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, "CPU topology parameters must be greater than zero"); } - mc->smp_parse(ms, config, errp); + smp_parse(ms, config, errp); if (*errp) { goto out_free; } @@ -947,7 +947,6 @@ static void machine_class_init(ObjectClass *oc, void *data) /* Default 128 MB as guest ram size */ mc->default_ram_size = 128 * MiB; mc->rom_file_has_mr = true; - mc->smp_parse = smp_parse; /* numa node memory size aligned on 8MB by default. * On Linux, each node's border has to be 8MB aligned diff --git a/include/hw/boards.h b/include/hw/boards.h index 72a23e4e0f..fa284e01e9 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -177,10 +177,6 @@ typedef struct { * kvm-type may be NULL if it is not needed. * @numa_mem_supported: * true if '--numa node.mem' option is supported and false otherwise - * @smp_parse: - * The function pointer to hook different machine specific functions for - * parsing "smp-opts" from QemuOpts to MachineState::CpuTopology and more - * machine specific topology fields, such as smp_dies for PCMachine. * @hotplug_allowed: * If the hook is provided, then it'll be called for each device * hotplug to check whether the device hotplug is allowed. Return @@ -217,7 +213,6 @@ struct MachineClass { void (*reset)(MachineState *state); void (*wakeup)(MachineState *state); int (*kvm_type)(MachineState *machine, const char *arg); - void (*smp_parse)(MachineState *ms, SMPConfiguration *config, Error **errp); BlockInterfaceType block_default_type; int units_per_default_bus; From 2b52619994ab48504c5fc0d32a1af24159405ce0 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:14 +0800 Subject: [PATCH 0178/1334] machine: Move smp_prefer_sockets to struct SMPCompatProps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have a common structure SMPCompatProps used to store information about SMP compatibility stuff, so we can also move smp_prefer_sockets there for cleaner code. No functional change intended. Signed-off-by: Yanan Wang Acked-by: David Gibson Reviewed-by: Andrew Jones Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-15-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/arm/virt.c | 2 +- hw/core/machine.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/ppc/spapr.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- include/hw/boards.h | 3 ++- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 8c13deb5db..7170aaacd5 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2815,7 +2815,7 @@ static void virt_machine_6_1_options(MachineClass *mc) virt_machine_6_2_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); - mc->smp_prefer_sockets = true; + mc->smp_props.prefer_sockets = true; /* qemu ITS was introduced with 6.2 */ vmc->no_tcg_its = true; diff --git a/hw/core/machine.c b/hw/core/machine.c index 8b0f1aed83..54f04a5ac6 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -817,7 +817,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) } else { maxcpus = maxcpus > 0 ? maxcpus : cpus; - if (mc->smp_prefer_sockets) { + if (mc->smp_props.prefer_sockets) { /* prefer sockets over cores before 6.2 */ if (sockets == 0) { cores = cores > 0 ? cores : 1; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 932e6e2cfe..6ad0d763c5 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -432,7 +432,7 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m) m->is_default = false; compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); - m->smp_prefer_sockets = true; + m->smp_props.prefer_sockets = true; } DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 57ab07e5b7..fcc6e4eb2b 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -372,7 +372,7 @@ static void pc_q35_6_1_machine_options(MachineClass *m) m->alias = NULL; compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); - m->smp_prefer_sockets = true; + m->smp_props.prefer_sockets = true; } DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL, diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 2986108b5a..163c90388a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4685,7 +4685,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc) spapr_machine_6_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); smc->pre_6_2_numa_affinity = true; - mc->smp_prefer_sockets = true; + mc->smp_props.prefer_sockets = true; } DEFINE_SPAPR_MACHINE(6_1, "6.1", false); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 5401c985cf..653587ea62 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -814,7 +814,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc) { ccw_machine_6_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); - mc->smp_prefer_sockets = true; + mc->smp_props.prefer_sockets = true; } DEFINE_CCW_MACHINE(6_1, "6.1", false); diff --git a/include/hw/boards.h b/include/hw/boards.h index fa284e01e9..5adbcbb99b 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -110,9 +110,11 @@ typedef struct { /** * SMPCompatProps: + * @prefer_sockets - whether sockets are preferred over cores in smp parsing * @dies_supported - whether dies are supported by the machine */ typedef struct { + bool prefer_sockets; bool dies_supported; } SMPCompatProps; @@ -250,7 +252,6 @@ struct MachineClass { bool nvdimm_supported; bool numa_mem_supported; bool auto_enable_numa; - bool smp_prefer_sockets; SMPCompatProps smp_props; const char *default_ram_id; From e7f944bb94a375e8ee7469ffa535ea6e11ce59e1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Oct 2021 19:04:03 +0200 Subject: [PATCH 0179/1334] machine: Use g_autoptr in machine_set_smp Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 54f04a5ac6..d49ebc24e2 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -897,7 +897,7 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, { MachineClass *mc = MACHINE_GET_CLASS(obj); MachineState *ms = MACHINE(obj); - SMPConfiguration *config; + g_autoptr(SMPConfiguration) config = NULL; ERRP_GUARD(); if (!visit_type_SMPConfiguration(v, name, &config, errp)) { @@ -920,7 +920,7 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, smp_parse(ms, config, errp); if (*errp) { - goto out_free; + return; } /* sanity-check smp_cpus and max_cpus against mc */ @@ -935,9 +935,6 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, ms->smp.max_cpus, mc->name, mc->max_cpus); } - -out_free: - qapi_free_SMPConfiguration(config); } static void machine_class_init(ObjectClass *oc, void *data) From 8bdfec393a2ac67df9cecc0983d130db4a6bba58 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 29 Sep 2021 10:58:15 +0800 Subject: [PATCH 0180/1334] machine: Put all sanity-check in the generic SMP parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put both sanity-check of the input SMP configuration and sanity-check of the output SMP configuration uniformly in the generic parser. Then machine_set_smp() will become cleaner, also all the invalid scenarios can be tested only by calling the parser. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Pankaj Gupta Reviewed-by: Daniel P. Berrangé Message-Id: <20210929025816.21076-16-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 62 +++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index d49ebc24e2..3920a2f2af 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -798,6 +798,20 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) unsigned threads = config->has_threads ? config->threads : 0; unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; + /* + * Specified CPU topology parameters must be greater than zero, + * explicit configuration like "cpus=0" is not allowed. + */ + if ((config->has_cpus && config->cpus == 0) || + (config->has_sockets && config->sockets == 0) || + (config->has_dies && config->dies == 0) || + (config->has_cores && config->cores == 0) || + (config->has_threads && config->threads == 0) || + (config->has_maxcpus && config->maxcpus == 0)) { + warn_report("Deprecated CPU topology (considered invalid): " + "CPU topology parameters must be greater than zero"); + } + /* * If not supported by the machine, a topology parameter must be * omitted or specified equal to 1. @@ -873,6 +887,22 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) topo_msg, maxcpus, cpus); return; } + + if (ms->smp.cpus < mc->min_cpus) { + error_setg(errp, "Invalid SMP CPUs %d. The min CPUs " + "supported by machine '%s' is %d", + ms->smp.cpus, + mc->name, mc->min_cpus); + return; + } + + if (ms->smp.max_cpus > mc->max_cpus) { + error_setg(errp, "Invalid SMP CPUs %d. The max CPUs " + "supported by machine '%s' is %d", + ms->smp.max_cpus, + mc->name, mc->max_cpus); + return; + } } static void machine_get_smp(Object *obj, Visitor *v, const char *name, @@ -895,46 +925,14 @@ static void machine_get_smp(Object *obj, Visitor *v, const char *name, static void machine_set_smp(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - MachineClass *mc = MACHINE_GET_CLASS(obj); MachineState *ms = MACHINE(obj); g_autoptr(SMPConfiguration) config = NULL; - ERRP_GUARD(); if (!visit_type_SMPConfiguration(v, name, &config, errp)) { return; } - /* - * Specified CPU topology parameters must be greater than zero, - * explicit configuration like "cpus=0" is not allowed. - */ - if ((config->has_cpus && config->cpus == 0) || - (config->has_sockets && config->sockets == 0) || - (config->has_dies && config->dies == 0) || - (config->has_cores && config->cores == 0) || - (config->has_threads && config->threads == 0) || - (config->has_maxcpus && config->maxcpus == 0)) { - warn_report("Deprecated CPU topology (considered invalid): " - "CPU topology parameters must be greater than zero"); - } - smp_parse(ms, config, errp); - if (*errp) { - return; - } - - /* sanity-check smp_cpus and max_cpus against mc */ - if (ms->smp.cpus < mc->min_cpus) { - error_setg(errp, "Invalid SMP CPUs %d. The min CPUs " - "supported by machine '%s' is %d", - ms->smp.cpus, - mc->name, mc->min_cpus); - } else if (ms->smp.max_cpus > mc->max_cpus) { - error_setg(errp, "Invalid SMP CPUs %d. The max CPUs " - "supported by machine '%s' is %d", - ms->smp.max_cpus, - mc->name, mc->max_cpus); - } } static void machine_class_init(ObjectClass *oc, void *data) From bcfdfae78f111fa3c0f81b2708098a545201bb68 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Sep 2021 16:27:08 +0200 Subject: [PATCH 0181/1334] docs: name included files ".rst.inc" Signed-off-by: Paolo Bonzini --- docs/devel/{ci-definitions.rst => ci-definitions.rst.inc} | 0 docs/devel/{ci-jobs.rst => ci-jobs.rst.inc} | 0 docs/devel/{ci-runners.rst => ci-runners.rst.inc} | 0 docs/devel/ci.rst | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename docs/devel/{ci-definitions.rst => ci-definitions.rst.inc} (100%) rename docs/devel/{ci-jobs.rst => ci-jobs.rst.inc} (100%) rename docs/devel/{ci-runners.rst => ci-runners.rst.inc} (100%) diff --git a/docs/devel/ci-definitions.rst b/docs/devel/ci-definitions.rst.inc similarity index 100% rename from docs/devel/ci-definitions.rst rename to docs/devel/ci-definitions.rst.inc diff --git a/docs/devel/ci-jobs.rst b/docs/devel/ci-jobs.rst.inc similarity index 100% rename from docs/devel/ci-jobs.rst rename to docs/devel/ci-jobs.rst.inc diff --git a/docs/devel/ci-runners.rst b/docs/devel/ci-runners.rst.inc similarity index 100% rename from docs/devel/ci-runners.rst rename to docs/devel/ci-runners.rst.inc diff --git a/docs/devel/ci.rst b/docs/devel/ci.rst index 8d95247188..d106610096 100644 --- a/docs/devel/ci.rst +++ b/docs/devel/ci.rst @@ -8,6 +8,6 @@ found at:: https://wiki.qemu.org/Testing/CI -.. include:: ci-definitions.rst -.. include:: ci-jobs.rst -.. include:: ci-runners.rst +.. include:: ci-definitions.rst.inc +.. include:: ci-jobs.rst.inc +.. include:: ci-runners.rst.inc From f9df7aac758fed5cc2fb9210ad0edba79434aeed Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Sep 2021 16:42:18 +0200 Subject: [PATCH 0182/1334] docs: move notes inside the body of the document Make all documents start with a heading. Signed-off-by: Paolo Bonzini --- docs/devel/multi-process.rst | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/devel/multi-process.rst b/docs/devel/multi-process.rst index 69699329d6..e5758a79ab 100644 --- a/docs/devel/multi-process.rst +++ b/docs/devel/multi-process.rst @@ -1,15 +1,17 @@ -This is the design document for multi-process QEMU. It does not -necessarily reflect the status of the current implementation, which -may lack features or be considerably different from what is described -in this document. This document is still useful as a description of -the goals and general direction of this feature. - -Please refer to the following wiki for latest details: -https://wiki.qemu.org/Features/MultiProcessQEMU - Multi-process QEMU =================== +.. note:: + + This is the design document for multi-process QEMU. It does not + necessarily reflect the status of the current implementation, which + may lack features or be considerably different from what is described + in this document. This document is still useful as a description of + the goals and general direction of this feature. + + Please refer to the following wiki for latest details: + https://wiki.qemu.org/Features/MultiProcessQEMU + QEMU is often used as the hypervisor for virtual machines running in the Oracle cloud. Since one of the advantages of cloud computing is the ability to run many VMs from different tenants in the same cloud From 988f7b8bfeffbf521814d1e48c321f7674277512 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 2 Sep 2021 11:35:25 +0200 Subject: [PATCH 0183/1334] i386: Support KVM_CAP_ENFORCE_PV_FEATURE_CPUID By default, KVM allows the guest to use all currently supported PV features even when they were not announced in guest visible CPUIDs. Introduce a new "kvm-pv-enforce-cpuid" flag to limit the supported feature set to the exposed features. The feature is supported by Linux >= 5.10 and is not enabled by default in QEMU. Signed-off-by: Vitaly Kuznetsov Message-Id: <20210902093530.345756-4-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 ++ target/i386/cpu.h | 3 +++ target/i386/kvm/kvm.c | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index cacec605bf..598019de12 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6860,6 +6860,8 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true), DEFINE_PROP_BOOL("kvm-no-smi-migration", X86CPU, kvm_no_smi_migration, false), + DEFINE_PROP_BOOL("kvm-pv-enforce-cpuid", X86CPU, kvm_pv_enforce_cpuid, + false), DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true), DEFINE_PROP_BOOL("x-migrate-smi-count", X86CPU, migrate_smi_count, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 29552dc2a7..c990150373 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1802,6 +1802,9 @@ struct X86CPU { /* Stop SMI delivery for migration compatibility with old machines */ bool kvm_no_smi_migration; + /* Forcefully disable KVM PV features not exposed in guest CPUIDs */ + bool kvm_pv_enforce_cpuid; + /* Number of physical address bits supported */ uint32_t phys_bits; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 7f1b060e6d..d6a70c27e5 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1629,6 +1629,16 @@ int kvm_arch_init_vcpu(CPUState *cs) cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); + if (cpu->kvm_pv_enforce_cpuid) { + r = kvm_vcpu_enable_cap(cs, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 0, 1); + if (r < 0) { + fprintf(stderr, + "failed to enable KVM_CAP_ENFORCE_PV_FEATURE_CPUID: %s", + strerror(-r)); + abort(); + } + } + for (i = 0; i <= limit; i++) { if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { fprintf(stderr, "unsupported level value: 0x%x\n", limit); From 8b8939e44fc8315885598a9e1ee8deeea3b68a96 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Sep 2021 16:25:50 +0200 Subject: [PATCH 0184/1334] docs: put "make" information together in build-system.rst Signed-off-by: Paolo Bonzini --- docs/devel/build-system.rst | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 3baec158f2..0f636d620e 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -380,6 +380,16 @@ phony target, while benchmarks are run with ``make bench``. Meson test suites such as ``unit`` can be ran with ``make check-unit`` too. It is also possible to run tests defined in meson.build with ``meson test``. +Useful make targets +------------------- + +``help`` + Print a help message for the most common build targets. + +``print-VAR`` + Print the value of the variable VAR. Useful for debugging the build + system. + Important files for the build system ==================================== @@ -473,14 +483,3 @@ Built by Makefile: meson.build. The rules are produced from Meson's JSON description of tests (obtained with "meson introspect --tests") through the script scripts/mtest2make.py. - - -Useful make targets -------------------- - -``help`` - Print a help message for the most common build targets. - -``print-VAR`` - Print the value of the variable VAR. Useful for debugging the build - system. From 70367f091777419f42e5f68f4206deb641335877 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 2 Sep 2021 11:35:26 +0200 Subject: [PATCH 0185/1334] i386: Support KVM_CAP_HYPERV_ENFORCE_CPUID By default, KVM allows the guest to use all currently supported Hyper-V enlightenments when Hyper-V CPUID interface was exposed, regardless of if some features were not announced in guest visible CPUIDs. hv-enforce-cpuid feature alters this behavior and only allows the guest to use exposed Hyper-V enlightenments. The feature is supported by Linux >= 5.14 and is not enabled by default in QEMU. Signed-off-by: Vitaly Kuznetsov Message-Id: <20210902093530.345756-5-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- docs/hyperv.txt | 17 ++++++++++++++--- target/i386/cpu.c | 1 + target/i386/cpu.h | 1 + target/i386/kvm/kvm.c | 9 +++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 000638a2fd..072709a68f 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -203,8 +203,11 @@ When the option is set to 'on' QEMU will always enable the feature, regardless of host setup. To keep guests secure, this can only be used in conjunction with exposing correct vCPU topology and vCPU pinning. -4. Development features -======================== +4. Supplementary features +========================= + +4.1. hv-passthrough +=================== In some cases (e.g. during development) it may make sense to use QEMU in 'pass-through' mode and give Windows guests all enlightenments currently supported by KVM. This pass-through mode is enabled by "hv-passthrough" CPU @@ -215,8 +218,16 @@ values from KVM to QEMU. "hv-passthrough" overrides all other "hv-*" settings on the command line. Also, enabling this flag effectively prevents migration as the list of enabled enlightenments may differ between target and destination hosts. +4.2. hv-enforce-cpuid +===================== +By default, KVM allows the guest to use all currently supported Hyper-V +enlightenments when Hyper-V CPUID interface was exposed, regardless of if +some features were not announced in guest visible CPUIDs. 'hv-enforce-cpuid' +feature alters this behavior and only allows the guest to use exposed Hyper-V +enlightenments. -4. Useful links + +5. Useful links ================ Hyper-V Top Level Functional specification and other information: https://github.com/MicrosoftDocs/Virtualization-Documentation diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 598019de12..2a19eba56d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6834,6 +6834,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_ON_OFF_AUTO("hv-no-nonarch-coresharing", X86CPU, hyperv_no_nonarch_cs, ON_OFF_AUTO_OFF), DEFINE_PROP_BOOL("hv-passthrough", X86CPU, hyperv_passthrough, false), + DEFINE_PROP_BOOL("hv-enforce-cpuid", X86CPU, hyperv_enforce_cpuid, false), DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c990150373..8a7209bbf2 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1719,6 +1719,7 @@ struct X86CPU { uint32_t hyperv_version_id[4]; uint32_t hyperv_limits[3]; uint32_t hyperv_nested[4]; + bool hyperv_enforce_cpuid; bool check_cpuid; bool enforce_cpuid; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index d6a70c27e5..fbe6b7ac72 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1531,6 +1531,15 @@ static int hyperv_init_vcpu(X86CPU *cpu) cpu->hyperv_nested[0] = evmcs_version; } + if (cpu->hyperv_enforce_cpuid) { + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_ENFORCE_CPUID, 0, 1); + if (ret < 0) { + error_report("failed to enable KVM_CAP_HYPERV_ENFORCE_CPUID: %s", + strerror(-ret)); + return ret; + } + } + return 0; } From 768f14f94ec50fe57a3964fff75d8b3456b588b5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Sep 2021 16:53:01 +0200 Subject: [PATCH 0186/1334] docs: reorganize qgraph.rst Clean up the heading levels to use === --- ~~~, and move the command line building near to the other execution steps. Signed-off-by: Paolo Bonzini --- docs/devel/qgraph.rst | 132 +++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/docs/devel/qgraph.rst b/docs/devel/qgraph.rst index c2882c3a33..db44d71002 100644 --- a/docs/devel/qgraph.rst +++ b/docs/devel/qgraph.rst @@ -1,8 +1,7 @@ .. _qgraph: -======================================== Qtest Driver Framework -======================================== +====================== In order to test a specific driver, plain libqos tests need to take care of booting QEMU with the right machine and devices. @@ -31,13 +30,15 @@ so the sdhci-test should only care of linking its qgraph node with that interface. In this way, if the command line of a sdhci driver is changed, only the respective qgraph driver node has to be adjusted. +QGraph concepts +--------------- + The graph is composed by nodes that represent machines, drivers, tests and edges that define the relationships between them (``CONSUMES``, ``PRODUCES``, and ``CONTAINS``). - Nodes -^^^^^^ +~~~~~ A node can be of four types: @@ -64,7 +65,7 @@ Notes for the nodes: drivers name, otherwise they won't be discovered Edges -^^^^^^ +~~~~~ An edge relation between two nodes (drivers or machines) ``X`` and ``Y`` can be: @@ -73,7 +74,7 @@ An edge relation between two nodes (drivers or machines) ``X`` and ``Y`` can be: - ``X CONTAINS Y``: ``Y`` is part of ``X`` component Execution steps -^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~ The basic framework steps are the following: @@ -92,8 +93,64 @@ The basic framework steps are the following: Depending on the QEMU binary used, only some drivers/machines will be available and only test that are reached by them will be executed. +Command line +~~~~~~~~~~~~ + +Command line is built by using node names and optional arguments +passed by the user when building the edges. + +There are three types of command line arguments: + +- ``in node`` : created from the node name. For example, machines will + have ``-M `` to its command line, while devices + ``-device ``. It is automatically done by the framework. +- ``after node`` : added as additional argument to the node name. + This argument is added optionally when creating edges, + by setting the parameter ``after_cmd_line`` and + ``extra_edge_opts`` in ``QOSGraphEdgeOptions``. + The framework automatically adds + a comma before ``extra_edge_opts``, + because it is going to add attributes + after the destination node pointed by + the edge containing these options, and automatically + adds a space before ``after_cmd_line``, because it + adds an additional device, not an attribute. +- ``before node`` : added as additional argument to the node name. + This argument is added optionally when creating edges, + by setting the parameter ``before_cmd_line`` in + ``QOSGraphEdgeOptions``. This attribute + is going to add attributes before the destination node + pointed by the edge containing these options. It is + helpful to commands that are not node-representable, + such as ``-fdsev`` or ``-netdev``. + +While adding command line in edges is always used, not all nodes names are +used in every path walk: this is because the contained or produced ones +are already added by QEMU, so only nodes that "consumes" will be used to +build the command line. Also, nodes that will have ``{ "abstract" : true }`` +as QMP attribute will loose their command line, since they are not proper +devices to be added in QEMU. + +Example:: + + QOSGraphEdgeOptions opts = { + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," + "file.read-zeroes=on,format=raw", + .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0", + + opts.extra_device_opts = "id=vs0"; + }; + + qos_node_create_driver("virtio-scsi-device", + virtio_scsi_device_create); + qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts); + +Will produce the following command line: +``-drive id=drv0,if=none,file=null-co://, -device virtio-scsi-device,id=vs0 -device scsi-hd,bus=vs0.0,drive=drv0`` + Troubleshooting unavailable tests -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + If there is no path from an available machine to a test then that test will be unavailable and won't execute. This can happen if a test or driver did not set up its qgraph node correctly. It can also happen if the necessary machine type @@ -151,7 +208,7 @@ Typically this is because the QEMU binary lacks support for the necessary machine type or device. Creating a new driver and its interface -""""""""""""""""""""""""""""""""""""""""" +--------------------------------------- Here we continue the ``sdhci`` use case, with the following scenario: @@ -489,7 +546,7 @@ or inverting the consumes edge in consumed_by:: arm/raspi2b --contains--> generic-sdhci Adding a new test -""""""""""""""""" +----------------- Given the above setup, adding a new test is very simple. ``sdhci-test``, taken from ``tests/qtest/sdhci-test.c``:: @@ -565,62 +622,7 @@ and for the binary ``QTEST_QEMU_BINARY=./qemu-system-arm``: Additional examples are also in ``test-qgraph.c`` -Command line: -"""""""""""""" - -Command line is built by using node names and optional arguments -passed by the user when building the edges. - -There are three types of command line arguments: - -- ``in node`` : created from the node name. For example, machines will - have ``-M `` to its command line, while devices - ``-device ``. It is automatically done by the framework. -- ``after node`` : added as additional argument to the node name. - This argument is added optionally when creating edges, - by setting the parameter ``after_cmd_line`` and - ``extra_edge_opts`` in ``QOSGraphEdgeOptions``. - The framework automatically adds - a comma before ``extra_edge_opts``, - because it is going to add attributes - after the destination node pointed by - the edge containing these options, and automatically - adds a space before ``after_cmd_line``, because it - adds an additional device, not an attribute. -- ``before node`` : added as additional argument to the node name. - This argument is added optionally when creating edges, - by setting the parameter ``before_cmd_line`` in - ``QOSGraphEdgeOptions``. This attribute - is going to add attributes before the destination node - pointed by the edge containing these options. It is - helpful to commands that are not node-representable, - such as ``-fdsev`` or ``-netdev``. - -While adding command line in edges is always used, not all nodes names are -used in every path walk: this is because the contained or produced ones -are already added by QEMU, so only nodes that "consumes" will be used to -build the command line. Also, nodes that will have ``{ "abstract" : true }`` -as QMP attribute will loose their command line, since they are not proper -devices to be added in QEMU. - -Example:: - - QOSGraphEdgeOptions opts = { - .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," - "file.read-zeroes=on,format=raw", - .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0", - - opts.extra_device_opts = "id=vs0"; - }; - - qos_node_create_driver("virtio-scsi-device", - virtio_scsi_device_create); - qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts); - -Will produce the following command line: -``-drive id=drv0,if=none,file=null-co://, -device virtio-scsi-device,id=vs0 -device scsi-hd,bus=vs0.0,drive=drv0`` - Qgraph API reference -^^^^^^^^^^^^^^^^^^^^ +-------------------- .. kernel-doc:: tests/qtest/libqos/qgraph.h From 050716292a63f4969b32cac32b85774521738ef5 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 2 Sep 2021 11:35:27 +0200 Subject: [PATCH 0187/1334] i386: Move HV_APIC_ACCESS_RECOMMENDED bit setting to hyperv_fill_cpuids() In preparation to enabling Hyper-V + APICv/AVIC move HV_APIC_ACCESS_RECOMMENDED setting out of kvm_hyperv_properties[]: the 'real' feature bit for the vAPIC features is HV_APIC_ACCESS_AVAILABLE, HV_APIC_ACCESS_RECOMMENDED is a recommendation to use the feature which we may not always want to give. Signed-off-by: Vitaly Kuznetsov Message-Id: <20210902093530.345756-6-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index fbe6b7ac72..a9a8f77df3 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -821,9 +821,7 @@ static struct { .desc = "virtual APIC (hv-vapic)", .flags = { {.func = HV_CPUID_FEATURES, .reg = R_EAX, - .bits = HV_APIC_ACCESS_AVAILABLE}, - {.func = HV_CPUID_ENLIGHTMENT_INFO, .reg = R_EAX, - .bits = HV_APIC_ACCESS_RECOMMENDED} + .bits = HV_APIC_ACCESS_AVAILABLE} } }, [HYPERV_FEAT_TIME] = { @@ -1366,6 +1364,7 @@ static int hyperv_fill_cpuids(CPUState *cs, c->ebx |= HV_POST_MESSAGES | HV_SIGNAL_EVENTS; } + /* Not exposed by KVM but needed to make CPU hotplug in Windows work */ c->edx |= HV_CPU_DYNAMIC_PARTITIONING_AVAILABLE; @@ -1374,6 +1373,10 @@ static int hyperv_fill_cpuids(CPUState *cs, c->eax = hv_build_cpuid_leaf(cs, HV_CPUID_ENLIGHTMENT_INFO, R_EAX); c->ebx = cpu->hyperv_spinlock_attempts; + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VAPIC)) { + c->eax |= HV_APIC_ACCESS_RECOMMENDED; + } + if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_ON) { c->eax |= HV_NO_NONARCH_CORESHARING; } else if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_AUTO) { From e9adb4ace229dfa742176e9ddb629dbb6a6081bc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Sep 2021 17:06:07 +0200 Subject: [PATCH 0188/1334] docs: reorganize tcg-plugins.rst Clean up the heading levels to use === --- ~~~, and create a new "writing plugins" section. Signed-off-by: Paolo Bonzini --- docs/devel/tcg-plugins.rst | 117 ++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index dac5101a3c..842ae01a4c 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -3,7 +3,6 @@ Copyright (c) 2019, Linaro Limited Written by Emilio Cota and Alex Bennée -================ QEMU TCG Plugins ================ @@ -16,60 +15,8 @@ only monitor it passively. However they can do this down to an individual instruction granularity including potentially subscribing to all load and store operations. -API Stability -============= - -This is a new feature for QEMU and it does allow people to develop -out-of-tree plugins that can be dynamically linked into a running QEMU -process. However the project reserves the right to change or break the -API should it need to do so. The best way to avoid this is to submit -your plugin upstream so they can be updated if/when the API changes. - -API versioning --------------- - -All plugins need to declare a symbol which exports the plugin API -version they were built against. This can be done simply by:: - - QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; - -The core code will refuse to load a plugin that doesn't export a -``qemu_plugin_version`` symbol or if plugin version is outside of QEMU's -supported range of API versions. - -Additionally the ``qemu_info_t`` structure which is passed to the -``qemu_plugin_install`` method of a plugin will detail the minimum and -current API versions supported by QEMU. The API version will be -incremented if new APIs are added. The minimum API version will be -incremented if existing APIs are changed or removed. - -Exposure of QEMU internals --------------------------- - -The plugin architecture actively avoids leaking implementation details -about how QEMU's translation works to the plugins. While there are -conceptions such as translation time and translation blocks the -details are opaque to plugins. The plugin is able to query select -details of instructions and system configuration only through the -exported *qemu_plugin* functions. - -Query Handle Lifetime ---------------------- - -Each callback provides an opaque anonymous information handle which -can usually be further queried to find out information about a -translation, instruction or operation. The handles themselves are only -valid during the lifetime of the callback so it is important that any -information that is needed is extracted during the callback and saved -by the plugin. - -API -=== - -.. kernel-doc:: include/qemu/qemu-plugin.h - Usage -===== +----- Any QEMU binary with TCG support has plugins enabled by default. Earlier releases needed to be explicitly enabled with:: @@ -87,8 +34,45 @@ Arguments are plugin specific and can be used to modify their behaviour. In this case the howvec plugin is being asked to use inline ops to count and break down the hint instructions by type. -Plugin Life cycle -================= +Writing plugins +--------------- + +API versioning +~~~~~~~~~~~~~~ + +This is a new feature for QEMU and it does allow people to develop +out-of-tree plugins that can be dynamically linked into a running QEMU +process. However the project reserves the right to change or break the +API should it need to do so. The best way to avoid this is to submit +your plugin upstream so they can be updated if/when the API changes. + +All plugins need to declare a symbol which exports the plugin API +version they were built against. This can be done simply by:: + + QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +The core code will refuse to load a plugin that doesn't export a +``qemu_plugin_version`` symbol or if plugin version is outside of QEMU's +supported range of API versions. + +Additionally the ``qemu_info_t`` structure which is passed to the +``qemu_plugin_install`` method of a plugin will detail the minimum and +current API versions supported by QEMU. The API version will be +incremented if new APIs are added. The minimum API version will be +incremented if existing APIs are changed or removed. + +Lifetime of the query handle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each callback provides an opaque anonymous information handle which +can usually be further queried to find out information about a +translation, instruction or operation. The handles themselves are only +valid during the lifetime of the callback so it is important that any +information that is needed is extracted during the callback and saved +by the plugin. + +Plugin life cycle +~~~~~~~~~~~~~~~~~ First the plugin is loaded and the public qemu_plugin_install function is called. The plugin will then register callbacks for various plugin @@ -111,11 +95,26 @@ callback which can then ensure atomicity itself. Finally when QEMU exits all the registered *atexit* callbacks are invoked. +Exposure of QEMU internals +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The plugin architecture actively avoids leaking implementation details +about how QEMU's translation works to the plugins. While there are +conceptions such as translation time and translation blocks the +details are opaque to plugins. The plugin is able to query select +details of instructions and system configuration only through the +exported *qemu_plugin* functions. + +API +~~~ + +.. kernel-doc:: include/qemu/qemu-plugin.h + Internals -========= +--------- Locking -------- +~~~~~~~ We have to ensure we cannot deadlock, particularly under MTTCG. For this we acquire a lock when called from plugin code. We also keep the @@ -142,7 +141,7 @@ requested. The plugin isn't completely uninstalled until the safe work has executed while all vCPUs are quiescent. Example Plugins -=============== +--------------- There are a number of plugins included with QEMU and you are encouraged to contribute your own plugins plugins upstream. There is a From e1f9a8e8c90ae54387922e33e5ac4fd759747d01 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 2 Sep 2021 11:35:28 +0200 Subject: [PATCH 0189/1334] i386: Implement pseudo 'hv-avic' ('hv-apicv') enlightenment The enlightenment allows to use Hyper-V SynIC with hardware APICv/AVIC enabled. Normally, Hyper-V SynIC disables these hardware features and suggests the guest to use paravirtualized AutoEOI feature. Linux-4.15 gains support for conditional APICv/AVIC disablement, the feature stays on until the guest tries to use AutoEOI feature with SynIC. With 'HV_DEPRECATING_AEOI_RECOMMENDED' bit exposed, modern enough Windows/ Hyper-V versions should follow the recommendation and not use the (unwanted) feature. Signed-off-by: Vitaly Kuznetsov Message-Id: <20210902093530.345756-7-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- docs/hyperv.txt | 10 +++++++++- target/i386/cpu.c | 4 ++++ target/i386/cpu.h | 1 + target/i386/kvm/hyperv-proto.h | 1 + target/i386/kvm/kvm.c | 10 +++++++++- 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 072709a68f..cd1ea3bbe9 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -189,7 +189,15 @@ enabled. Requires: hv-vpindex, hv-synic, hv-time, hv-stimer -3.17. hv-no-nonarch-coresharing=on/off/auto +3.18. hv-avic (hv-apicv) +======================= +The enlightenment allows to use Hyper-V SynIC with hardware APICv/AVIC enabled. +Normally, Hyper-V SynIC disables these hardware feature and suggests the guest +to use paravirtualized AutoEOI feature. +Note: enabling this feature on old hardware (without APICv/AVIC support) may +have negative effect on guest's performace. + +3.19. hv-no-nonarch-coresharing=on/off/auto =========================================== This enlightenment tells guest OS that virtual processors will never share a physical core unless they are reported as sibling SMT threads. This information diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 2a19eba56d..8154343cc4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6644,6 +6644,8 @@ static void x86_cpu_initfn(Object *obj) object_property_add_alias(obj, "sse4_1", obj, "sse4.1"); object_property_add_alias(obj, "sse4_2", obj, "sse4.2"); + object_property_add_alias(obj, "hv-apicv", obj, "hv-avic"); + if (xcc->model) { x86_cpu_load_model(cpu, xcc->model); } @@ -6831,6 +6833,8 @@ static Property x86_cpu_properties[] = { HYPERV_FEAT_IPI, 0), DEFINE_PROP_BIT64("hv-stimer-direct", X86CPU, hyperv_features, HYPERV_FEAT_STIMER_DIRECT, 0), + DEFINE_PROP_BIT64("hv-avic", X86CPU, hyperv_features, + HYPERV_FEAT_AVIC, 0), DEFINE_PROP_ON_OFF_AUTO("hv-no-nonarch-coresharing", X86CPU, hyperv_no_nonarch_cs, ON_OFF_AUTO_OFF), DEFINE_PROP_BOOL("hv-passthrough", X86CPU, hyperv_passthrough, false), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8a7209bbf2..65f0ee2caf 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1056,6 +1056,7 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define HYPERV_FEAT_EVMCS 12 #define HYPERV_FEAT_IPI 13 #define HYPERV_FEAT_STIMER_DIRECT 14 +#define HYPERV_FEAT_AVIC 15 #ifndef HYPERV_SPINLOCK_NEVER_NOTIFY #define HYPERV_SPINLOCK_NEVER_NOTIFY 0xFFFFFFFF diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 5fbb385cc1..89f81afda7 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -66,6 +66,7 @@ #define HV_APIC_ACCESS_RECOMMENDED (1u << 3) #define HV_SYSTEM_RESET_RECOMMENDED (1u << 4) #define HV_RELAXED_TIMING_RECOMMENDED (1u << 5) +#define HV_DEPRECATING_AEOI_RECOMMENDED (1u << 9) #define HV_CLUSTER_IPI_RECOMMENDED (1u << 10) #define HV_EX_PROCESSOR_MASKS_RECOMMENDED (1u << 11) #define HV_ENLIGHTENED_VMCS_RECOMMENDED (1u << 14) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index a9a8f77df3..68faf72e34 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -924,6 +924,13 @@ static struct { }, .dependencies = BIT(HYPERV_FEAT_STIMER) }, + [HYPERV_FEAT_AVIC] = { + .desc = "AVIC/APICv support (hv-avic/hv-apicv)", + .flags = { + {.func = HV_CPUID_ENLIGHTMENT_INFO, .reg = R_EAX, + .bits = HV_DEPRECATING_AEOI_RECOMMENDED} + } + }, }; static struct kvm_cpuid2 *try_get_hv_cpuid(CPUState *cs, int max, @@ -1373,7 +1380,8 @@ static int hyperv_fill_cpuids(CPUState *cs, c->eax = hv_build_cpuid_leaf(cs, HV_CPUID_ENLIGHTMENT_INFO, R_EAX); c->ebx = cpu->hyperv_spinlock_attempts; - if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VAPIC)) { + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VAPIC) && + !hyperv_feat_enabled(cpu, HYPERV_FEAT_AVIC)) { c->eax |= HV_APIC_ACCESS_RECOMMENDED; } From 9fce3601761779f14d8d1ea32e2b6abf2f704edb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 Sep 2021 17:06:57 +0200 Subject: [PATCH 0190/1334] docs: move gcov section at the end of testing.rst gcov testing applies to all tests, not just make check. Move it out of the make check section. Signed-off-by: Paolo Bonzini --- docs/devel/testing.rst | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 64c9744795..a80df07f6d 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -114,25 +114,6 @@ check-block are in the "auto" group). See the "QEMU iotests" section below for more information. -GCC gcov support ----------------- - -``gcov`` is a GCC tool to analyze the testing coverage by -instrumenting the tested code. To use it, configure QEMU with -``--enable-gcov`` option and build. Then run ``make check`` as usual. - -If you want to gather coverage information on a single test the ``make -clean-gcda`` target can be used to delete any existing coverage -information before running a single test. - -You can generate a HTML coverage report by executing ``make -coverage-html`` which will create -``meson-logs/coveragereport/index.html``. - -Further analysis can be conducted by running the ``gcov`` command -directly on the various .gcda output files. Please read the ``gcov`` -documentation for more information. - QEMU iotests ============ @@ -1302,3 +1283,22 @@ exercise as many corner cases as possible. It is a useful test suite to run to exercise QEMU's linux-user code:: https://linux-test-project.github.io/ + +GCC gcov support +================ + +``gcov`` is a GCC tool to analyze the testing coverage by +instrumenting the tested code. To use it, configure QEMU with +``--enable-gcov`` option and build. Then run the tests as usual. + +If you want to gather coverage information on a single test the ``make +clean-gcda`` target can be used to delete any existing coverage +information before running a single test. + +You can generate a HTML coverage report by executing ``make +coverage-html`` which will create +``meson-logs/coveragereport/index.html``. + +Further analysis can be conducted by running the ``gcov`` command +directly on the various .gcda output files. Please read the ``gcov`` +documentation for more information. From af7228b88dbe80ed5d5258b49be8b48ab351a476 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 2 Sep 2021 11:35:29 +0200 Subject: [PATCH 0191/1334] i386: Make Hyper-V version id configurable Currently, we hardcode Hyper-V version id (CPUID 0x40000002) to WS2008R2 and it is known that certain tools in Windows check this. It seems useful to provide some flexibility by making it possible to change this info at will. CPUID information is defined in TLFS as: EAX: Build Number EBX Bits 31-16: Major Version Bits 15-0: Minor Version ECX Service Pack EDX Bits 31-24: Service Branch Bits 23-0: Service Number Signed-off-by: Vitaly Kuznetsov Message-Id: <20210902093530.345756-8-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- docs/hyperv.txt | 14 ++++++++++++++ target/i386/cpu.c | 15 +++++++++++---- target/i386/cpu.h | 7 ++++++- target/i386/kvm/kvm.c | 26 ++++++++++++++++---------- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index cd1ea3bbe9..7803495468 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -211,6 +211,20 @@ When the option is set to 'on' QEMU will always enable the feature, regardless of host setup. To keep guests secure, this can only be used in conjunction with exposing correct vCPU topology and vCPU pinning. +3.20. hv-version-id-{build,major,minor,spack,sbranch,snumber} +============================================================= +This changes Hyper-V version identification in CPUID 0x40000002.EAX-EDX from the +default (WS2008R2). +- hv-version-id-build sets 'Build Number' (32 bits) +- hv-version-id-major sets 'Major Version' (16 bits) +- hv-version-id-minor sets 'Minor Version' (16 bits) +- hv-version-id-spack sets 'Service Pack' (32 bits) +- hv-version-id-sbranch sets 'Service Branch' (8 bits) +- hv-version-id-snumber sets 'Service Number' (24 bits) + +Note: hv-version-id-* are not enlightenments and thus don't enable Hyper-V +identification when specified without any other enlightenments. + 4. Supplementary features ========================= diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 8154343cc4..d1d057fabe 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6258,10 +6258,6 @@ static void x86_cpu_hyperv_realize(X86CPU *cpu) cpu->hyperv_interface_id[2] = 0; cpu->hyperv_interface_id[3] = 0; - /* Hypervisor system identity */ - cpu->hyperv_version_id[0] = 0x00001bbc; - cpu->hyperv_version_id[1] = 0x00060001; - /* Hypervisor implementation limits */ cpu->hyperv_limits[0] = 64; cpu->hyperv_limits[1] = 0; @@ -6840,6 +6836,17 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("hv-passthrough", X86CPU, hyperv_passthrough, false), DEFINE_PROP_BOOL("hv-enforce-cpuid", X86CPU, hyperv_enforce_cpuid, false), + /* WS2008R2 identify by default */ + DEFINE_PROP_UINT32("hv-version-id-build", X86CPU, hyperv_ver_id_build, + 0x1bbc), + DEFINE_PROP_UINT16("hv-version-id-major", X86CPU, hyperv_ver_id_major, + 0x0006), + DEFINE_PROP_UINT16("hv-version-id-minor", X86CPU, hyperv_ver_id_minor, + 0x0001), + DEFINE_PROP_UINT32("hv-version-id-spack", X86CPU, hyperv_ver_id_sp, 0), + DEFINE_PROP_UINT8("hv-version-id-sbranch", X86CPU, hyperv_ver_id_sb, 0), + DEFINE_PROP_UINT32("hv-version-id-snumber", X86CPU, hyperv_ver_id_sn, 0), + DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), DEFINE_PROP_BOOL("x-force-features", X86CPU, force_features, false), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 65f0ee2caf..3edaad7688 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1717,10 +1717,15 @@ struct X86CPU { OnOffAuto hyperv_no_nonarch_cs; uint32_t hyperv_vendor_id[3]; uint32_t hyperv_interface_id[4]; - uint32_t hyperv_version_id[4]; uint32_t hyperv_limits[3]; uint32_t hyperv_nested[4]; bool hyperv_enforce_cpuid; + uint32_t hyperv_ver_id_build; + uint16_t hyperv_ver_id_major; + uint16_t hyperv_ver_id_minor; + uint32_t hyperv_ver_id_sp; + uint8_t hyperv_ver_id_sb; + uint32_t hyperv_ver_id_sn; bool check_cpuid; bool enforce_cpuid; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 68faf72e34..f25837f63f 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1258,14 +1258,18 @@ bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp) cpu->hyperv_interface_id[3] = hv_cpuid_get_host(cs, HV_CPUID_INTERFACE, R_EDX); - cpu->hyperv_version_id[0] = + cpu->hyperv_ver_id_build = hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EAX); - cpu->hyperv_version_id[1] = - hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EBX); - cpu->hyperv_version_id[2] = + cpu->hyperv_ver_id_major = + hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EBX) >> 16; + cpu->hyperv_ver_id_minor = + hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EBX) & 0xffff; + cpu->hyperv_ver_id_sp = hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_ECX); - cpu->hyperv_version_id[3] = - hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EDX); + cpu->hyperv_ver_id_sb = + hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EDX) >> 24; + cpu->hyperv_ver_id_sn = + hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EDX) & 0xffffff; cpu->hv_max_vps = hv_cpuid_get_host(cs, HV_CPUID_IMPLEMENT_LIMITS, R_EAX); @@ -1351,10 +1355,12 @@ static int hyperv_fill_cpuids(CPUState *cs, c = &cpuid_ent[cpuid_i++]; c->function = HV_CPUID_VERSION; - c->eax = cpu->hyperv_version_id[0]; - c->ebx = cpu->hyperv_version_id[1]; - c->ecx = cpu->hyperv_version_id[2]; - c->edx = cpu->hyperv_version_id[3]; + c->eax = cpu->hyperv_ver_id_build; + c->ebx = (uint32_t)cpu->hyperv_ver_id_major << 16 | + cpu->hyperv_ver_id_minor; + c->ecx = cpu->hyperv_ver_id_sp; + c->edx = (uint32_t)cpu->hyperv_ver_id_sb << 24 | + (cpu->hyperv_ver_id_sn & 0xffffff); c = &cpuid_ent[cpuid_i++]; c->function = HV_CPUID_FEATURES; From 16e79e1b01a698908e14eda3078d4a8e7b1b9c2b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Sep 2021 17:21:58 +0200 Subject: [PATCH 0192/1334] docs: reorganize testing.rst Clean up the heading levels to use === --- ~~~ ^^^ '''. Reorganize the outline for the Avocado part, and always include headings for the class names. Signed-off-by: Paolo Bonzini --- docs/devel/testing.rst | 146 +++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 70 deletions(-) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index a80df07f6d..7500f076c2 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -1,11 +1,10 @@ -=============== Testing in QEMU =============== This document describes the testing infrastructure in QEMU. Testing with "make check" -========================= +------------------------- The "make check" testing family includes most of the C based tests in QEMU. For a quick help, run ``make check-help`` from the source tree. @@ -24,7 +23,7 @@ expect the executables to exist and will fail with obscure messages if they cannot find them. Unit tests ----------- +~~~~~~~~~~ Unit tests, which can be invoked with ``make check-unit``, are simple C tests that typically link to individual QEMU object files and exercise them by @@ -67,7 +66,7 @@ and copy the actual command line which executes the unit test, then run it from the command line. QTest ------ +~~~~~ QTest is a device emulation testing framework. It can be very useful to test device models; it could also control certain aspects of QEMU (such as virtual @@ -81,7 +80,7 @@ QTest cases can be executed with make check-qtest QAPI schema tests ------------------ +~~~~~~~~~~~~~~~~~ The QAPI schema tests validate the QAPI parser used by QMP, by feeding predefined input to the parser and comparing the result with the reference @@ -108,14 +107,14 @@ parser (either fixing a bug or extending/modifying the syntax). To do this: ``qapi-schema += foo.json`` check-block ------------ +~~~~~~~~~~~ ``make check-block`` runs a subset of the block layer iotests (the tests that are in the "auto" group). See the "QEMU iotests" section below for more information. QEMU iotests -============ +------------ QEMU iotests, under the directory ``tests/qemu-iotests``, is the testing framework widely used to test block layer related features. It is higher level @@ -152,7 +151,7 @@ More options are supported by the ``./check`` script, run ``./check -h`` for help. Writing a new test case ------------------------ +~~~~~~~~~~~~~~~~~~~~~~~ Consider writing a tests case when you are making any changes to the block layer. An iotest case is usually the choice for that. There are already many @@ -206,7 +205,8 @@ test failure. If using such devices are explicitly desired, consider adding ``locking=off`` option to disable image locking. Debugging a test case ------------------------ +~~~~~~~~~~~~~~~~~~~~~ + The following options to the ``check`` script can be useful when debugging a failing test: @@ -235,7 +235,7 @@ a failing test: ``$TEST_DIR/qemu-machine-``. Test case groups ----------------- +~~~~~~~~~~~~~~~~ "Tests may belong to one or more test groups, which are defined in the form of a comment in the test source file. By convention, test groups are listed @@ -285,10 +285,10 @@ Note that the following group names have a special meaning: .. _container-ref: Container based tests -===================== +--------------------- Introduction ------------- +~~~~~~~~~~~~ The container testing framework in QEMU utilizes public images to build and test QEMU in predefined and widely accessible Linux @@ -303,7 +303,7 @@ The container images are also used to augment the generation of tests for testing TCG. See :ref:`checktcg-ref` for more details. Docker Prerequisites --------------------- +~~~~~~~~~~~~~~~~~~~~ Install "docker" with the system package manager and start the Docker service on your development machine, then make sure you have the privilege to run @@ -334,7 +334,7 @@ exploit the whole host with Docker bind mounting or other privileged operations. So only do it on development machines. Podman Prerequisites --------------------- +~~~~~~~~~~~~~~~~~~~~ Install "podman" with the system package manager. @@ -346,7 +346,7 @@ Install "podman" with the system package manager. The last command should print an empty table, to verify the system is ready. Quickstart ----------- +~~~~~~~~~~ From source tree, type ``make docker-help`` to see the help. Testing can be started without configuring or building QEMU (``configure`` and @@ -362,7 +362,7 @@ is downloaded and initialized automatically), in which the ``test-build`` job is executed. Registry --------- +~~~~~~~~ The QEMU project has a container registry hosted by GitLab at ``registry.gitlab.com/qemu-project/qemu`` which will automatically be @@ -376,7 +376,7 @@ locally by using the ``NOCACHE`` build option: make docker-image-debian10 NOCACHE=1 Images ------- +~~~~~~ Along with many other images, the ``centos8`` image is defined in a Dockerfile in ``tests/docker/dockerfiles/``, called ``centos8.docker``. ``make docker-help`` @@ -391,7 +391,7 @@ mainly used to do necessary host side setup. One such setup is ``binfmt_misc``, for example, to make qemu-user powered cross build containers work. Tests ------ +~~~~~ Different tests are added to cover various configurations to build and test QEMU. Docker tests are the executables under ``tests/docker`` named @@ -402,7 +402,7 @@ source and build it. The full list of tests is printed in the ``make docker-help`` help. Debugging a Docker test failure -------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When CI tasks, maintainers or yourself report a Docker test failure, follow the below steps to debug it: @@ -419,7 +419,7 @@ below steps to debug it: the prompt for debug. Options -------- +~~~~~~~ Various options can be used to affect how Docker tests are done. The full list is in the ``make docker`` help text. The frequently used ones are: @@ -433,7 +433,7 @@ list is in the ``make docker`` help text. The frequently used ones are: failure" section. Thread Sanitizer -================ +---------------- Thread Sanitizer (TSan) is a tool which can detect data races. QEMU supports building and testing with this tool. @@ -443,7 +443,7 @@ For more information on TSan: https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual Thread Sanitizer in Docker ---------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~ TSan is currently supported in the ubuntu2004 docker. The test-tsan test will build using TSan and then run make check. @@ -458,7 +458,7 @@ We recommend using DEBUG=1 to allow launching the test from inside the docker, and to allow review of the warnings generated by TSan. Building and Testing with TSan ------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is possible to build and test with TSan, with a few additional steps. These steps are normally done automatically in the docker. @@ -497,7 +497,7 @@ This allows for running the test and then checking the warnings afterwards. If you want TSan to stop and exit with error on warnings, use exitcode=66. TSan Suppressions ------------------ +~~~~~~~~~~~~~~~~~ Keep in mind that for any data race warning, although there might be a data race detected by TSan, there might be no actual bug here. TSan provides several different mechanisms for suppressing warnings. In general it is recommended @@ -523,7 +523,7 @@ More information on the file format can be found here under "Blacklist Format": https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags TSan Annotations ----------------- +~~~~~~~~~~~~~~~~ include/qemu/tsan.h defines annotations. See this file for more descriptions of the annotations themselves. Annotations can be used to suppress TSan warnings or give TSan more information so that it can detect proper @@ -540,14 +540,14 @@ The full set of annotations can be found here: https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp VM testing -========== +---------- This test suite contains scripts that bootstrap various guest images that have necessary packages to build QEMU. The basic usage is documented in ``Makefile`` help which is displayed with ``make vm-help``. Quickstart ----------- +~~~~~~~~~~ Run ``make vm-help`` to list available make targets. Invoke a specific make command to run build test in an image. For example, ``make vm-build-freebsd`` @@ -562,7 +562,7 @@ concerned about attackers taking control of the guest and potentially exploiting a QEMU security bug to compromise the host. QEMU binaries -------------- +~~~~~~~~~~~~~ By default, qemu-system-x86_64 is searched in $PATH to run the guest. If there isn't one, or if it is older than 2.10, the test won't work. In this case, @@ -571,20 +571,20 @@ provide the QEMU binary in env var: ``QEMU=/path/to/qemu-2.10+``. Likewise the path to qemu-img can be set in QEMU_IMG environment variable. Make jobs ---------- +~~~~~~~~~ The ``-j$X`` option in the make command line is not propagated into the VM, specify ``J=$X`` to control the make jobs in the guest. Debugging ---------- +~~~~~~~~~ Add ``DEBUG=1`` and/or ``V=1`` to the make command to allow interactive debugging and verbose output. If this is not enough, see the next section. ``V=1`` will be propagated down into the make jobs in the guest. Manual invocation ------------------ +~~~~~~~~~~~~~~~~~ Each guest script is an executable script with the same command line options. For example to work with the netbsd guest, use ``$QEMU_SRC/tests/vm/netbsd``: @@ -608,7 +608,7 @@ For example to work with the netbsd guest, use ``$QEMU_SRC/tests/vm/netbsd``: $ ./netbsd --interactive --image /var/tmp/netbsd.img sh Adding new guests ------------------ +~~~~~~~~~~~~~~~~~ Please look at existing guest scripts for how to add new guests. @@ -641,7 +641,7 @@ the script's ``main()``. recommended. Image fuzzer testing -==================== +-------------------- An image fuzzer was added to exercise format drivers. Currently only qcow2 is supported. To start the fuzzer, run @@ -654,7 +654,7 @@ Alternatively, some command different from "qemu-img info" can be tested, by changing the ``-c`` option. Acceptance tests using the Avocado Framework -============================================ +-------------------------------------------- The ``tests/acceptance`` directory hosts functional tests, also known as acceptance level tests. They're usually higher level tests, and @@ -693,7 +693,7 @@ Tests based on ``avocado_qemu.Test`` can easily: - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html Running tests -------------- +~~~~~~~~~~~~~ You can run the acceptance tests simply by executing: @@ -791,7 +791,7 @@ of Avocado or ``make check-acceptance``, and can also be queried using: tests/venv/bin/avocado list tests/acceptance Manual Installation -------------------- +~~~~~~~~~~~~~~~~~~~ To manually install Avocado and its dependencies, run: @@ -804,7 +804,7 @@ Alternatively, follow the instructions on this link: https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/installing.html Overview --------- +~~~~~~~~ The ``tests/acceptance/avocado_qemu`` directory provides the ``avocado_qemu`` Python module, containing the ``avocado_qemu.Test`` @@ -840,7 +840,7 @@ in the current directory, tagged as "quick", run: avocado run -t quick . The ``avocado_qemu.Test`` base test class ------------------------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``avocado_qemu.Test`` class has a number of characteristics that are worth being mentioned right away. @@ -890,7 +890,7 @@ At test "tear down", ``avocado_qemu.Test`` handles all the QEMUMachines shutdown. The ``avocado_qemu.LinuxTest`` base test class -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``avocado_qemu.LinuxTest`` is further specialization of the ``avocado_qemu.Test`` class, so it contains all the characteristics of @@ -933,7 +933,7 @@ execution of a QEMU binary, giving its users: a more succinct and intuitive way QEMU binary selection -~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^ The QEMU binary used for the ``self.vm`` QEMUMachine instance will primarily depend on the value of the ``qemu_bin`` parameter. If it's @@ -954,20 +954,23 @@ The resulting ``qemu_bin`` value will be preserved in the ``avocado_qemu.Test`` as an attribute with the same name. Attribute reference -------------------- +~~~~~~~~~~~~~~~~~~~ + +Test +^^^^ Besides the attributes and methods that are part of the base ``avocado.Test`` class, the following attributes are available on any ``avocado_qemu.Test`` instance. vm -~~ +'' A QEMUMachine instance, initially configured according to the given ``qemu_bin`` parameter. arch -~~~~ +'''' The architecture can be used on different levels of the stack, e.g. by the framework or by the test itself. At the framework level, it will @@ -984,7 +987,7 @@ name. If one is not given explicitly, it will either be set to ``:avocado: tags=arch:VALUE`` tag, it will be set to ``VALUE``. cpu -~~~ +''' The cpu model that will be set to all QEMUMachine instances created by the test. @@ -995,7 +998,7 @@ name. If one is not given explicitly, it will either be set to ``:avocado: tags=cpu:VALUE`` tag, it will be set to ``VALUE``. machine -~~~~~~~ +''''''' The machine type that will be set to all QEMUMachine instances created by the test. @@ -1006,20 +1009,20 @@ name. If one is not given explicitly, it will either be set to ``:avocado: tags=machine:VALUE`` tag, it will be set to ``VALUE``. qemu_bin -~~~~~~~~ +'''''''' The preserved value of the ``qemu_bin`` parameter or the result of the dynamic probe for a QEMU binary in the current working directory or source tree. LinuxTest -~~~~~~~~~ +^^^^^^^^^ Besides the attributes present on the ``avocado_qemu.Test`` base class, the ``avocado_qemu.LinuxTest`` adds the following attributes: distro -...... +'''''' The name of the Linux distribution used as the guest image for the test. The name should match the **Provider** column on the list @@ -1028,7 +1031,7 @@ of images supported by the avocado.utils.vmimage library: https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images distro_version -.............. +'''''''''''''' The version of the Linux distribution as the guest image for the test. The name should match the **Version** column on the list @@ -1037,7 +1040,7 @@ of images supported by the avocado.utils.vmimage library: https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images distro_checksum -............... +''''''''''''''' The sha256 hash of the guest image file used for the test. @@ -1046,7 +1049,7 @@ same name), no validation on the integrity of the image will be performed. Parameter reference -------------------- +~~~~~~~~~~~~~~~~~~~ To understand how Avocado parameters are accessed by tests, and how they can be passed to tests, please refer to:: @@ -1060,8 +1063,11 @@ like the following: PARAMS (key=qemu_bin, path=*, default=./qemu-system-x86_64) => './qemu-system-x86_64 +Test +^^^^ + arch -~~~~ +'''' The architecture that will influence the selection of a QEMU binary (when one is not explicitly given). @@ -1074,31 +1080,30 @@ This parameter has a direct relation with the ``arch`` attribute. If not given, it will default to None. cpu -~~~ +''' The cpu model that will be set to all QEMUMachine instances created by the test. machine -~~~~~~~ +''''''' The machine type that will be set to all QEMUMachine instances created by the test. - qemu_bin -~~~~~~~~ +'''''''' The exact QEMU binary to be used on QEMUMachine. LinuxTest -~~~~~~~~~ +^^^^^^^^^ Besides the parameters present on the ``avocado_qemu.Test`` base class, the ``avocado_qemu.LinuxTest`` adds the following parameters: distro -...... +'''''' The name of the Linux distribution used as the guest image for the test. The name should match the **Provider** column on the list @@ -1107,7 +1112,7 @@ of images supported by the avocado.utils.vmimage library: https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images distro_version -.............. +'''''''''''''' The version of the Linux distribution as the guest image for the test. The name should match the **Version** column on the list @@ -1116,7 +1121,7 @@ of images supported by the avocado.utils.vmimage library: https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images distro_checksum -............... +''''''''''''''' The sha256 hash of the guest image file used for the test. @@ -1124,7 +1129,8 @@ If this value is not set in the code or by this parameter no validation on the integrity of the image will be performed. Skipping tests --------------- +~~~~~~~~~~~~~~ + The Avocado framework provides Python decorators which allow for easily skip tests running under certain conditions. For example, on the lack of a binary on the test system or when the running environment is a CI system. For further @@ -1139,7 +1145,7 @@ environment variables became a kind of standard way to enable/disable tests. Here is a list of the most used variables: AVOCADO_ALLOW_LARGE_STORAGE -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tests which are going to fetch or produce assets considered *large* are not going to run unless that ``AVOCADO_ALLOW_LARGE_STORAGE=1`` is exported on the environment. @@ -1148,7 +1154,7 @@ The definition of *large* is a bit arbitrary here, but it usually means an asset which occupies at least 1GB of size on disk when uncompressed. AVOCADO_ALLOW_UNTRUSTED_CODE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ There are tests which will boot a kernel image or firmware that can be considered not safe to run on the developer's workstation, thus they are skipped by default. The definition of *not safe* is also arbitrary but @@ -1159,7 +1165,7 @@ You should export ``AVOCADO_ALLOW_UNTRUSTED_CODE=1`` on the environment in order to allow tests which make use of those kind of assets. AVOCADO_TIMEOUT_EXPECTED -~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^ The Avocado framework has a timeout mechanism which interrupts tests to avoid the test suite of getting stuck. The timeout value can be set via test parameter or property defined in the test class, for further details:: @@ -1173,7 +1179,7 @@ compiled with debug flags. Therefore, the ``AVOCADO_TIMEOUT_EXPECTED`` variable has been used to determine whether those tests should run or not. GITLAB_CI -~~~~~~~~~ +^^^^^^^^^ A number of tests are flagged to not run on the GitLab CI. Usually because they proved to the flaky or there are constraints on the CI environment which would make them fail. If you encounter a similar situation then use that @@ -1186,7 +1192,7 @@ variable as shown on the code snippet below to skip the test: do_something() Uninstalling Avocado --------------------- +~~~~~~~~~~~~~~~~~~~~ If you've followed the manual installation instructions above, you can easily uninstall Avocado. Start by listing the packages you have @@ -1204,7 +1210,7 @@ Avocado is installed will be cleaned up as part of ``make check-clean``. .. _checktcg-ref: Testing with "make check-tcg" -============================= +----------------------------- The check-tcg tests are intended for simple smoke tests of both linux-user and softmmu TCG functionality. However to build test @@ -1237,7 +1243,7 @@ itself. See :ref:`container-ref` for more details. Running subset of tests ------------------------ +~~~~~~~~~~~~~~~~~~~~~~~ You can build the tests for one architecture:: @@ -1251,7 +1257,7 @@ Adding ``V=1`` to the invocation will show the details of how to invoke QEMU for the test which is useful for debugging tests. TCG test dependencies ---------------------- +~~~~~~~~~~~~~~~~~~~~~ The TCG tests are deliberately very light on dependencies and are either totally bare with minimal gcc lib support (for softmmu tests) @@ -1285,7 +1291,7 @@ to run to exercise QEMU's linux-user code:: https://linux-test-project.github.io/ GCC gcov support -================ +---------------- ``gcov`` is a GCC tool to analyze the testing coverage by instrumenting the tested code. To use it, configure QEMU with From f701ecec2bbaae2d04985eba87924a7329534e9a Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 2 Sep 2021 11:35:30 +0200 Subject: [PATCH 0193/1334] i386: Change the default Hyper-V version to match WS2016 KVM implements some Hyper-V 2016 functions so providing WS2008R2 version is somewhat incorrect. While generally guests shouldn't care about it and always check feature bits, it is known that some tools in Windows actually check version info. For compatibility reasons make the change for 6.2 machine types only. Signed-off-by: Vitaly Kuznetsov Message-Id: <20210902093530.345756-9-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- docs/hyperv.txt | 2 +- hw/i386/pc.c | 6 +++++- target/i386/cpu.c | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 7803495468..5d99fd9a72 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -214,7 +214,7 @@ exposing correct vCPU topology and vCPU pinning. 3.20. hv-version-id-{build,major,minor,spack,sbranch,snumber} ============================================================= This changes Hyper-V version identification in CPUID 0x40000002.EAX-EDX from the -default (WS2008R2). +default (WS2016). - hv-version-id-build sets 'Build Number' (32 bits) - hv-version-id-major sets 'Major Version' (16 bits) - hv-version-id-minor sets 'Minor Version' (16 bits) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 28e1b83b9d..86223acfd3 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -93,7 +93,11 @@ #include "trace.h" #include CONFIG_DEVICES -GlobalProperty pc_compat_6_1[] = {}; +GlobalProperty pc_compat_6_1[] = { + { TYPE_X86_CPU, "hv-version-id-build", "0x1bbc" }, + { TYPE_X86_CPU, "hv-version-id-major", "0x0006" }, + { TYPE_X86_CPU, "hv-version-id-minor", "0x0001" }, +}; const size_t pc_compat_6_1_len = G_N_ELEMENTS(pc_compat_6_1); GlobalProperty pc_compat_6_0[] = { diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d1d057fabe..a7b1b6aa93 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6838,11 +6838,11 @@ static Property x86_cpu_properties[] = { /* WS2008R2 identify by default */ DEFINE_PROP_UINT32("hv-version-id-build", X86CPU, hyperv_ver_id_build, - 0x1bbc), + 0x3839), DEFINE_PROP_UINT16("hv-version-id-major", X86CPU, hyperv_ver_id_major, - 0x0006), + 0x000A), DEFINE_PROP_UINT16("hv-version-id-minor", X86CPU, hyperv_ver_id_minor, - 0x0001), + 0x0000), DEFINE_PROP_UINT32("hv-version-id-spack", X86CPU, hyperv_ver_id_sp, 0), DEFINE_PROP_UINT8("hv-version-id-sbranch", X86CPU, hyperv_ver_id_sb, 0), DEFINE_PROP_UINT32("hv-version-id-snumber", X86CPU, hyperv_ver_id_sn, 0), From 1c00917409c9604cfc587045ba37395a48337dff Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:04 -0400 Subject: [PATCH 0194/1334] qapi/pylintrc: ignore 'consider-using-f-string' warning Pylint 2.11.x adds this warning. We're not yet ready to pursue that conversion, so silence it for now. Signed-off-by: John Snow Message-Id: <20210930205716.1148693-2-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/pylintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index c5275d5f59..5b7dbc58ad 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -23,6 +23,7 @@ disable=fixme, too-many-branches, too-many-statements, too-many-instance-attributes, + consider-using-f-string, [REPORTS] From 2adb988ed4ca31813d237c475a6a327ef16c5432 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:05 -0400 Subject: [PATCH 0195/1334] qapi/gen: use dict.items() to iterate over _modules New pylint warning. I could silence it, but this is the only occurrence in the entire tree, including everything in iotests/ and python/. Easier to just change this one instance. (The warning is emitted in cases where you are fetching the values anyway, so you may as well just take advantage of the iterator to avoid redundant lookups.) Signed-off-by: John Snow Message-Id: <20210930205716.1148693-3-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/gen.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index ab26d5c937..2ec1e7b3b6 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -296,10 +296,9 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): self._current_module = old_module def write(self, output_dir: str, opt_builtins: bool = False) -> None: - for name in self._module: + for name, (genc, genh) in self._module.items(): if QAPISchemaModule.is_builtin_module(name) and not opt_builtins: continue - (genc, genh) = self._module[name] genc.write(output_dir) genh.write(output_dir) From 012336a152641b264a65176a388a7fb0118e1781 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:06 -0400 Subject: [PATCH 0196/1334] qapi/parser: fix unused check_args_section arguments Pylint informs us we're not using these arguments. Oops, it's right. Correct the error message and remove the remaining unused parameter. Fix test output now that the error message is improved. Fixes: e151941d1b Signed-off-by: John Snow Message-Id: <20210930205716.1148693-4-jsnow@redhat.com> [Commit message formatting tweaked] Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 16 +++++++++------- tests/qapi-schema/doc-bad-feature.err | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index f03ba2cfec..bfd2dbfd9a 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -753,16 +753,18 @@ class QAPIDoc: def check(self): - def check_args_section(args, info, what): + def check_args_section(args, what): bogus = [name for name, section in args.items() if not section.member] if bogus: raise QAPISemError( self.info, - "documented member%s '%s' %s not exist" - % ("s" if len(bogus) > 1 else "", - "', '".join(bogus), - "do" if len(bogus) > 1 else "does")) + "documented %s%s '%s' %s not exist" % ( + what, + "s" if len(bogus) > 1 else "", + "', '".join(bogus), + "do" if len(bogus) > 1 else "does" + )) - check_args_section(self.args, self.info, 'members') - check_args_section(self.features, self.info, 'features') + check_args_section(self.args, 'member') + check_args_section(self.features, 'feature') diff --git a/tests/qapi-schema/doc-bad-feature.err b/tests/qapi-schema/doc-bad-feature.err index e4c62adfa3..49d1746c3d 100644 --- a/tests/qapi-schema/doc-bad-feature.err +++ b/tests/qapi-schema/doc-bad-feature.err @@ -1 +1 @@ -doc-bad-feature.json:3: documented member 'a' does not exist +doc-bad-feature.json:3: documented feature 'a' does not exist From a9e2eb06ed061f37c3ba6ad52722eef20afd713a Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:07 -0400 Subject: [PATCH 0197/1334] qapi: Add spaces after symbol declaration for consistency Several QGA definitions omit a blank line after the symbol declaration. This works OK currently, but it's the only place where we do this. Adjust it for consistency. Future commits may wind up enforcing this formatting. Signed-off-by: John Snow Message-Id: <20210930205716.1148693-5-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/block-core.json | 1 + qga/qapi-schema.json | 3 +++ tests/qapi-schema/doc-good.json | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/qapi/block-core.json b/qapi/block-core.json index 623a4f4a3f..6d3217abb6 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3132,6 +3132,7 @@ ## # @BlockdevQcow2EncryptionFormat: +# # @aes: AES-CBC with plain64 initialization vectors # # Since: 2.10 diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index c60f5e669d..94e4aacdcc 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1140,6 +1140,7 @@ ## # @GuestExec: +# # @pid: pid of child process in guest OS # # Since: 2.5 @@ -1171,6 +1172,7 @@ ## # @GuestHostName: +# # @host-name: Fully qualified domain name of the guest OS # # Since: 2.10 @@ -1197,6 +1199,7 @@ ## # @GuestUser: +# # @user: Username # @domain: Logon domain (windows only) # @login-time: Time of login of this user on the computer. If multiple diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index a20acffd8b..86dc25d2bd 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -53,6 +53,7 @@ ## # @Enum: +# # @one: The _one_ {and only} # # Features: @@ -67,6 +68,7 @@ ## # @Base: +# # @base1: # the first member ## @@ -75,6 +77,7 @@ ## # @Variant1: +# # A paragraph # # Another paragraph (but no @var: line) @@ -91,11 +94,13 @@ ## # @Variant2: +# ## { 'struct': 'Variant2', 'data': {} } ## # @Object: +# # Features: # @union-feat1: a feature ## @@ -109,6 +114,7 @@ ## # @Alternate: +# # @i: an integer # @b is undocumented # @@ -126,6 +132,7 @@ ## # @cmd: +# # @arg1: the first argument # # @arg2: the second @@ -175,6 +182,7 @@ ## # @EVT_BOXED: +# # Features: # @feat3: a feature ## From cd87c14cde5db42a2f13bfdbba1f3cbeb347a411 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:08 -0400 Subject: [PATCH 0198/1334] qapi/parser: remove FIXME comment from _append_body_line True, we do not check the validity of this symbol -- but we don't check the validity of definition names during parse, either -- that happens later, during the expr check. I don't want to introduce a dependency on expr.py:check_name_str here and introduce a cycle. Instead, rest assured that a documentation block is required for each definition. This requirement uses the names of each section to ensure that we fulfilled this requirement. e.g., let's say that block-core.json has a comment block for "Snapshot!Info" by accident. We'll see this error message: In file included from ../../qapi/block.json:8: ../../qapi/block-core.json: In struct 'SnapshotInfo': ../../qapi/block-core.json:38: documentation comment is for 'Snapshot!Info' That's a pretty decent error message. Now, let's say that we actually mangle it twice, identically: ../../qapi/block-core.json: In struct 'Snapshot!Info': ../../qapi/block-core.json:38: struct has an invalid name That's also pretty decent. If we forget to fix it in both places, we'll just be back to the first error. Therefore, let's just drop this FIXME and adjust the error message to not imply a more thorough check than is actually performed. Signed-off-by: John Snow Message-Id: <20210930205716.1148693-6-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 6 ++++-- tests/qapi-schema/doc-empty-symbol.err | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index bfd2dbfd9a..23898ab1dc 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -556,9 +556,11 @@ class QAPIDoc: if not line.endswith(':'): raise QAPIParseError(self._parser, "line should end with ':'") self.symbol = line[1:-1] - # FIXME invalid names other than the empty string aren't flagged + # Invalid names are not checked here, but the name provided MUST + # match the following definition, which *is* validated in expr.py. if not self.symbol: - raise QAPIParseError(self._parser, "invalid name") + raise QAPIParseError( + self._parser, "name required after '@'") elif self.symbol: # This is a definition documentation block if name.startswith('@') and name.endswith(':'): diff --git a/tests/qapi-schema/doc-empty-symbol.err b/tests/qapi-schema/doc-empty-symbol.err index 81b90e882a..aa51be41b2 100644 --- a/tests/qapi-schema/doc-empty-symbol.err +++ b/tests/qapi-schema/doc-empty-symbol.err @@ -1 +1 @@ -doc-empty-symbol.json:4:1: invalid name +doc-empty-symbol.json:4:1: name required after '@' From 1e20a77576dedf1489ce1cdb6abc4b34663637a4 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:09 -0400 Subject: [PATCH 0199/1334] qapi/parser: clarify _end_section() logic The "if self._section" clause in end_section is mysterious: In which circumstances might we end a section when we don't have one? QAPIDoc always expects there to be a "current section", only except after a call to end_comment(). This actually *shouldn't* ever be 'None', so let's remove that logic so I don't wonder why it's like this again in three months. Signed-off-by: John Snow Message-Id: <20210930205716.1148693-7-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 23898ab1dc..82f1d952b1 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -718,13 +718,21 @@ class QAPIDoc: self.sections.append(self._section) def _end_section(self): - if self._section: - text = self._section.text = self._section.text.strip() - if self._section.name and (not text or text.isspace()): - raise QAPIParseError( - self._parser, - "empty doc section '%s'" % self._section.name) - self._section = None + assert self._section is not None + + text = self._section.text = self._section.text.strip() + + # Only the 'body' section is allowed to have an empty body. + # All other sections, including anonymous ones, must have text. + if self._section != self.body and not text: + # We do not create anonymous sections unless there is + # something to put in them; this is a parser bug. + assert self._section.name + raise QAPIParseError( + self._parser, + "empty doc section '%s'" % self._section.name) + + self._section = None def _append_freeform(self, line): match = re.match(r'(@\S+:)', line) From f4c05aaf148a44d80855eb45b9342feaeeb4764a Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:10 -0400 Subject: [PATCH 0200/1334] qapi/parser: Introduce NullSection Here's the weird bit. QAPIDoc generally expects -- virtually everywhere -- that it will always have a current section. The sole exception to this is in the case that end_comment() is called, which leaves us with *no* section. However, in this case, we also don't expect to actually ever mutate the comment contents ever again. NullSection is just a Null-object that allows us to maintain the invariant that we *always* have a current section, enforced by static typing -- allowing us to type that field as QAPIDoc.Section instead of the more ambiguous Optional[QAPIDoc.Section]. end_section is renamed to switch_section and now accepts as an argument the new section to activate, clarifying that no callers ever just unilaterally end a section; they only do so when starting a new section. Signed-off-by: John Snow Message-Id: <20210930205716.1148693-8-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 82f1d952b1..40c5da4b17 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -478,6 +478,13 @@ class QAPIDoc: def connect(self, member): self.member = member + class NullSection(Section): + """ + Immutable dummy section for use at the end of a doc block. + """ + def append(self, line): + assert False, "Text appended after end_comment() called." + def __init__(self, parser, info): # self._parser is used to report errors with QAPIParseError. The # resulting error position depends on the state of the parser. @@ -525,7 +532,7 @@ class QAPIDoc: self._append_line(line) def end_comment(self): - self._end_section() + self._switch_section(QAPIDoc.NullSection(self._parser)) @staticmethod def _is_section_tag(name): @@ -699,9 +706,9 @@ class QAPIDoc: raise QAPIParseError(self._parser, "'%s' parameter name duplicated" % name) assert not self.sections - self._end_section() - self._section = QAPIDoc.ArgSection(self._parser, name, indent) - symbols_dict[name] = self._section + new_section = QAPIDoc.ArgSection(self._parser, name, indent) + self._switch_section(new_section) + symbols_dict[name] = new_section def _start_args_section(self, name, indent): self._start_symbol_section(self.args, name, indent) @@ -713,13 +720,11 @@ class QAPIDoc: if name in ('Returns', 'Since') and self.has_section(name): raise QAPIParseError(self._parser, "duplicated '%s' section" % name) - self._end_section() - self._section = QAPIDoc.Section(self._parser, name, indent) - self.sections.append(self._section) - - def _end_section(self): - assert self._section is not None + new_section = QAPIDoc.Section(self._parser, name, indent) + self._switch_section(new_section) + self.sections.append(new_section) + def _switch_section(self, new_section): text = self._section.text = self._section.text.strip() # Only the 'body' section is allowed to have an empty body. @@ -732,7 +737,7 @@ class QAPIDoc: self._parser, "empty doc section '%s'" % self._section.name) - self._section = None + self._section = new_section def _append_freeform(self, line): match = re.match(r'(@\S+:)', line) From e7ac60fcd0623fe255f54c33f2bed2e7b2f780f5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:11 -0400 Subject: [PATCH 0201/1334] qapi/parser: add import cycle workaround Adding static types causes a cycle in the QAPI generator: [schema -> expr -> parser -> schema]. It exists because the QAPIDoc class needs the names of types defined by the schema module, but the schema module needs to import both expr.py/parser.py to do its actual parsing. Ultimately, the layering violation is that parser.py should not have any knowledge of specifics of the Schema. QAPIDoc performs double-duty here both as a parser *and* as a finalized object that is part of the schema. In this patch, add the offending type hints alongside the workaround to avoid the cycle becoming a problem at runtime. See https://mypy.readthedocs.io/en/latest/runtime_troubles.html#import-cycles for more information on this workaround technique. I see three ultimate resolutions here: (1) Just keep this patch and use the TYPE_CHECKING trick to eliminate the cycle which is only present during static analysis. (2) Don't bother to annotate connect_member() et al, give them 'object' or 'Any'. I don't particularly like this, because it diminishes the usefulness of type hints for documentation purposes. Still, it's an extremely quick fix. (3) Reimplement doc <--> definition correlation directly in schema.py, integrating doc fields directly into QAPISchemaMember and relieving the QAPIDoc class of the responsibility. Users of the information would instead visit the members first and retrieve their documentation instead of the inverse operation -- visiting the documentation and retrieving their members. My preference is (3), but in the short-term (1) is the easiest way to have my cake (strong type hints) and eat it too (Not have import cycles). Do (1) for now, but plan for (3). Signed-off-by: John Snow Message-Id: <20210930205716.1148693-9-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 40c5da4b17..75582ddb00 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -18,6 +18,7 @@ from collections import OrderedDict import os import re from typing import ( + TYPE_CHECKING, Dict, List, Optional, @@ -30,6 +31,12 @@ from .error import QAPISemError, QAPISourceError from .source import QAPISourceInfo +if TYPE_CHECKING: + # pylint: disable=cyclic-import + # TODO: Remove cycle. [schema -> expr -> parser -> schema] + from .schema import QAPISchemaFeature, QAPISchemaMember + + # Return value alias for get_expr(). _ExprValue = Union[List[object], Dict[str, object], str, bool] @@ -473,9 +480,9 @@ class QAPIDoc: class ArgSection(Section): def __init__(self, parser, name, indent=0): super().__init__(parser, name, indent) - self.member = None + self.member: Optional['QAPISchemaMember'] = None - def connect(self, member): + def connect(self, member: 'QAPISchemaMember') -> None: self.member = member class NullSection(Section): @@ -747,14 +754,14 @@ class QAPIDoc: % match.group(1)) self._section.append(line) - def connect_member(self, member): + def connect_member(self, member: 'QAPISchemaMember') -> None: if member.name not in self.args: # Undocumented TODO outlaw self.args[member.name] = QAPIDoc.ArgSection(self._parser, member.name) self.args[member.name].connect(member) - def connect_feature(self, feature): + def connect_feature(self, feature: 'QAPISchemaFeature') -> None: if feature.name not in self.features: raise QAPISemError(feature.info, "feature '%s' lacks documentation" From 5f0d9f3bc762fcbb1637b5e257c9cd8b9a8aa9ab Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:12 -0400 Subject: [PATCH 0202/1334] qapi/parser: add type hint annotations (QAPIDoc) Annotations do not change runtime behavior. This commit consists of only annotations. Signed-off-by: John Snow Message-Id: <20210930205716.1148693-10-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 67 ++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 75582ddb00..73c1c4ef59 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -37,6 +37,9 @@ if TYPE_CHECKING: from .schema import QAPISchemaFeature, QAPISchemaMember +#: Represents a single Top Level QAPI schema expression. +TopLevelExpr = Dict[str, object] + # Return value alias for get_expr(). _ExprValue = Union[List[object], Dict[str, object], str, bool] @@ -454,7 +457,8 @@ class QAPIDoc: """ class Section: - def __init__(self, parser, name=None, indent=0): + def __init__(self, parser: QAPISchemaParser, + name: Optional[str] = None, indent: int = 0): # parser, for error messages about indentation self._parser = parser # optional section name (argument/member or section name) @@ -463,7 +467,7 @@ class QAPIDoc: # the expected indent level of the text of this section self._indent = indent - def append(self, line): + def append(self, line: str) -> None: # Strip leading spaces corresponding to the expected indent level # Blank lines are always OK. if line: @@ -478,7 +482,8 @@ class QAPIDoc: self.text += line.rstrip() + '\n' class ArgSection(Section): - def __init__(self, parser, name, indent=0): + def __init__(self, parser: QAPISchemaParser, + name: str, indent: int = 0): super().__init__(parser, name, indent) self.member: Optional['QAPISchemaMember'] = None @@ -489,35 +494,34 @@ class QAPIDoc: """ Immutable dummy section for use at the end of a doc block. """ - def append(self, line): + def append(self, line: str) -> None: assert False, "Text appended after end_comment() called." - def __init__(self, parser, info): + def __init__(self, parser: QAPISchemaParser, info: QAPISourceInfo): # self._parser is used to report errors with QAPIParseError. The # resulting error position depends on the state of the parser. # It happens to be the beginning of the comment. More or less # servicable, but action at a distance. self._parser = parser self.info = info - self.symbol = None + self.symbol: Optional[str] = None self.body = QAPIDoc.Section(parser) - # dict mapping parameter name to ArgSection - self.args = OrderedDict() - self.features = OrderedDict() - # a list of Section - self.sections = [] + # dicts mapping parameter/feature names to their ArgSection + self.args: Dict[str, QAPIDoc.ArgSection] = OrderedDict() + self.features: Dict[str, QAPIDoc.ArgSection] = OrderedDict() + self.sections: List[QAPIDoc.Section] = [] # the current section self._section = self.body self._append_line = self._append_body_line - def has_section(self, name): + def has_section(self, name: str) -> bool: """Return True if we have a section with this name.""" for i in self.sections: if i.name == name: return True return False - def append(self, line): + def append(self, line: str) -> None: """ Parse a comment line and add it to the documentation. @@ -538,18 +542,18 @@ class QAPIDoc: line = line[1:] self._append_line(line) - def end_comment(self): + def end_comment(self) -> None: self._switch_section(QAPIDoc.NullSection(self._parser)) @staticmethod - def _is_section_tag(name): + def _is_section_tag(name: str) -> bool: return name in ('Returns:', 'Since:', # those are often singular or plural 'Note:', 'Notes:', 'Example:', 'Examples:', 'TODO:') - def _append_body_line(self, line): + def _append_body_line(self, line: str) -> None: """ Process a line of documentation text in the body section. @@ -591,7 +595,7 @@ class QAPIDoc: # This is a free-form documentation block self._append_freeform(line) - def _append_args_line(self, line): + def _append_args_line(self, line: str) -> None: """ Process a line of documentation text in an argument section. @@ -637,7 +641,7 @@ class QAPIDoc: self._append_freeform(line) - def _append_features_line(self, line): + def _append_features_line(self, line: str) -> None: name = line.split(' ', 1)[0] if name.startswith('@') and name.endswith(':'): @@ -669,7 +673,7 @@ class QAPIDoc: self._append_freeform(line) - def _append_various_line(self, line): + def _append_various_line(self, line: str) -> None: """ Process a line of documentation text in an additional section. @@ -705,7 +709,11 @@ class QAPIDoc: self._append_freeform(line) - def _start_symbol_section(self, symbols_dict, name, indent): + def _start_symbol_section( + self, + symbols_dict: Dict[str, 'QAPIDoc.ArgSection'], + name: str, + indent: int) -> None: # FIXME invalid names other than the empty string aren't flagged if not name: raise QAPIParseError(self._parser, "invalid parameter name") @@ -717,13 +725,14 @@ class QAPIDoc: self._switch_section(new_section) symbols_dict[name] = new_section - def _start_args_section(self, name, indent): + def _start_args_section(self, name: str, indent: int) -> None: self._start_symbol_section(self.args, name, indent) - def _start_features_section(self, name, indent): + def _start_features_section(self, name: str, indent: int) -> None: self._start_symbol_section(self.features, name, indent) - def _start_section(self, name=None, indent=0): + def _start_section(self, name: Optional[str] = None, + indent: int = 0) -> None: if name in ('Returns', 'Since') and self.has_section(name): raise QAPIParseError(self._parser, "duplicated '%s' section" % name) @@ -731,7 +740,7 @@ class QAPIDoc: self._switch_section(new_section) self.sections.append(new_section) - def _switch_section(self, new_section): + def _switch_section(self, new_section: 'QAPIDoc.Section') -> None: text = self._section.text = self._section.text.strip() # Only the 'body' section is allowed to have an empty body. @@ -746,7 +755,7 @@ class QAPIDoc: self._section = new_section - def _append_freeform(self, line): + def _append_freeform(self, line: str) -> None: match = re.match(r'(@\S+:)', line) if match: raise QAPIParseError(self._parser, @@ -768,14 +777,16 @@ class QAPIDoc: % feature.name) self.features[feature.name].connect(feature) - def check_expr(self, expr): + def check_expr(self, expr: TopLevelExpr) -> None: if self.has_section('Returns') and 'command' not in expr: raise QAPISemError(self.info, "'Returns:' is only valid for commands") - def check(self): + def check(self) -> None: - def check_args_section(args, what): + def check_args_section( + args: Dict[str, QAPIDoc.ArgSection], what: str + ) -> None: bogus = [name for name, section in args.items() if not section.member] if bogus: From 15acf48cfed15b37771922093693007d1ad09219 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:13 -0400 Subject: [PATCH 0203/1334] qapi/parser: Add FIXME for consolidating JSON-related types The fix for this comment is forthcoming in a future commit, but this will keep me honest. The linting configuration in ./python/setup.cfg prohibits 'FIXME' comments. A goal of this long-running series is to move ./scripts/qapi to ./python/qemu/qapi so that the QAPI generator is regularly type-checked by GitLab CI. This comment is a time-bomb to force me to address this issue prior to that step. Signed-off-by: John Snow Message-Id: <20210930205716.1148693-11-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 73c1c4ef59..0265b47b95 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -43,6 +43,10 @@ TopLevelExpr = Dict[str, object] # Return value alias for get_expr(). _ExprValue = Union[List[object], Dict[str, object], str, bool] +# FIXME: Consolidate and centralize definitions for TopLevelExpr, +# _ExprValue, _JSONValue, and _JSONObject; currently scattered across +# several modules. + class QAPIParseError(QAPISourceError): """Error class for all QAPI schema parsing errors.""" From 2e28283e419357f0ee03a33a9224f908f9f67b04 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:14 -0400 Subject: [PATCH 0204/1334] qapi/parser: enable mypy checks Signed-off-by: John Snow Message-Id: <20210930205716.1148693-12-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/mypy.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini index 54ca4483d6..6625356429 100644 --- a/scripts/qapi/mypy.ini +++ b/scripts/qapi/mypy.ini @@ -3,11 +3,6 @@ strict = True disallow_untyped_calls = False python_version = 3.6 -[mypy-qapi.parser] -disallow_untyped_defs = False -disallow_incomplete_defs = False -check_untyped_defs = False - [mypy-qapi.schema] disallow_untyped_defs = False disallow_incomplete_defs = False From 18e3673e0f8479e392e55245ad70c1eedb383507 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:15 -0400 Subject: [PATCH 0205/1334] qapi/parser: Silence too-few-public-methods warning Eh. Not worth the fuss today. There are bigger fish to fry. Signed-off-by: John Snow Message-Id: <20210930205716.1148693-13-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 0265b47b95..1b006cdc13 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -461,8 +461,10 @@ class QAPIDoc: """ class Section: + # pylint: disable=too-few-public-methods def __init__(self, parser: QAPISchemaParser, name: Optional[str] = None, indent: int = 0): + # parser, for error messages about indentation self._parser = parser # optional section name (argument/member or section name) @@ -498,6 +500,7 @@ class QAPIDoc: """ Immutable dummy section for use at the end of a doc block. """ + # pylint: disable=too-few-public-methods def append(self, line: str) -> None: assert False, "Text appended after end_comment() called." From d183e0481b1510b253ac94e702c76115f3bb6450 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 30 Sep 2021 16:57:16 -0400 Subject: [PATCH 0206/1334] qapi/parser: enable pylint checks Signed-off-by: John Snow Message-Id: <20210930205716.1148693-14-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/pylintrc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index 5b7dbc58ad..b259531a72 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -2,8 +2,7 @@ # Add files or directories matching the regex patterns to the ignore list. # The regex matches against base names, not paths. -ignore-patterns=parser.py, - schema.py, +ignore-patterns=schema.py, [MESSAGES CONTROL] From 3830df5f83b9b52d9496763ce1a50afb9231c998 Mon Sep 17 00:00:00 2001 From: nia Date: Fri, 1 Oct 2021 15:30:03 +0000 Subject: [PATCH 0207/1334] configure: Loosen GCC requirement from 7.5.0 to 7.4.0 As discussed in issue 614, we're shipping GCC 7.4.0 as the system compiler in NetBSD 9, the most recent stable branch, and are still actively interested in QEMU on this platform. The differences between GCC 7.5.0 and 7.4.0 are trivial. Signed-off-by: Nia Alarie Reviewed-by: Richard Henderson Message-Id: Signed-off-by: Paolo Bonzini --- configure | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 1043ccce4f..b0b1a1cc25 100755 --- a/configure +++ b/configure @@ -2094,8 +2094,8 @@ cat > $TMPC << EOF # endif # endif #elif defined(__GNUC__) && defined(__GNUC_MINOR__) -# if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 5) -# error You need at least GCC v7.5.0 to compile QEMU +# if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 4) +# error You need at least GCC v7.4.0 to compile QEMU # endif #else # error You either need GCC or Clang to compiler QEMU @@ -2103,7 +2103,7 @@ cat > $TMPC << EOF int main (void) { return 0; } EOF if ! compile_prog "" "" ; then - error_exit "You need at least GCC v7.5 or Clang v6.0 (or XCode Clang v10.0)" + error_exit "You need at least GCC v7.4 or Clang v6.0 (or XCode Clang v10.0)" fi # Accumulate -Wfoo and -Wno-bar separately. From 75b98cb9f6456ccf194211beffcbf93b0a995fa4 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 29 Sep 2021 18:24:43 +0200 Subject: [PATCH 0208/1334] virtio-mem-pci: Fix memory leak when creating MEMORY_DEVICE_SIZE_CHANGE event Apparently, we don't have to duplicate the string. Fixes: 722a3c783ef4 ("virtio-pci: Send qapi events when the virtio-mem size changes") Cc: qemu-stable@nongnu.org Signed-off-by: David Hildenbrand Reviewed-by: Markus Armbruster Message-Id: <20210929162445.64060-2-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/virtio/virtio-mem-pci.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index fa5395cd88..7e384b7397 100644 --- a/hw/virtio/virtio-mem-pci.c +++ b/hw/virtio/virtio-mem-pci.c @@ -88,13 +88,8 @@ static void virtio_mem_pci_size_change_notify(Notifier *notifier, void *data) size_change_notifier); DeviceState *dev = DEVICE(pci_mem); const uint64_t * const size_p = data; - const char *id = NULL; - if (dev->id) { - id = g_strdup(dev->id); - } - - qapi_event_send_memory_device_size_change(!!id, id, *size_p); + qapi_event_send_memory_device_size_change(!!dev->id, dev->id, *size_p); } static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) From d89dd28f0e29c9eae997b0cd645208454a2f3374 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 29 Sep 2021 18:24:44 +0200 Subject: [PATCH 0209/1334] qapi: Include qom-path in MEMORY_DEVICE_SIZE_CHANGE qapi events As we might not always have a device id, it is impossible to always match MEMORY_DEVICE_SIZE_CHANGE events to an actual device. Let's include the qom-path in the event, which allows for reliable mapping of events to devices. Fixes: 722a3c783ef4 ("virtio-pci: Send qapi events when the virtio-mem size changes") Suggested-by: Markus Armbruster Reviewed-by: Markus Armbruster Signed-off-by: David Hildenbrand Message-Id: <20210929162445.64060-3-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/virtio/virtio-mem-pci.c | 5 ++++- qapi/machine.json | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index 7e384b7397..be2383b0c5 100644 --- a/hw/virtio/virtio-mem-pci.c +++ b/hw/virtio/virtio-mem-pci.c @@ -87,9 +87,12 @@ static void virtio_mem_pci_size_change_notify(Notifier *notifier, void *data) VirtIOMEMPCI *pci_mem = container_of(notifier, VirtIOMEMPCI, size_change_notifier); DeviceState *dev = DEVICE(pci_mem); + char *qom_path = object_get_canonical_path(OBJECT(dev)); const uint64_t * const size_p = data; - qapi_event_send_memory_device_size_change(!!dev->id, dev->id, *size_p); + qapi_event_send_memory_device_size_change(!!dev->id, dev->id, *size_p, + qom_path); + g_free(qom_path); } static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) diff --git a/qapi/machine.json b/qapi/machine.json index 0e91a57a76..5db54df298 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1336,8 +1336,11 @@ # action). # # @id: device's ID +# # @size: the new size of memory that the device provides # +# @qom-path: path to the device object in the QOM tree (since 6.2) +# # Note: this event is rate-limited. # # Since: 5.1 @@ -1350,7 +1353,7 @@ # ## { 'event': 'MEMORY_DEVICE_SIZE_CHANGE', - 'data': { '*id': 'str', 'size': 'size' } } + 'data': { '*id': 'str', 'size': 'size', 'qom-path' : 'str'} } ## From 77ae2302ae167aa840d5a3aa489f7958db7c1426 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 29 Sep 2021 18:24:45 +0200 Subject: [PATCH 0210/1334] monitor: Rate-limit MEMORY_DEVICE_SIZE_CHANGE qapi events per device We want to rate-limit MEMORY_DEVICE_SIZE_CHANGE events per device, otherwise we can lose some events for devices. We can now use the qom-path to reliably map an event to a device and make rate-limiting device-aware. This was noticed by starting a VM with two virtio-mem devices that each have a requested size > 0. The Linux guest will initialize both devices in parallel, resulting in losing MEMORY_DEVICE_SIZE_CHANGE events for one of the devices. Fixes: 722a3c783ef4 ("virtio-pci: Send qapi events when the virtio-mem size changes") Suggested-by: Markus Armbruster Reviewed-by: Markus Armbruster Signed-off-by: David Hildenbrand Message-Id: <20210929162445.64060-4-david@redhat.com> Signed-off-by: Paolo Bonzini --- monitor/monitor.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/monitor/monitor.c b/monitor/monitor.c index 46a171bca6..21c7a68758 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -474,6 +474,10 @@ static unsigned int qapi_event_throttle_hash(const void *key) hash += g_str_hash(qdict_get_str(evstate->data, "node-name")); } + if (evstate->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE) { + hash += g_str_hash(qdict_get_str(evstate->data, "qom-path")); + } + return hash; } @@ -496,6 +500,11 @@ static gboolean qapi_event_throttle_equal(const void *a, const void *b) qdict_get_str(evb->data, "node-name")); } + if (eva->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE) { + return !strcmp(qdict_get_str(eva->data, "qom-path"), + qdict_get_str(evb->data, "qom-path")); + } + return TRUE; } From 45e576c74533c70b38ba00f0c298dcdbc1635163 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 27 Jul 2021 10:25:42 +0200 Subject: [PATCH 0211/1334] tpm: mark correct memory region range dirty when clearing RAM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We might not start at the beginning of the memory region. Let's calculate the offset into the memory region via the difference in the host addresses. Acked-by: Stefan Berger Fixes: ffab1be70692 ("tpm: clear RAM when "memory overwrite" requested") Cc: Marc-André Lureau Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Eduardo Habkost Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Claudio Fontana Cc: Thomas Huth Cc: "Alex Bennée" Cc: Peter Xu Cc: Laurent Vivier Cc: Stefan Berger Signed-off-by: David Hildenbrand Reviewed-by: Peter Xu Message-Id: <20210727082545.17934-2-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/tpm/tpm_ppi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c index 362edcc5c9..274e9aa4b0 100644 --- a/hw/tpm/tpm_ppi.c +++ b/hw/tpm/tpm_ppi.c @@ -30,11 +30,14 @@ void tpm_ppi_reset(TPMPPI *tpmppi) guest_phys_blocks_init(&guest_phys_blocks); guest_phys_blocks_append(&guest_phys_blocks); QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) { + hwaddr mr_offs = block->host_addr - + (uint8_t *)memory_region_get_ram_ptr(block->mr); + trace_tpm_ppi_memset(block->host_addr, block->target_end - block->target_start); memset(block->host_addr, 0, block->target_end - block->target_start); - memory_region_set_dirty(block->mr, 0, + memory_region_set_dirty(block->mr, mr_offs, block->target_end - block->target_start); } guest_phys_blocks_free(&guest_phys_blocks); From 602f8ea79ce39b7bd6d2e22c686ef05227e1876b Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 27 Jul 2021 10:25:43 +0200 Subject: [PATCH 0212/1334] softmmu/memory_mapping: never merge ranges accross memory regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's make sure to not merge when different memory regions are involved. Unlikely, but theoretically possible. Acked-by: Stefan Berger Reviewed-by: Peter Xu Cc: Marc-André Lureau Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Eduardo Habkost Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Claudio Fontana Cc: Thomas Huth Cc: "Alex Bennée" Cc: Peter Xu Cc: Laurent Vivier Cc: Stefan Berger Signed-off-by: David Hildenbrand Message-Id: <20210727082545.17934-3-david@redhat.com> Signed-off-by: Paolo Bonzini --- softmmu/memory_mapping.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/softmmu/memory_mapping.c b/softmmu/memory_mapping.c index e7af276546..d401ca7e31 100644 --- a/softmmu/memory_mapping.c +++ b/softmmu/memory_mapping.c @@ -229,7 +229,8 @@ static void guest_phys_blocks_region_add(MemoryListener *listener, /* we want continuity in both guest-physical and host-virtual memory */ if (predecessor->target_end < target_start || - predecessor->host_addr + predecessor_size != host_addr) { + predecessor->host_addr + predecessor_size != host_addr || + predecessor->mr != section->mr) { predecessor = NULL; } } From 3513bb1be1f025e011a69bafd02b6f59fa1d8383 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 27 Jul 2021 10:25:44 +0200 Subject: [PATCH 0213/1334] softmmu/memory_mapping: factor out adding physical memory ranges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's factor out adding a MemoryRegionSection to the list, to be reused in RamDiscardManager context next. Reviewed-by: Stefan Berger Reviewed-by: Peter Xu Cc: Marc-André Lureau Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Eduardo Habkost Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Claudio Fontana Cc: Thomas Huth Cc: "Alex Bennée" Cc: Peter Xu Cc: Laurent Vivier Cc: Stefan Berger Signed-off-by: David Hildenbrand Message-Id: <20210727082545.17934-4-david@redhat.com> Signed-off-by: Paolo Bonzini --- softmmu/memory_mapping.c | 41 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/softmmu/memory_mapping.c b/softmmu/memory_mapping.c index d401ca7e31..a2af02c41c 100644 --- a/softmmu/memory_mapping.c +++ b/softmmu/memory_mapping.c @@ -193,29 +193,14 @@ typedef struct GuestPhysListener { MemoryListener listener; } GuestPhysListener; -static void guest_phys_blocks_region_add(MemoryListener *listener, +static void guest_phys_block_add_section(GuestPhysListener *g, MemoryRegionSection *section) { - GuestPhysListener *g; - uint64_t section_size; - hwaddr target_start, target_end; - uint8_t *host_addr; - GuestPhysBlock *predecessor; - - /* we only care about RAM */ - if (!memory_region_is_ram(section->mr) || - memory_region_is_ram_device(section->mr) || - memory_region_is_nonvolatile(section->mr)) { - return; - } - - g = container_of(listener, GuestPhysListener, listener); - section_size = int128_get64(section->size); - target_start = section->offset_within_address_space; - target_end = target_start + section_size; - host_addr = memory_region_get_ram_ptr(section->mr) + - section->offset_within_region; - predecessor = NULL; + const hwaddr target_start = section->offset_within_address_space; + const hwaddr target_end = target_start + int128_get64(section->size); + uint8_t *host_addr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region; + GuestPhysBlock *predecessor = NULL; /* find continuity in guest physical address space */ if (!QTAILQ_EMPTY(&g->list->head)) { @@ -261,6 +246,20 @@ static void guest_phys_blocks_region_add(MemoryListener *listener, #endif } +static void guest_phys_blocks_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + GuestPhysListener *g = container_of(listener, GuestPhysListener, listener); + + /* we only care about RAM */ + if (!memory_region_is_ram(section->mr) || + memory_region_is_ram_device(section->mr) || + memory_region_is_nonvolatile(section->mr)) { + return; + } + guest_phys_block_add_section(g, section); +} + void guest_phys_blocks_append(GuestPhysBlockList *list) { GuestPhysListener g = { 0 }; From cb83ba8c1ab856b4327e7e869c410bbfd4152c2c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 27 Jul 2021 10:25:45 +0200 Subject: [PATCH 0214/1334] softmmu/memory_mapping: optimize for RamDiscardManager sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtio-mem logically plugs/unplugs memory within a sparse memory region and notifies via the RamDiscardManager interface when parts become plugged (populated) or unplugged (discarded). Currently, we end up (via the two users) 1) zeroing all logically unplugged/discarded memory during TPM resets. 2) reading all logically unplugged/discarded memory when dumping, to figure out the content is zero. 1) is always bad, because we assume unplugged memory stays discarded (and is already implicitly zero). 2) isn't that bad with anonymous memory, we end up reading the zero page (slow and unnecessary, though). However, once we use some file-backed memory (future use case), even reading will populate memory. Let's cut out all parts marked as not-populated (discarded) via the RamDiscardManager. As virtio-mem is the single user, this now means that logically unplugged memory ranges will no longer be included in the dump, which results in smaller dump files and faster dumping. virtio-mem has a minimum granularity of 1 MiB (and the default is usually 2 MiB). Theoretically, we can see quite some fragmentation, in practice we won't have it completely fragmented in 1 MiB pieces. Still, we might end up with many physical ranges. Both, the ELF format and kdump seem to be ready to support many individual ranges (e.g., for ELF it seems to be UINT32_MAX, kdump has a linear bitmap). Reviewed-by: Peter Xu Cc: Marc-André Lureau Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Eduardo Habkost Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Claudio Fontana Cc: Thomas Huth Cc: "Alex Bennée" Cc: Peter Xu Cc: Laurent Vivier Cc: Stefan Berger Signed-off-by: David Hildenbrand Message-Id: <20210727082545.17934-5-david@redhat.com> Signed-off-by: Paolo Bonzini --- softmmu/memory_mapping.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/softmmu/memory_mapping.c b/softmmu/memory_mapping.c index a2af02c41c..a62eaa49cc 100644 --- a/softmmu/memory_mapping.c +++ b/softmmu/memory_mapping.c @@ -246,6 +246,15 @@ static void guest_phys_block_add_section(GuestPhysListener *g, #endif } +static int guest_phys_ram_populate_cb(MemoryRegionSection *section, + void *opaque) +{ + GuestPhysListener *g = opaque; + + guest_phys_block_add_section(g, section); + return 0; +} + static void guest_phys_blocks_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -257,6 +266,17 @@ static void guest_phys_blocks_region_add(MemoryListener *listener, memory_region_is_nonvolatile(section->mr)) { return; } + + /* for special sparse regions, only add populated parts */ + if (memory_region_has_ram_discard_manager(section->mr)) { + RamDiscardManager *rdm; + + rdm = memory_region_get_ram_discard_manager(section->mr); + ram_discard_manager_replay_populated(rdm, section, + guest_phys_ram_populate_cb, g); + return; + } + guest_phys_block_add_section(g, section); } From ba858d1fadec417ab95c29938d2a0bb967d86114 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 28 Sep 2021 09:14:49 +0200 Subject: [PATCH 0215/1334] qemu-options: -chardev reconnect=seconds duplicated in help, tidy up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 5dd1f02b4bc2f2c2ef3a2adfd8a412c8c8769085 Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Message-Id: <20210928071449.1416022-1-armbru@redhat.com> Signed-off-by: Laurent Vivier --- qemu-options.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-options.hx b/qemu-options.hx index 8ef178180d..4f2dc91e0b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3202,7 +3202,7 @@ DEFHEADING(Character device options:) DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev help\n" "-chardev null,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" - "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4=on|off][,ipv6=on|off][,nodelay=on|off][,reconnect=seconds]\n" + "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4=on|off][,ipv6=on|off][,nodelay=on|off]\n" " [,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds][,mux=on|off]\n" " [,logfile=PATH][,logappend=on|off][,tls-creds=ID][,tls-authz=ID] (tcp)\n" "-chardev socket,id=id,path=path[,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds]\n" From 553dc36b387d9c99740f5ae825465ffdd93685f2 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 28 Sep 2021 20:11:33 +0800 Subject: [PATCH 0216/1334] qemu-options: Tweak [, maxcpus=cpus] to [, maxcpus=maxcpus] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In qemu-option.hx, there is "-smp [[cpus=]n][,maxcpus=cpus]..." in the DEF part, and "-smp [[cpus=]n][,maxcpus=maxcpus]..." in the RST part. Obviously the later is right, let's fix the previous one. Signed-off-by: Yanan Wang Reviewed-by: Damien Hedde Reviewed-by: Daniel P. Berrangé Reviewed-by: Andrew Jones Message-Id: <20210928121134.21064-2-wangyanan55@huawei.com> Signed-off-by: Laurent Vivier --- qemu-options.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-options.hx b/qemu-options.hx index 4f2dc91e0b..bba1ef973f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -206,7 +206,7 @@ SRST ERST DEF("smp", HAS_ARG, QEMU_OPTION_smp, - "-smp [[cpus=]n][,maxcpus=cpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]\n" + "-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]\n" " set the number of CPUs to 'n' [default=1]\n" " maxcpus= maximum number of total CPUs, including\n" " offline CPUs for hotplug, etc\n" From 848dd26928e2f4ceb63d7de06dd0b5f5b55bbddd Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 28 Sep 2021 20:11:34 +0800 Subject: [PATCH 0217/1334] qemu-options: Add missing "sockets=2, maxcpus=2" to CLI "-smp 2" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is one numa config example in qemu-options.hx currently using "-smp 2" and assuming that there will be 2 sockets and 2 cpus totally. However now the actual calculation logic of missing sockets and cores is not immutable and is considered liable to change. Although we will get maxcpus=2 finally based on current parser, it's always stable to specify it explicitly. So "-smp 2,sockets=2,maxcpus=2" will be optimal when we expect multiple sockets and 2 cpus totally. Signed-off-by: Yanan Wang Reviewed-by: Philippe Mathieu-Daude Reviewed-by: Daniel P. Berrangé Reviewed-by: Andrew Jones Message-Id: <20210928121134.21064-3-wangyanan55@huawei.com> Signed-off-by: Laurent Vivier --- qemu-options.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-options.hx b/qemu-options.hx index bba1ef973f..5f375bbfa6 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -401,7 +401,7 @@ SRST -m 2G \ -object memory-backend-ram,size=1G,id=m0 \ -object memory-backend-ram,size=1G,id=m1 \ - -smp 2 \ + -smp 2,sockets=2,maxcpus=2 \ -numa node,nodeid=0,memdev=m0 \ -numa node,nodeid=1,memdev=m1,initiator=0 \ -numa cpu,node-id=0,socket-id=0 \ From 196fb7ac7cb83f3b5547435f3b1d8b5bfaca35fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Sep 2021 09:03:16 -0400 Subject: [PATCH 0218/1334] target/sh4: Use lookup_symbol in sh4_tr_disas_log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct thing to do has been present but commented out since the initial commit of the sh4 translator. Fixes: fdf9b3e831e Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210929130316.121330-1-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/sh4/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index cf5fe9243d..d363050272 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -2344,7 +2344,7 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) static void sh4_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) { - qemu_log("IN:\n"); /* , lookup_symbol(dcbase->pc_first)); */ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); } From daf0db06308b55c518312abc39a4bf74413ac007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Sep 2021 22:19:26 +0200 Subject: [PATCH 0219/1334] hw/remote/proxy: Categorize Wireless devices as 'Network' ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU doesn't distinct network devices per link layer (Ethernet, Wi-Fi, CAN, ...). Categorize PCI Wireless cards as Network devices. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Jagannathan Raman Message-Id: <20210926201926.1690896-1-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- hw/remote/proxy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index 499f540c94..bad164299d 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -324,6 +324,7 @@ static void probe_pci_info(PCIDevice *dev, Error **errp) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); break; case PCI_BASE_CLASS_NETWORK: + case PCI_BASE_CLASS_WIRELESS: set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); break; case PCI_BASE_CLASS_INPUT: From 7f7c8d0ce3630849a4df3d627b11de354fcb3bb0 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Mon, 4 Oct 2021 16:04:45 +0200 Subject: [PATCH 0220/1334] i386: docs: Briefly describe KVM PV features KVM PV features don't seem to be documented anywhere, in particular, the fact that some of the features are enabled by default and some are not can only be figured out from the code. Signed-off-by: Vitaly Kuznetsov Message-Id: <20211004140445.624875-1-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- docs/system/i386/kvm-pv.rst | 100 ++++++++++++++++++++++++++++++++++++ docs/system/target-i386.rst | 1 + 2 files changed, 101 insertions(+) create mode 100644 docs/system/i386/kvm-pv.rst diff --git a/docs/system/i386/kvm-pv.rst b/docs/system/i386/kvm-pv.rst new file mode 100644 index 0000000000..1e5a9923ef --- /dev/null +++ b/docs/system/i386/kvm-pv.rst @@ -0,0 +1,100 @@ +Paravirtualized KVM features +============================ + +Description +----------- + +In some cases when implementing hardware interfaces in software is slow, ``KVM`` +implements its own paravirtualized interfaces. + +Setup +----- + +Paravirtualized ``KVM`` features are represented as CPU flags. The following +features are enabled by default for any CPU model when ``KVM`` acceleration is +enabled: + +- ``kvmclock`` +- ``kvm-nopiodelay`` +- ``kvm-asyncpf`` +- ``kvm-steal-time`` +- ``kvm-pv-eoi`` +- ``kvmclock-stable-bit`` + +``kvm-msi-ext-dest-id`` feature is enabled by default in x2apic mode with split +irqchip (e.g. "-machine ...,kernel-irqchip=split -cpu ...,x2apic"). + +Note: when CPU model ``host`` is used, QEMU passes through all supported +paravirtualized ``KVM`` features to the guest. + +Existing features +----------------- + +``kvmclock`` + Expose a ``KVM`` specific paravirtualized clocksource to the guest. Supported + since Linux v2.6.26. + +``kvm-nopiodelay`` + The guest doesn't need to perform delays on PIO operations. Supported since + Linux v2.6.26. + +``kvm-mmu`` + This feature is deprecated. + +``kvm-asyncpf`` + Enable asynchronous page fault mechanism. Supported since Linux v2.6.38. + Note: since Linux v5.10 the feature is deprecated and not enabled by ``KVM``. + Use ``kvm-asyncpf-int`` instead. + +``kvm-steal-time`` + Enable stolen (when guest vCPU is not running) time accounting. Supported + since Linux v3.1. + +``kvm-pv-eoi`` + Enable paravirtualized end-of-interrupt signaling. Supported since Linux + v3.10. + +``kvm-pv-unhalt`` + Enable paravirtualized spinlocks support. Supported since Linux v3.12. + +``kvm-pv-tlb-flush`` + Enable paravirtualized TLB flush mechanism. Supported since Linux v4.16. + +``kvm-pv-ipi`` + Enable paravirtualized IPI mechanism. Supported since Linux v4.19. + +``kvm-poll-control`` + Enable host-side polling on HLT control from the guest. Supported since Linux + v5.10. + +``kvm-pv-sched-yield`` + Enable paravirtualized sched yield feature. Supported since Linux v5.10. + +``kvm-asyncpf-int`` + Enable interrupt based asynchronous page fault mechanism. Supported since Linux + v5.10. + +``kvm-msi-ext-dest-id`` + Support 'Extended Destination ID' for external interrupts. The feature allows + to use up to 32768 CPUs without IRQ remapping (but other limits may apply making + the number of supported vCPUs for a given configuration lower). Supported since + Linux v5.10. + +``kvmclock-stable-bit`` + Tell the guest that guest visible TSC value can be fully trusted for kvmclock + computations and no warps are expected. Supported since Linux v2.6.35. + +Supplementary features +---------------------- + +``kvm-pv-enforce-cpuid`` + Limit the supported paravirtualized feature set to the exposed features only. + Note, by default, ``KVM`` allows the guest to use all currently supported + paravirtualized features even when they were not announced in guest visible + CPUIDs. Supported since Linux v5.10. + + +Useful links +------------ + +Please refer to Documentation/virt/kvm in Linux for additional details. diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst index 6a86d63863..4daa53c35d 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -26,6 +26,7 @@ Architectural features :maxdepth: 1 i386/cpu + i386/kvm-pv i386/sgx .. _pcsys_005freq: From cff03145ed3cec5c7bd542ea2e6b4458439e0bb0 Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Thu, 30 Sep 2021 08:49:14 +0300 Subject: [PATCH 0221/1334] sev/i386: Introduce sev_add_kernel_loader_hashes for measured linux boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the sev_add_kernel_loader_hashes function to calculate the hashes of the kernel/initrd/cmdline and fill a designated OVMF encrypted hash table area. For this to work, OVMF must support an encrypted area to place the data which is advertised via a special GUID in the OVMF reset table. The hashes of each of the files is calculated (or the string in the case of the cmdline with trailing '\0' included). Each entry in the hashes table is GUID identified and since they're passed through the sev_encrypt_flash interface, the hashes will be accumulated by the AMD PSP measurement (SEV_LAUNCH_MEASURE). Co-developed-by: James Bottomley Signed-off-by: James Bottomley Signed-off-by: Dov Murik Reviewed-by: Daniel P. Berrangé Message-Id: <20210930054915.13252-2-dovmurik@linux.ibm.com> Signed-off-by: Paolo Bonzini --- target/i386/sev-stub.c | 5 ++ target/i386/sev.c | 137 +++++++++++++++++++++++++++++++++++++++++ target/i386/sev_i386.h | 12 ++++ 3 files changed, 154 insertions(+) diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c index 0227cb5177..d8e6583171 100644 --- a/target/i386/sev-stub.c +++ b/target/i386/sev-stub.c @@ -81,3 +81,8 @@ sev_get_attestation_report(const char *mnonce, Error **errp) error_setg(errp, "SEV is not available in this QEMU"); return NULL; } + +bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) +{ + g_assert_not_reached(); +} diff --git a/target/i386/sev.c b/target/i386/sev.c index fa7210473a..bcd9260fa4 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -23,6 +23,7 @@ #include "qemu/base64.h" #include "qemu/module.h" #include "qemu/uuid.h" +#include "crypto/hash.h" #include "sysemu/kvm.h" #include "sev_i386.h" #include "sysemu/sysemu.h" @@ -83,6 +84,32 @@ typedef struct __attribute__((__packed__)) SevInfoBlock { uint32_t reset_addr; } SevInfoBlock; +#define SEV_HASH_TABLE_RV_GUID "7255371f-3a3b-4b04-927b-1da6efa8d454" +typedef struct QEMU_PACKED SevHashTableDescriptor { + /* SEV hash table area guest address */ + uint32_t base; + /* SEV hash table area size (in bytes) */ + uint32_t size; +} SevHashTableDescriptor; + +/* hard code sha256 digest size */ +#define HASH_SIZE 32 + +typedef struct QEMU_PACKED SevHashTableEntry { + QemuUUID guid; + uint16_t len; + uint8_t hash[HASH_SIZE]; +} SevHashTableEntry; + +typedef struct QEMU_PACKED SevHashTable { + QemuUUID guid; + uint16_t len; + SevHashTableEntry cmdline; + SevHashTableEntry initrd; + SevHashTableEntry kernel; + uint8_t padding[]; +} SevHashTable; + static SevGuestState *sev_guest; static Error *sev_mig_blocker; @@ -1071,6 +1098,116 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) return 0; } +static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +}; + +static const QemuUUID sev_kernel_entry_guid = { + .data = UUID_LE(0x4de79437, 0xabd2, 0x427f, 0xb8, 0x35, 0xd5, 0xb1, + 0x72, 0xd2, 0x04, 0x5b) +}; +static const QemuUUID sev_initrd_entry_guid = { + .data = UUID_LE(0x44baf731, 0x3a2f, 0x4bd7, 0x9a, 0xf1, 0x41, 0xe2, + 0x91, 0x69, 0x78, 0x1d) +}; +static const QemuUUID sev_cmdline_entry_guid = { + .data = UUID_LE(0x97d02dd8, 0xbd20, 0x4c94, 0xaa, 0x78, 0xe7, 0x71, + 0x4d, 0x36, 0xab, 0x2a) +}; + +/* + * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page + * which is included in SEV's initial memory measurement. + */ +bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) +{ + uint8_t *data; + SevHashTableDescriptor *area; + SevHashTable *ht; + uint8_t cmdline_hash[HASH_SIZE]; + uint8_t initrd_hash[HASH_SIZE]; + uint8_t kernel_hash[HASH_SIZE]; + uint8_t *hashp; + size_t hash_len = HASH_SIZE; + int aligned_len; + + if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { + error_setg(errp, "SEV: kernel specified but OVMF has no hash table guid"); + return false; + } + area = (SevHashTableDescriptor *)data; + + /* + * Calculate hash of kernel command-line with the terminating null byte. If + * the user doesn't supply a command-line via -append, the 1-byte "\0" will + * be used. + */ + hashp = cmdline_hash; + if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->cmdline_data, + ctx->cmdline_size, &hashp, &hash_len, errp) < 0) { + return false; + } + assert(hash_len == HASH_SIZE); + + /* + * Calculate hash of initrd. If the user doesn't supply an initrd via + * -initrd, an empty buffer will be used (ctx->initrd_size == 0). + */ + hashp = initrd_hash; + if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->initrd_data, + ctx->initrd_size, &hashp, &hash_len, errp) < 0) { + return false; + } + assert(hash_len == HASH_SIZE); + + /* Calculate hash of the kernel */ + hashp = kernel_hash; + struct iovec iov[2] = { + { .iov_base = ctx->setup_data, .iov_len = ctx->setup_size }, + { .iov_base = ctx->kernel_data, .iov_len = ctx->kernel_size } + }; + if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, iov, ARRAY_SIZE(iov), + &hashp, &hash_len, errp) < 0) { + return false; + } + assert(hash_len == HASH_SIZE); + + /* + * Populate the hashes table in the guest's memory at the OVMF-designated + * area for the SEV hashes table + */ + ht = qemu_map_ram_ptr(NULL, area->base); + + ht->guid = sev_hash_table_header_guid; + ht->len = sizeof(*ht); + + ht->cmdline.guid = sev_cmdline_entry_guid; + ht->cmdline.len = sizeof(ht->cmdline); + memcpy(ht->cmdline.hash, cmdline_hash, sizeof(ht->cmdline.hash)); + + ht->initrd.guid = sev_initrd_entry_guid; + ht->initrd.len = sizeof(ht->initrd); + memcpy(ht->initrd.hash, initrd_hash, sizeof(ht->initrd.hash)); + + ht->kernel.guid = sev_kernel_entry_guid; + ht->kernel.len = sizeof(ht->kernel); + memcpy(ht->kernel.hash, kernel_hash, sizeof(ht->kernel.hash)); + + /* When calling sev_encrypt_flash, the length has to be 16 byte aligned */ + aligned_len = ROUND_UP(ht->len, 16); + if (aligned_len != ht->len) { + /* zero the excess data so the measurement can be reliably calculated */ + memset(ht->padding, 0, aligned_len - ht->len); + } + + if (sev_encrypt_flash((uint8_t *)ht, aligned_len, errp) < 0) { + return false; + } + + return true; +} + static void sev_register_types(void) { diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h index ae6d840478..2afe108069 100644 --- a/target/i386/sev_i386.h +++ b/target/i386/sev_i386.h @@ -28,6 +28,17 @@ #define SEV_POLICY_DOMAIN 0x10 #define SEV_POLICY_SEV 0x20 +typedef struct SevKernelLoaderContext { + char *setup_data; + size_t setup_size; + char *kernel_data; + size_t kernel_size; + char *initrd_data; + size_t initrd_size; + char *cmdline_data; + size_t cmdline_size; +} SevKernelLoaderContext; + extern bool sev_es_enabled(void); extern uint64_t sev_get_me_mask(void); extern SevInfo *sev_get_info(void); @@ -37,5 +48,6 @@ extern char *sev_get_launch_measurement(void); extern SevCapability *sev_get_capabilities(Error **errp); extern SevAttestationReport * sev_get_attestation_report(const char *mnonce, Error **errp); +extern bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); #endif From c0c2d319d6714517cc4e0332edf99f2fccaa9442 Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Thu, 30 Sep 2021 08:49:15 +0300 Subject: [PATCH 0222/1334] x86/sev: generate SEV kernel loader hashes in x86_load_linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If SEV is enabled and a kernel is passed via -kernel, pass the hashes of kernel/initrd/cmdline in an encrypted guest page to OVMF for SEV measured boot. Co-developed-by: James Bottomley Signed-off-by: James Bottomley Signed-off-by: Dov Murik Reviewed-by: Daniel P. Berrangé Message-Id: <20210930054915.13252-3-dovmurik@linux.ibm.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 41ef9a84a9..0c7c054e3a 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -47,6 +47,7 @@ #include "hw/i386/fw_cfg.h" #include "hw/intc/i8259.h" #include "hw/rtc/mc146818rtc.h" +#include "target/i386/sev_i386.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/irq.h" @@ -780,6 +781,7 @@ void x86_load_linux(X86MachineState *x86ms, const char *initrd_filename = machine->initrd_filename; const char *dtb_filename = machine->dtb; const char *kernel_cmdline = machine->kernel_cmdline; + SevKernelLoaderContext sev_load_ctx = {}; /* Align to 16 bytes as a paranoia measure */ cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; @@ -926,6 +928,8 @@ void x86_load_linux(X86MachineState *x86ms, fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + sev_load_ctx.cmdline_data = (char *)kernel_cmdline; + sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1; if (protocol >= 0x202) { stl_p(header + 0x228, cmdline_addr); @@ -1007,6 +1011,8 @@ void x86_load_linux(X86MachineState *x86ms, fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); + sev_load_ctx.initrd_data = initrd_data; + sev_load_ctx.initrd_size = initrd_size; stl_p(header + 0x218, initrd_addr); stl_p(header + 0x21c, initrd_size); @@ -1065,15 +1071,32 @@ void x86_load_linux(X86MachineState *x86ms, load_image_size(dtb_filename, setup_data->data, dtb_size); } - memcpy(setup, header, MIN(sizeof(header), setup_size)); + /* + * If we're starting an encrypted VM, it will be OVMF based, which uses the + * efi stub for booting and doesn't require any values to be placed in the + * kernel header. We therefore don't update the header so the hash of the + * kernel on the other side of the fw_cfg interface matches the hash of the + * file the user passed in. + */ + if (!sev_enabled()) { + memcpy(setup, header, MIN(sizeof(header), setup_size)); + } fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); + sev_load_ctx.kernel_data = (char *)kernel; + sev_load_ctx.kernel_size = kernel_size; fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); + sev_load_ctx.setup_data = (char *)setup; + sev_load_ctx.setup_size = setup_size; + + if (sev_enabled()) { + sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); + } option_rom[nb_option_roms].bootindex = 0; option_rom[nb_option_roms].name = "linuxboot.bin"; From 93ddefbc3c909bb6c3b76086f1dfc8ad98dd3725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20To=CC=82rres?= Date: Mon, 4 Oct 2021 10:46:37 +0200 Subject: [PATCH 0223/1334] hw/misc: applesmc: use host osk as default on macs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running on a Mac, QEMU is able to get the host OSK and use it as the default value for the AppleSMC device. The OSK query operation doesn't require administrator privileges and can be executed by any user on the system. This patch is based on open-source code from Apple, just like the implementation from VirtualBox. Apple: https://opensource.apple.com/source/IOKitUser/IOKitUser-647.6.13/pwr_mgt.subproj/IOPMLibPrivate.c https://opensource.apple.com/source/PowerManagement/PowerManagement-637.60.1/pmconfigd/PrivateLib.c VirtualBox: https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Devices/EFI/DevSmc.cpp#L516 Signed-off-by: Pedro Tôrres --- hw/misc/applesmc.c | 192 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 1 deletion(-) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 1b9acaf1d3..cec247b5ee 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -38,6 +38,171 @@ #include "qemu/timer.h" #include "qom/object.h" +#if defined(__APPLE__) && defined(__MACH__) +#include + +enum { + kSMCSuccess = 0x00, + kSMCKeyNotFound = 0x84 +}; + +enum { + kSMCUserClientOpen = 0x00, + kSMCUserClientClose = 0x01, + kSMCHandleYPCEvent = 0x02, + kSMCReadKey = 0x05, + kSMCGetKeyInfo = 0x09 +}; + +typedef struct SMCVersion { + uint8_t major; + uint8_t minor; + uint8_t build; + uint8_t reserved; + uint16_t release; +} SMCVersion; + +typedef struct SMCPLimitData { + uint16_t version; + uint16_t length; + uint32_t cpuPLimit; + uint32_t gpuPLimit; + uint32_t memPLimit; +} SMCPLimitData; + +typedef struct SMCKeyInfoData { + IOByteCount dataSize; + uint32_t dataType; + uint8_t dataAttributes; +} SMCKeyInfoData; + +typedef struct { + uint32_t key; + SMCVersion vers; + SMCPLimitData pLimitData; + SMCKeyInfoData keyInfo; + uint8_t result; + uint8_t status; + uint8_t data8; + uint32_t data32; + uint8_t bytes[32]; +} SMCParamStruct; + +static IOReturn smc_call_struct_method(uint32_t selector, + SMCParamStruct *inputStruct, + SMCParamStruct *outputStruct) +{ + IOReturn ret; + + size_t inputStructCnt = sizeof(SMCParamStruct); + size_t outputStructCnt = sizeof(SMCParamStruct); + + io_service_t smcService = IO_OBJECT_NULL; + io_connect_t smcConnect = IO_OBJECT_NULL; + + smcService = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching("AppleSMC")); + if (smcService == IO_OBJECT_NULL) { + ret = kIOReturnNotFound; + goto exit; + } + + ret = IOServiceOpen(smcService, mach_task_self(), 1, &smcConnect); + if (ret != kIOReturnSuccess) { + smcConnect = IO_OBJECT_NULL; + goto exit; + } + if (smcConnect == IO_OBJECT_NULL) { + ret = kIOReturnError; + goto exit; + } + + ret = IOConnectCallMethod(smcConnect, kSMCUserClientOpen, + NULL, 0, NULL, 0, + NULL, NULL, NULL, NULL); + if (ret != kIOReturnSuccess) { + goto exit; + } + + ret = IOConnectCallStructMethod(smcConnect, selector, + inputStruct, inputStructCnt, + outputStruct, &outputStructCnt); + +exit: + if (smcConnect != IO_OBJECT_NULL) { + IOConnectCallMethod(smcConnect, kSMCUserClientClose, + NULL, 0, NULL, 0, NULL, + NULL, NULL, NULL); + IOServiceClose(smcConnect); + } + + return ret; +} + +static IOReturn smc_read_key(uint32_t key, + uint8_t *bytes, + IOByteCount *dataSize) +{ + IOReturn ret; + + SMCParamStruct inputStruct; + SMCParamStruct outputStruct; + + if (key == 0 || bytes == NULL) { + ret = kIOReturnCannotWire; + goto exit; + } + + /* determine key's data size */ + memset(&inputStruct, 0, sizeof(SMCParamStruct)); + inputStruct.data8 = kSMCGetKeyInfo; + inputStruct.key = key; + + memset(&outputStruct, 0, sizeof(SMCParamStruct)); + ret = smc_call_struct_method(kSMCHandleYPCEvent, &inputStruct, &outputStruct); + if (ret != kIOReturnSuccess) { + goto exit; + } + if (outputStruct.result == kSMCKeyNotFound) { + ret = kIOReturnNotFound; + goto exit; + } + if (outputStruct.result != kSMCSuccess) { + ret = kIOReturnInternalError; + goto exit; + } + + /* get key value */ + memset(&inputStruct, 0, sizeof(SMCParamStruct)); + inputStruct.data8 = kSMCReadKey; + inputStruct.key = key; + inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; + + memset(&outputStruct, 0, sizeof(SMCParamStruct)); + ret = smc_call_struct_method(kSMCHandleYPCEvent, &inputStruct, &outputStruct); + if (ret != kIOReturnSuccess) { + goto exit; + } + if (outputStruct.result == kSMCKeyNotFound) { + ret = kIOReturnNotFound; + goto exit; + } + if (outputStruct.result != kSMCSuccess) { + ret = kIOReturnInternalError; + goto exit; + } + + memset(bytes, 0, *dataSize); + if (*dataSize > inputStruct.keyInfo.dataSize) { + *dataSize = inputStruct.keyInfo.dataSize; + } + memcpy(bytes, outputStruct.bytes, *dataSize); + +exit: + return ret; +} +#endif + /* #define DEBUG_SMC */ #define APPLESMC_DEFAULT_IOBASE 0x300 @@ -315,6 +480,7 @@ static const MemoryRegionOps applesmc_err_io_ops = { static void applesmc_isa_realize(DeviceState *dev, Error **errp) { AppleSMCState *s = APPLE_SMC(dev); + bool valid_key = false; memory_region_init_io(&s->io_data, OBJECT(s), &applesmc_data_io_ops, s, "applesmc-data", 1); @@ -331,7 +497,31 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp) isa_register_ioport(&s->parent_obj, &s->io_err, s->iobase + APPLESMC_ERR_PORT); - if (!s->osk || (strlen(s->osk) != 64)) { + if (s->osk) { + valid_key = strlen(s->osk) == 64; + } else { +#if defined(__APPLE__) && defined(__MACH__) + IOReturn ret; + IOByteCount size = 32; + + ret = smc_read_key('OSK0', (uint8_t *) default_osk, &size); + if (ret != kIOReturnSuccess) { + goto failure; + } + + ret = smc_read_key('OSK1', (uint8_t *) default_osk + size, &size); + if (ret != kIOReturnSuccess) { + goto failure; + } + + warn_report("Using AppleSMC with host key"); + valid_key = true; + s->osk = default_osk; +failure:; +#endif + } + + if (!valid_key) { warn_report("Using AppleSMC with invalid key"); s->osk = default_osk; } From 4dba27890844146d69e84916d024697947821655 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 Sep 2021 17:14:43 +0200 Subject: [PATCH 0224/1334] configure, meson: move CPU_CFLAGS out of QEMU_CFLAGS Flags that choose the target architecture, such as -m32 on x86, affect all invocations of the compiler driver, for example including options such as --print-search-dirs. To ensure that they are treated as such, place them in the cross file in the [binaries] section instead of including them in QEMU_CFLAGS. Signed-off-by: Paolo Bonzini --- configure | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/configure b/configure index b0b1a1cc25..1d3f099498 100755 --- a/configure +++ b/configure @@ -142,11 +142,11 @@ lines: ${BASH_LINENO[*]}" } do_cc() { - do_compiler "$cc" "$@" + do_compiler "$cc" $CPU_CFLAGS "$@" } do_cxx() { - do_compiler "$cxx" "$@" + do_compiler "$cxx" $CPU_CFLAGS "$@" } # Append $2 to the variable named $1, with space separation @@ -1688,7 +1688,6 @@ esac eval "cross_cc_${cpu}=\$cc" cross_cc_vars="$cross_cc_vars cross_cc_${cpu}" -QEMU_CFLAGS="$CPU_CFLAGS $QEMU_CFLAGS" # For user-mode emulation the host arch has to be one we explicitly # support, even if we're using TCI. @@ -5114,9 +5113,9 @@ if test "$skip_meson" = no; then echo "c_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross echo "cpp_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross echo "[binaries]" >> $cross - echo "c = [$(meson_quote $cc)]" >> $cross - test -n "$cxx" && echo "cpp = [$(meson_quote $cxx)]" >> $cross - test -n "$objcc" && echo "objc = [$(meson_quote $objcc)]" >> $cross + echo "c = [$(meson_quote $cc $CPU_CFLAGS)]" >> $cross + test -n "$cxx" && echo "cpp = [$(meson_quote $cxx $CPU_CFLAGS)]" >> $cross + test -n "$objcc" && echo "objc = [$(meson_quote $objcc $CPU_CFLAGS)]" >> $cross echo "ar = [$(meson_quote $ar)]" >> $cross echo "nm = [$(meson_quote $nm)]" >> $cross echo "pkgconfig = [$(meson_quote $pkg_config_exe)]" >> $cross From 3c158eba1e11e52dbd0e14fc8fd40ec80abca436 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 5 Oct 2021 04:07:50 -0400 Subject: [PATCH 0225/1334] migration: block-dirty-bitmap: add missing qemu_mutex_lock_iothread init_dirty_bitmap_migration assumes the iothread lock (BQL) to be held, but instead it isn't. Instead of adding the lock to qemu_savevm_state_setup(), follow the same pattern as the other ->save_setup callbacks and lock+unlock inside dirty_bitmap_save_setup(). Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Stefan Hajnoczi Message-Id: <20211005080751.3797161-2-eesposit@redhat.com> Signed-off-by: Paolo Bonzini --- migration/block-dirty-bitmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 35f5ef688d..9aba7d9c22 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -1215,7 +1215,10 @@ static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque) { DBMSaveState *s = &((DBMState *)opaque)->save; SaveBitmapState *dbms = NULL; + + qemu_mutex_lock_iothread(); if (init_dirty_bitmap_migration(s) < 0) { + qemu_mutex_unlock_iothread(); return -1; } @@ -1223,7 +1226,7 @@ static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque) send_bitmap_start(f, s, dbms); } qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS); - + qemu_mutex_unlock_iothread(); return 0; } From 68b88468f6a4144bf0217624fbb0ff6b0fa1d694 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 5 Oct 2021 04:07:51 -0400 Subject: [PATCH 0226/1334] migration: add missing qemu_mutex_lock_iothread in migration_completion qemu_savevm_state_complete_postcopy assumes the iothread lock (BQL) to be held, but instead it isn't. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Dr. David Alan Gilbert Message-Id: <20211005080751.3797161-3-eesposit@redhat.com> Signed-off-by: Paolo Bonzini --- migration/migration.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index bb909781b7..6ac807ef3d 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3168,7 +3168,10 @@ static void migration_completion(MigrationState *s) } else if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { trace_migration_completion_postcopy_end(); + qemu_mutex_lock_iothread(); qemu_savevm_state_complete_postcopy(s->to_dst_file); + qemu_mutex_unlock_iothread(); + trace_migration_completion_postcopy_end_after_complete(); } else if (s->state == MIGRATION_STATUS_CANCELLING) { goto fail; From 69c4c5c1c47f5dac140eb6485c5281a9f145dcf3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Mar 2021 14:50:00 +0100 Subject: [PATCH 0227/1334] meson: bump submodule to 0.59.2 The update to 0.57 has been delayed due to it causing warnings for some actual issues, but it brings in important bugfixes and new features. 0.58 also brings in a bugfix that is useful for modinfo. Important bugfixes: - 0.57: https://github.com/mesonbuild/meson/pull/7760, build: use PIE objects for non-PIC static libraries if b_pie=true - 0.57: https://github.com/mesonbuild/meson/pull/7900, thus avoiding unnecessary rebuilds after running meson. - 0.58.2: https://github.com/mesonbuild/meson/pull/8900, fixes for passing extract_objects() to custom_target (useful for modinfo) Features: - 0.57: the keyval module has now been stabilized - 0.57: env argument to custom_target (useful for hexagon) - 0.57: Feature parity between "meson test" and QEMU's TAP driver - 0.57: https://github.com/mesonbuild/meson/pull/8231, allows bringing back version numbers in the configuration summary - 0.59: Utility methods for feature objects Signed-off-by: Paolo Bonzini --- meson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson b/meson index 776acd2a80..b25d94e7c7 160000 --- a/meson +++ b/meson @@ -1 +1 @@ -Subproject commit 776acd2a805c9b42b4f0375150977df42130317f +Subproject commit b25d94e7c77fda05a7fdfe8afe562cf9760d69da From 654d6b0453aa6eb19af0d75b0f087a97a5776da7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Feb 2021 14:59:26 +0100 Subject: [PATCH 0228/1334] meson: switch minimum meson version to 0.58.2, minimum recommended to 0.59.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Meson 0.58.2 does not need b_staticpic=$pie anymore, and has stabilized the keyval module. Remove the workaround and use a few replacements for features deprecated in the 0.57.0 release cycle. One feature that we would like to use is passing dependencies to summary. However, that was broken in 0.59.0 and 0.59.1. Therefore, use the embedded Meson if the host has anything older than 0.59.2, but allow --meson= to use 0.58.2. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- configure | 8 ++---- docs/meson.build | 14 ++++----- meson.build | 54 ++++++++++++++++------------------- plugins/meson.build | 4 +-- scripts/mtest2make.py | 7 ++--- tests/qapi-schema/meson.build | 4 +-- tests/qtest/meson.build | 2 +- tests/unit/meson.build | 2 +- trace/meson.build | 4 +-- 9 files changed, 44 insertions(+), 55 deletions(-) diff --git a/configure b/configure index 1d3f099498..877bf3d76a 100755 --- a/configure +++ b/configure @@ -1994,7 +1994,7 @@ python_version=$($python -c 'import sys; print("%d.%d.%d" % (sys.version_info[0] python="$python -B" if test -z "$meson"; then - if test "$explicit_python" = no && has meson && version_ge "$(meson --version)" 0.55.3; then + if test "$explicit_python" = no && has meson && version_ge "$(meson --version)" 0.59.2; then meson=meson elif test $git_submodules_action != 'ignore' ; then meson=git @@ -5163,10 +5163,6 @@ if test "$skip_meson" = no; then mv $cross config-meson.cross rm -rf meson-private meson-info meson-logs - unset staticpic - if ! version_ge "$($meson --version)" 0.56.0; then - staticpic=$(if test "$pie" = yes; then echo true; else echo false; fi) - fi NINJA=$ninja $meson setup \ --prefix "$prefix" \ --libdir "$libdir" \ @@ -5186,7 +5182,6 @@ if test "$skip_meson" = no; then -Dwerror=$(if test "$werror" = yes; then echo true; else echo false; fi) \ -Dstrip=$(if test "$strip_opt" = yes; then echo true; else echo false; fi) \ -Db_pie=$(if test "$pie" = yes; then echo true; else echo false; fi) \ - ${staticpic:+-Db_staticpic=$staticpic} \ -Db_coverage=$(if test "$gcov" = yes; then echo true; else echo false; fi) \ -Db_lto=$lto -Dcfi=$cfi -Dcfi_debug=$cfi_debug \ -Dmalloc=$malloc -Dmalloc_trim=$malloc_trim -Dsparse=$sparse \ @@ -5222,6 +5217,7 @@ else perl -i -ne ' s/^gettext = true$/gettext = auto/; s/^gettext = false$/gettext = disabled/; + /^b_staticpic/ && next; print;' meson-private/cmd_line.txt fi fi diff --git a/docs/meson.build b/docs/meson.build index cffe1ecf1d..be4dc30f39 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -37,14 +37,14 @@ endif if build_docs SPHINX_ARGS += ['-Dversion=' + meson.project_version(), '-Drelease=' + config_host['PKGVERSION']] - sphinx_extn_depends = [ meson.source_root() / 'docs/sphinx/depfile.py', - meson.source_root() / 'docs/sphinx/hxtool.py', - meson.source_root() / 'docs/sphinx/kerneldoc.py', - meson.source_root() / 'docs/sphinx/kernellog.py', - meson.source_root() / 'docs/sphinx/qapidoc.py', - meson.source_root() / 'docs/sphinx/qmp_lexer.py', + sphinx_extn_depends = [ meson.current_source_dir() / 'sphinx/depfile.py', + meson.current_source_dir() / 'sphinx/hxtool.py', + meson.current_source_dir() / 'sphinx/kerneldoc.py', + meson.current_source_dir() / 'sphinx/kernellog.py', + meson.current_source_dir() / 'sphinx/qapidoc.py', + meson.current_source_dir() / 'sphinx/qmp_lexer.py', qapi_gen_depends ] - sphinx_template_files = [ meson.source_root() / 'docs/_templates/footer.html' ] + sphinx_template_files = [ meson.project_source_root() / 'docs/_templates/footer.html' ] have_ga = have_tools and config_host.has_key('CONFIG_GUEST_AGENT') diff --git a/meson.build b/meson.build index 60f4f45165..17e77fe4ef 100644 --- a/meson.build +++ b/meson.build @@ -1,14 +1,10 @@ -project('qemu', ['c'], meson_version: '>=0.55.0', - default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto'] + - (meson.version().version_compare('>=0.56.0') ? [ 'b_staticpic=false' ] : []), - version: run_command('head', meson.source_root() / 'VERSION').stdout().strip()) +project('qemu', ['c'], meson_version: '>=0.58.2', + default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto', + 'b_staticpic=false'], + version: files('VERSION')) not_found = dependency('', required: false) -if meson.version().version_compare('>=0.56.0') - keyval = import('keyval') -else - keyval = import('unstable-keyval') -endif +keyval = import('keyval') ss = import('sourceset') fs = import('fs') @@ -1972,21 +1968,21 @@ genh += configure_file(output: 'config-host.h', configuration: config_host_data) hxtool = find_program('scripts/hxtool') shaderinclude = find_program('scripts/shaderinclude.pl') qapi_gen = find_program('scripts/qapi-gen.py') -qapi_gen_depends = [ meson.source_root() / 'scripts/qapi/__init__.py', - meson.source_root() / 'scripts/qapi/commands.py', - meson.source_root() / 'scripts/qapi/common.py', - meson.source_root() / 'scripts/qapi/error.py', - meson.source_root() / 'scripts/qapi/events.py', - meson.source_root() / 'scripts/qapi/expr.py', - meson.source_root() / 'scripts/qapi/gen.py', - meson.source_root() / 'scripts/qapi/introspect.py', - meson.source_root() / 'scripts/qapi/parser.py', - meson.source_root() / 'scripts/qapi/schema.py', - meson.source_root() / 'scripts/qapi/source.py', - meson.source_root() / 'scripts/qapi/types.py', - meson.source_root() / 'scripts/qapi/visit.py', - meson.source_root() / 'scripts/qapi/common.py', - meson.source_root() / 'scripts/qapi-gen.py' +qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.py', + meson.current_source_dir() / 'scripts/qapi/commands.py', + meson.current_source_dir() / 'scripts/qapi/common.py', + meson.current_source_dir() / 'scripts/qapi/error.py', + meson.current_source_dir() / 'scripts/qapi/events.py', + meson.current_source_dir() / 'scripts/qapi/expr.py', + meson.current_source_dir() / 'scripts/qapi/gen.py', + meson.current_source_dir() / 'scripts/qapi/introspect.py', + meson.current_source_dir() / 'scripts/qapi/parser.py', + meson.current_source_dir() / 'scripts/qapi/schema.py', + meson.current_source_dir() / 'scripts/qapi/source.py', + meson.current_source_dir() / 'scripts/qapi/types.py', + meson.current_source_dir() / 'scripts/qapi/visit.py', + meson.current_source_dir() / 'scripts/qapi/common.py', + meson.current_source_dir() / 'scripts/qapi-gen.py' ] tracetool = [ @@ -2635,14 +2631,14 @@ foreach target : target_dirs if target.endswith('-softmmu') execs = [{ 'name': 'qemu-system-' + target_name, - 'gui': false, + 'win_subsystem': 'console', 'sources': files('softmmu/main.c'), 'dependencies': [] }] if targetos == 'windows' and (sdl.found() or gtk.found()) execs += [{ 'name': 'qemu-system-' + target_name + 'w', - 'gui': true, + 'win_subsystem': 'windows', 'sources': files('softmmu/main.c'), 'dependencies': [] }] @@ -2651,7 +2647,7 @@ foreach target : target_dirs specific_fuzz = specific_fuzz_ss.apply(config_target, strict: false) execs += [{ 'name': 'qemu-fuzz-' + target_name, - 'gui': false, + 'win_subsystem': 'console', 'sources': specific_fuzz.sources(), 'dependencies': specific_fuzz.dependencies(), }] @@ -2659,7 +2655,7 @@ foreach target : target_dirs else execs = [{ 'name': 'qemu-' + target_name, - 'gui': false, + 'win_subsystem': 'console', 'sources': [], 'dependencies': [] }] @@ -2678,7 +2674,7 @@ foreach target : target_dirs link_language: link_language, link_depends: [block_syms, qemu_syms] + exe.get('link_depends', []), link_args: link_args, - gui_app: exe['gui']) + win_subsystem: exe['win_subsystem']) if targetos == 'darwin' icon = 'pc-bios/qemu.rsrc' diff --git a/plugins/meson.build b/plugins/meson.build index bfd5c9822a..aeb386ebae 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -2,9 +2,9 @@ plugin_ldflags = [] # Modules need more symbols than just those in plugins/qemu-plugins.symbols if not enable_modules if 'CONFIG_HAS_LD_DYNAMIC_LIST' in config_host - plugin_ldflags = ['-Wl,--dynamic-list=' + (meson.build_root() / 'qemu-plugins-ld.symbols')] + plugin_ldflags = ['-Wl,--dynamic-list=' + (meson.project_build_root() / 'qemu-plugins-ld.symbols')] elif 'CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST' in config_host - plugin_ldflags = ['-Wl,-exported_symbols_list,' + (meson.build_root() / 'qemu-plugins-ld64.symbols')] + plugin_ldflags = ['-Wl,-exported_symbols_list,' + (meson.project_build_root() / 'qemu-plugins-ld64.symbols')] endif endif diff --git a/scripts/mtest2make.py b/scripts/mtest2make.py index ee072c0502..02c0453e67 100644 --- a/scripts/mtest2make.py +++ b/scripts/mtest2make.py @@ -60,11 +60,8 @@ def process_tests(test, targets, suites): if test['workdir'] is not None: print('.test.dir.%d := %s' % (i, shlex.quote(test['workdir']))) - if 'depends' in test: - deps = (targets.get(x, []) for x in test['depends']) - deps = itertools.chain.from_iterable(deps) - else: - deps = ['all'] + deps = (targets.get(x, []) for x in test['depends']) + deps = itertools.chain.from_iterable(deps) print('.test.name.%d := %s' % (i, test['name'])) print('.test.driver.%d := %s' % (i, driver)) diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index 6187efbd58..df5acfd08b 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -1,5 +1,5 @@ test_env = environment() -test_env.set('PYTHONPATH', meson.source_root() / 'scripts') +test_env.set('PYTHONPATH', meson.project_source_root() / 'scripts') test_env.set('PYTHONIOENCODING', 'utf-8') schemas = [ @@ -248,7 +248,7 @@ if build_docs # clutter up the build dir with the cache. command: [SPHINX_ARGS, '-b', 'text', '-E', - '-c', meson.source_root() / 'docs', + '-c', meson.project_source_root() / 'docs', '-D', 'master_doc=doc-good', meson.current_source_dir(), meson.current_build_dir()]) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 19444d4752..c9d8458062 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -275,7 +275,7 @@ foreach dir : target_dirs qtest_env.set('QTEST_QEMU_IMG', './qemu-img') test_deps += [qemu_img] endif - qtest_env.set('G_TEST_DBUS_DAEMON', meson.source_root() / 'tests/dbus-vmstate-daemon.sh') + qtest_env.set('G_TEST_DBUS_DAEMON', meson.project_source_root() / 'tests/dbus-vmstate-daemon.sh') qtest_env.set('QTEST_QEMU_BINARY', './qemu-system-' + target_base) if have_tools and have_vhost_user_blk_server qtest_env.set('QTEST_QEMU_STORAGE_DAEMON_BINARY', './storage-daemon/qemu-storage-daemon') diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 5736d285b2..7c297d7e5c 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -43,7 +43,7 @@ tests = { 'test-keyval': [testqapi], 'test-logging': [], 'test-uuid': [], - 'ptimer-test': ['ptimer-test-stubs.c', meson.source_root() / 'hw/core/ptimer.c'], + 'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'], 'test-qapi-util': [], } diff --git a/trace/meson.build b/trace/meson.build index e401e7c415..b8f95de200 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -4,7 +4,7 @@ specific_ss.add(files('control-target.c')) trace_events_files = [] dtrace = find_program('dtrace', required: 'CONFIG_TRACE_DTRACE' in config_host) foreach dir : [ '.' ] + trace_events_subdirs - trace_events_file = meson.source_root() / dir / 'trace-events' + trace_events_file = meson.project_source_root() / dir / 'trace-events' trace_events_files += [ trace_events_file ] group_name = dir == '.' ? 'root' : dir.underscorify() group = '--group=' + group_name @@ -70,7 +70,7 @@ foreach d : [ ] gen = custom_target(d[0], output: d[0], - input: meson.source_root() / 'trace-events', + input: meson.project_source_root() / 'trace-events', command: [ tracetool, '--group=root', '--format=@0@'.format(d[1]), '@INPUT@', '@OUTPUT@' ], depend_files: tracetool_depends) specific_ss.add(when: 'CONFIG_TCG', if_true: gen) From 0a11c44e891221a584bf06e75871dd1425555a65 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Mar 2021 18:55:57 +0100 Subject: [PATCH 0229/1334] hexagon: use env keyword argument to pass PYTHONPATH This feature is new in meson 0.57 and allows getting rid of the "env" wrapper. Signed-off-by: Paolo Bonzini --- target/hexagon/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index 6fd9360b74..c6d858ffb2 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -156,7 +156,8 @@ dectree_generated = custom_target( 'dectree_generated.h.inc', output: 'dectree_generated.h.inc', depends: [iset_py], - command: ['env', 'PYTHONPATH=' + meson.current_build_dir(), files('dectree.py'), '@OUTPUT@'], + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files('dectree.py'), '@OUTPUT@'], ) hexagon_ss.add(dectree_generated) From 2796032a51c635464481a9212e941da53b3b3a61 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 May 2021 12:43:24 +0200 Subject: [PATCH 0230/1334] target/xtensa: list cores in a text file Avoid that leftover files affect the build; instead, use the same mechanism that was in place before the Meson transition of updating a file from import_core.sh. Starting with Meson 0.57, the file can be easily read from the filesystem module, so do that instead of using run_command. Signed-off-by: Paolo Bonzini --- target/xtensa/cores.list | 9 +++++++++ target/xtensa/import_core.sh | 3 +++ target/xtensa/meson.build | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 target/xtensa/cores.list diff --git a/target/xtensa/cores.list b/target/xtensa/cores.list new file mode 100644 index 0000000000..5772a00ab2 --- /dev/null +++ b/target/xtensa/cores.list @@ -0,0 +1,9 @@ +core-dc232b.c +core-dc233c.c +core-de212.c +core-de233_fpu.c +core-dsp3400.c +core-fsf.c +core-sample_controller.c +core-test_kc705_be.c +core-test_mmuhifi_c3.c diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh index 396b264be9..df66d09393 100755 --- a/target/xtensa/import_core.sh +++ b/target/xtensa/import_core.sh @@ -66,3 +66,6 @@ static XtensaConfig $NAME __attribute__((unused)) = { REGISTER_CORE($NAME) EOF + +grep -qxf core-${NAME}.c "$BASE"/cores.list || \ + echo core-${NAME}.c >> "$BASE"/cores.list diff --git a/target/xtensa/meson.build b/target/xtensa/meson.build index 7c4efa6c62..20bbf9b335 100644 --- a/target/xtensa/meson.build +++ b/target/xtensa/meson.build @@ -1,7 +1,7 @@ xtensa_ss = ss.source_set() -xtensa_cores = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT/$MESON_SUBDIR ; ls -1 core-*.c') -xtensa_ss.add(files(xtensa_cores.stdout().strip().split('\n'))) +xtensa_cores = fs.read('cores.list') +xtensa_ss.add(files(xtensa_cores.strip().split('\n'))) xtensa_ss.add(files( 'cpu.c', From bb647c49b8f1f986d8171dd61db65e8a8d255be0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 3 Jun 2021 11:24:56 +0200 Subject: [PATCH 0231/1334] meson: show library versions in the summary Meson 0.57 allows passing external programs and dependency objects to summary(). Use this to show library versions and paths in the summary. Signed-off-by: Paolo Bonzini --- meson.build | 112 +++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/meson.build b/meson.build index 17e77fe4ef..7b596fdcd9 100644 --- a/meson.build +++ b/meson.build @@ -2859,13 +2859,13 @@ summary_info = {} summary_info += {'git': config_host['GIT']} summary_info += {'make': config_host['MAKE']} summary_info += {'python': '@0@ (version: @1@)'.format(python.full_path(), python.language_version())} -summary_info += {'sphinx-build': sphinx_build.found()} +summary_info += {'sphinx-build': sphinx_build} if config_host.has_key('HAVE_GDB_BIN') summary_info += {'gdb': config_host['HAVE_GDB_BIN']} endif summary_info += {'genisoimage': config_host['GENISOIMAGE']} if targetos == 'windows' and config_host.has_key('CONFIG_GUEST_AGENT') - summary_info += {'wixl': wixl.found() ? wixl.full_path() : false} + summary_info += {'wixl': wixl} endif if slirp_opt != 'disabled' and 'CONFIG_SLIRP_SMBD' in config_host summary_info += {'smbd': config_host['CONFIG_SMBD_COMMAND']} @@ -2956,7 +2956,7 @@ if get_option('cfi') summary_info += {'CFI debug support': get_option('cfi_debug')} endif summary_info += {'strip binaries': get_option('strip')} -summary_info += {'sparse': sparse.found() ? sparse.full_path() : false} +summary_info += {'sparse': sparse} summary_info += {'mingw32 support': targetos == 'windows'} # snarf the cross-compilation information for tests @@ -3028,19 +3028,19 @@ if have_block summary_info += {'vvfat support': config_host.has_key('CONFIG_VVFAT')} summary_info += {'qed support': config_host.has_key('CONFIG_QED')} summary_info += {'parallels support': config_host.has_key('CONFIG_PARALLELS')} - summary_info += {'FUSE exports': fuse.found()} + summary_info += {'FUSE exports': fuse} endif summary(summary_info, bool_yn: true, section: 'Block layer support') # Crypto summary_info = {} summary_info += {'TLS priority': config_host['CONFIG_TLS_PRIORITY']} -summary_info += {'GNUTLS support': gnutls.found()} -summary_info += {'GNUTLS crypto': gnutls_crypto.found()} -# TODO: add back version -summary_info += {'libgcrypt': gcrypt.found()} -# TODO: add back version -summary_info += {'nettle': nettle.found()} +summary_info += {'GNUTLS support': gnutls} +if gnutls.found() + summary_info += {' GNUTLS crypto': gnutls_crypto.found()} +endif +summary_info += {'libgcrypt': gcrypt} +summary_info += {'nettle': nettle} if nettle.found() summary_info += {' XTS': xts != 'private'} endif @@ -3052,76 +3052,72 @@ summary(summary_info, bool_yn: true, section: 'Crypto') # Libraries summary_info = {} if targetos == 'darwin' - summary_info += {'Cocoa support': cocoa.found()} + summary_info += {'Cocoa support': cocoa} endif -# TODO: add back version -summary_info += {'SDL support': sdl.found()} -summary_info += {'SDL image support': sdl_image.found()} -# TODO: add back version -summary_info += {'GTK support': gtk.found()} -summary_info += {'pixman': pixman.found()} -# TODO: add back version -summary_info += {'VTE support': vte.found()} -# TODO: add back version -summary_info += {'slirp support': slirp_opt == 'disabled' ? false : slirp_opt} -summary_info += {'libtasn1': tasn1.found()} -summary_info += {'PAM': pam.found()} -summary_info += {'iconv support': iconv.found()} -summary_info += {'curses support': curses.found()} -# TODO: add back version -summary_info += {'virgl support': virgl.found()} -summary_info += {'curl support': curl.found()} -summary_info += {'Multipath support': mpathpersist.found()} -summary_info += {'VNC support': vnc.found()} +summary_info += {'SDL support': sdl} +summary_info += {'SDL image support': sdl_image} +summary_info += {'GTK support': gtk} +summary_info += {'pixman': pixman} +summary_info += {'VTE support': vte} +summary_info += {'slirp support': slirp_opt == 'internal' ? slirp_opt : slirp} +summary_info += {'libtasn1': tasn1} +summary_info += {'PAM': pam} +summary_info += {'iconv support': iconv} +summary_info += {'curses support': curses} +summary_info += {'virgl support': virgl} +summary_info += {'curl support': curl} +summary_info += {'Multipath support': mpathpersist} +summary_info += {'VNC support': vnc} if vnc.found() - summary_info += {'VNC SASL support': sasl.found()} - summary_info += {'VNC JPEG support': jpeg.found()} - summary_info += {'VNC PNG support': png.found()} + summary_info += {'VNC SASL support': sasl} + summary_info += {'VNC JPEG support': jpeg} + summary_info += {'VNC PNG support': png} endif -summary_info += {'brlapi support': brlapi.found()} +summary_info += {'brlapi support': brlapi} summary_info += {'vde support': config_host.has_key('CONFIG_VDE')} summary_info += {'netmap support': config_host.has_key('CONFIG_NETMAP')} summary_info += {'Linux AIO support': config_host.has_key('CONFIG_LINUX_AIO')} -summary_info += {'Linux io_uring support': linux_io_uring.found()} -summary_info += {'ATTR/XATTR support': libattr.found()} +summary_info += {'Linux io_uring support': linux_io_uring} +summary_info += {'ATTR/XATTR support': libattr} summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')} summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')} summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} -summary_info += {'libcap-ng support': libcap_ng.found()} -summary_info += {'bpf support': libbpf.found()} +summary_info += {'libcap-ng support': libcap_ng} +summary_info += {'bpf support': libbpf} # TODO: add back protocol and server version summary_info += {'spice support': config_host.has_key('CONFIG_SPICE')} -summary_info += {'rbd support': rbd.found()} +summary_info += {'rbd support': rbd} summary_info += {'xfsctl support': config_host.has_key('CONFIG_XFS')} -summary_info += {'smartcard support': cacard.found()} -summary_info += {'U2F support': u2f.found()} -summary_info += {'libusb': libusb.found()} -summary_info += {'usb net redir': usbredir.found()} +summary_info += {'smartcard support': cacard} +summary_info += {'U2F support': u2f} +summary_info += {'libusb': libusb} +summary_info += {'usb net redir': usbredir} summary_info += {'OpenGL support': config_host.has_key('CONFIG_OPENGL')} -summary_info += {'GBM': gbm.found()} -summary_info += {'libiscsi support': libiscsi.found()} -summary_info += {'libnfs support': libnfs.found()} +summary_info += {'GBM': gbm} +summary_info += {'libiscsi support': libiscsi} +summary_info += {'libnfs support': libnfs} if targetos == 'windows' if config_host.has_key('CONFIG_GUEST_AGENT') summary_info += {'QGA VSS support': config_host.has_key('CONFIG_QGA_VSS')} summary_info += {'QGA w32 disk info': config_host.has_key('CONFIG_QGA_NTDDSCSI')} endif endif -summary_info += {'seccomp support': seccomp.found()} -summary_info += {'GlusterFS support': glusterfs.found()} +summary_info += {'seccomp support': seccomp} +summary_info += {'GlusterFS support': glusterfs} summary_info += {'TPM support': config_host.has_key('CONFIG_TPM')} summary_info += {'libssh support': config_host.has_key('CONFIG_LIBSSH')} -summary_info += {'lzo support': lzo.found()} -summary_info += {'snappy support': snappy.found()} -summary_info += {'bzip2 support': libbzip2.found()} -summary_info += {'lzfse support': liblzfse.found()} -summary_info += {'zstd support': zstd.found()} +summary_info += {'lzo support': lzo} +summary_info += {'snappy support': snappy} +summary_info += {'bzip2 support': libbzip2} +summary_info += {'lzfse support': liblzfse} +summary_info += {'zstd support': zstd} summary_info += {'NUMA host support': config_host.has_key('CONFIG_NUMA')} -summary_info += {'libxml2': libxml2.found()} -summary_info += {'capstone': capstone_opt == 'disabled' ? false : capstone_opt} -summary_info += {'libpmem support': libpmem.found()} -summary_info += {'libdaxctl support': libdaxctl.found()} -summary_info += {'libudev': libudev.found()} +summary_info += {'libxml2': libxml2} +summary_info += {'capstone': capstone_opt == 'internal' ? capstone_opt : capstone} +summary_info += {'libpmem support': libpmem} +summary_info += {'libdaxctl support': libdaxctl} +summary_info += {'libudev': libudev} +# Dummy dependency, keep .found() summary_info += {'FUSE lseek': fuse_lseek.found()} summary(summary_info, bool_yn: true, section: 'Dependencies') From ab4dd2746c234f038206b3ccfe6bec1f19f98c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 6 Sep 2021 12:43:17 +0200 Subject: [PATCH 0232/1334] hw/virtio: Acquire RCU read lock in virtqueue_packed_drop_all() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vring_get_region_caches() must be called with the RCU read lock acquired. virtqueue_packed_drop_all() does not, and uses the 'caches' pointer. Fix that by using the RCU_READ_LOCK_GUARD() macro. Reported-by: Stefano Garzarella Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210906104318.1569967-3-philmd@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella --- hw/virtio/virtio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 240759ff0b..dd0ab433b8 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1703,6 +1703,8 @@ static unsigned int virtqueue_packed_drop_all(VirtQueue *vq) VirtIODevice *vdev = vq->vdev; VRingPackedDesc desc; + RCU_READ_LOCK_GUARD(); + caches = vring_get_region_caches(vq); if (!caches) { return 0; From d6ed27bae717ceac9de0c53a31389143846b8465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 6 Sep 2021 12:43:18 +0200 Subject: [PATCH 0233/1334] hw/virtio: Have virtqueue_get_avail_bytes() pass caches arg to callees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both virtqueue_packed_get_avail_bytes() and virtqueue_split_get_avail_bytes() access the region cache, but their caller also does. Simplify by having virtqueue_get_avail_bytes calling both with RCU lock held, and passing the caches as argument. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210906104318.1569967-4-philmd@redhat.com> Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella --- hw/virtio/virtio.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index dd0ab433b8..cc69a9b881 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -984,28 +984,23 @@ static int virtqueue_split_read_next_desc(VirtIODevice *vdev, VRingDesc *desc, return VIRTQUEUE_READ_DESC_MORE; } +/* Called within rcu_read_lock(). */ static void virtqueue_split_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, unsigned int *out_bytes, - unsigned max_in_bytes, unsigned max_out_bytes) + unsigned max_in_bytes, unsigned max_out_bytes, + VRingMemoryRegionCaches *caches) { VirtIODevice *vdev = vq->vdev; unsigned int max, idx; unsigned int total_bufs, in_total, out_total; - VRingMemoryRegionCaches *caches; MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; int64_t len = 0; int rc; - RCU_READ_LOCK_GUARD(); - idx = vq->last_avail_idx; total_bufs = in_total = out_total = 0; max = vq->vring.num; - caches = vring_get_region_caches(vq); - if (!caches) { - goto err; - } while ((rc = virtqueue_num_heads(vq, idx)) > 0) { MemoryRegionCache *desc_cache = &caches->desc; @@ -1124,32 +1119,28 @@ static int virtqueue_packed_read_next_desc(VirtQueue *vq, return VIRTQUEUE_READ_DESC_MORE; } +/* Called within rcu_read_lock(). */ static void virtqueue_packed_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, unsigned int *out_bytes, unsigned max_in_bytes, - unsigned max_out_bytes) + unsigned max_out_bytes, + VRingMemoryRegionCaches *caches) { VirtIODevice *vdev = vq->vdev; unsigned int max, idx; unsigned int total_bufs, in_total, out_total; MemoryRegionCache *desc_cache; - VRingMemoryRegionCaches *caches; MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; int64_t len = 0; VRingPackedDesc desc; bool wrap_counter; - RCU_READ_LOCK_GUARD(); idx = vq->last_avail_idx; wrap_counter = vq->last_avail_wrap_counter; total_bufs = in_total = out_total = 0; max = vq->vring.num; - caches = vring_get_region_caches(vq); - if (!caches) { - goto err; - } for (;;) { unsigned int num_bufs = total_bufs; @@ -1250,6 +1241,8 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, uint16_t desc_size; VRingMemoryRegionCaches *caches; + RCU_READ_LOCK_GUARD(); + if (unlikely(!vq->vring.desc)) { goto err; } @@ -1268,10 +1261,12 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { virtqueue_packed_get_avail_bytes(vq, in_bytes, out_bytes, - max_in_bytes, max_out_bytes); + max_in_bytes, max_out_bytes, + caches); } else { virtqueue_split_get_avail_bytes(vq, in_bytes, out_bytes, - max_in_bytes, max_out_bytes); + max_in_bytes, max_out_bytes, + caches); } return; From 8fc898ce0b3e7fea8c7c2a8d8977f2a9b77ecebf Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 5 Oct 2021 18:11:56 +0200 Subject: [PATCH 0234/1334] block/backup: avoid integer overflow of `max-workers` QAPI generates `struct BackupPerf` where `max-workers` value is stored in an `int64_t` variable. But block_copy_async(), and the underlying code, uses an `int` parameter. At the end that variable is used to initialize `max_busy_tasks` in block/aio_task.c causing the following assertion failure if a value greater than INT_MAX(2147483647) is used: ../block/aio_task.c:63: aio_task_pool_wait_one: Assertion `pool->busy_tasks > 0' failed. Let's check that `max-workers` doesn't exceed INT_MAX and print an error in that case. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2009310 Signed-off-by: Stefano Garzarella Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211005161157.282396-2-sgarzare@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/backup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/backup.c b/block/backup.c index 687d2882bc..8b072db5d9 100644 --- a/block/backup.c +++ b/block/backup.c @@ -407,8 +407,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } - if (perf->max_workers < 1) { - error_setg(errp, "max-workers must be greater than zero"); + if (perf->max_workers < 1 || perf->max_workers > INT_MAX) { + error_setg(errp, "max-workers must be between 1 and %d", INT_MAX); return NULL; } From a9515df4d66da34cbc2938e4c1f46015fc74ff93 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 5 Oct 2021 18:11:57 +0200 Subject: [PATCH 0235/1334] block/aio_task: assert `max_busy_tasks` is greater than 0 All code in block/aio_task.c expects `max_busy_tasks` to always be greater than 0. Assert this condition during the AioTaskPool creation where `max_busy_tasks` is set. Signed-off-by: Stefano Garzarella Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211005161157.282396-3-sgarzare@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/aio_task.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block/aio_task.c b/block/aio_task.c index 88989fa248..9bd17ea2c1 100644 --- a/block/aio_task.c +++ b/block/aio_task.c @@ -98,6 +98,8 @@ AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks) { AioTaskPool *pool = g_new0(AioTaskPool, 1); + assert(max_busy_tasks > 0); + pool->main_co = qemu_coroutine_self(); pool->max_busy_tasks = max_busy_tasks; From d6a9378f47515c6d70dbff4912c5740c98709880 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 21 Sep 2021 18:16:41 +0200 Subject: [PATCH 0236/1334] vhost-vsock: fix migration issue when seqpacket is supported Commit 1e08fd0a46 ("vhost-vsock: SOCK_SEQPACKET feature bit support") enabled the SEQPACKET feature bit. This commit is released with QEMU 6.1, so if we try to migrate a VM where the host kernel supports SEQPACKET but machine type version is less than 6.1, we get the following errors: Features 0x130000002 unsupported. Allowed features: 0x179000000 Failed to load virtio-vhost_vsock:virtio error while loading state for instance 0x0 of device '0000:00:05.0/virtio-vhost_vsock' load of migration failed: Operation not permitted Let's disable the feature bit for machine types < 6.1. We add a new OnOffAuto property for this, called `seqpacket`. When it is `auto` (default), QEMU behaves as before, trying to enable the feature, when it is `on` QEMU will fail if the backend (vhost-vsock kernel module) doesn't support it. Fixes: 1e08fd0a46 ("vhost-vsock: SOCK_SEQPACKET feature bit support") Cc: qemu-stable@nongnu.org Reported-by: Jiang Wang Signed-off-by: Stefano Garzarella Message-Id: <20210921161642.206461-2-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/machine.c | 1 + hw/virtio/vhost-vsock.c | 19 ++++++++++++++++--- include/hw/virtio/vhost-vsock.h | 3 +++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 3920a2f2af..74f2a9a984 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -46,6 +46,7 @@ GlobalProperty hw_compat_6_0[] = { { "nvme-ns", "eui64-default", "off"}, { "e1000", "init-vet", "off" }, { "e1000e", "init-vet", "off" }, + { "vhost-vsock-device", "seqpacket", "off" }, }; const size_t hw_compat_6_0_len = G_N_ELEMENTS(hw_compat_6_0); diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index 1b1a5c70ed..dade0da031 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -114,10 +114,21 @@ static uint64_t vhost_vsock_get_features(VirtIODevice *vdev, Error **errp) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); + VHostVSock *vsock = VHOST_VSOCK(vdev); - virtio_add_feature(&requested_features, VIRTIO_VSOCK_F_SEQPACKET); - return vhost_get_features(&vvc->vhost_dev, feature_bits, - requested_features); + if (vsock->seqpacket != ON_OFF_AUTO_OFF) { + virtio_add_feature(&requested_features, VIRTIO_VSOCK_F_SEQPACKET); + } + + requested_features = vhost_get_features(&vvc->vhost_dev, feature_bits, + requested_features); + + if (vsock->seqpacket == ON_OFF_AUTO_ON && + !virtio_has_feature(requested_features, VIRTIO_VSOCK_F_SEQPACKET)) { + error_setg(errp, "vhost-vsock backend doesn't support seqpacket"); + } + + return requested_features; } static const VMStateDescription vmstate_virtio_vhost_vsock = { @@ -218,6 +229,8 @@ static void vhost_vsock_device_unrealize(DeviceState *dev) static Property vhost_vsock_properties[] = { DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0), DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd), + DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSock, seqpacket, + ON_OFF_AUTO_AUTO), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/virtio/vhost-vsock.h b/include/hw/virtio/vhost-vsock.h index 84f4e727c7..3f121a624f 100644 --- a/include/hw/virtio/vhost-vsock.h +++ b/include/hw/virtio/vhost-vsock.h @@ -30,6 +30,9 @@ struct VHostVSock { VHostVSockCommon parent; VHostVSockConf conf; + /* features */ + OnOffAuto seqpacket; + /*< public >*/ }; From 46ce017167d800c5d96104a88cfaed9949c4a3b6 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 21 Sep 2021 18:16:42 +0200 Subject: [PATCH 0237/1334] vhost-vsock: handle common features in vhost-vsock-common virtio-vsock features, like VIRTIO_VSOCK_F_SEQPACKET, can be handled by vhost-vsock-common parent class. In this way, we can reuse the same code for all virtio-vsock backends (i.e. vhost-vsock, vhost-user-vsock). Let's move `seqpacket` property to vhost-vsock-common class, add vhost_vsock_common_get_features() used by children, and disable `seqpacket` for vhost-user-vsock device for machine types < 6.2. The behavior of vhost-vsock device doesn't change; vhost-user-vsock device now supports `seqpacket` property. Signed-off-by: Stefano Garzarella Message-Id: <20210921161642.206461-3-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/machine.c | 4 +++- hw/virtio/vhost-user-vsock.c | 4 +++- hw/virtio/vhost-vsock-common.c | 31 ++++++++++++++++++++++++++ hw/virtio/vhost-vsock.c | 24 +------------------- include/hw/virtio/vhost-vsock-common.h | 5 +++++ include/hw/virtio/vhost-vsock.h | 3 --- 6 files changed, 43 insertions(+), 28 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 74f2a9a984..b8d95eec32 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -37,7 +37,9 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-pci.h" -GlobalProperty hw_compat_6_1[] = {}; +GlobalProperty hw_compat_6_1[] = { + { "vhost-user-vsock-device", "seqpacket", "off" }, +}; const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1); GlobalProperty hw_compat_6_0[] = { diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index 6095ed7349..52bd682c34 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -81,7 +81,9 @@ static uint64_t vuv_get_features(VirtIODevice *vdev, { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); - return vhost_get_features(&vvc->vhost_dev, user_feature_bits, features); + features = vhost_get_features(&vvc->vhost_dev, user_feature_bits, features); + + return vhost_vsock_common_get_features(vdev, features, errp); } static const VMStateDescription vuv_vmstate = { diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index 4ad6e234ad..3f3771274e 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -18,6 +18,30 @@ #include "qemu/iov.h" #include "monitor/monitor.h" +const int feature_bits[] = { + VIRTIO_VSOCK_F_SEQPACKET, + VHOST_INVALID_FEATURE_BIT +}; + +uint64_t vhost_vsock_common_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); + + if (vvc->seqpacket != ON_OFF_AUTO_OFF) { + virtio_add_feature(&features, VIRTIO_VSOCK_F_SEQPACKET); + } + + features = vhost_get_features(&vvc->vhost_dev, feature_bits, features); + + if (vvc->seqpacket == ON_OFF_AUTO_ON && + !virtio_has_feature(features, VIRTIO_VSOCK_F_SEQPACKET)) { + error_setg(errp, "vhost-vsock backend doesn't support seqpacket"); + } + + return features; +} + int vhost_vsock_common_start(VirtIODevice *vdev) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); @@ -231,11 +255,18 @@ void vhost_vsock_common_unrealize(VirtIODevice *vdev) virtio_cleanup(vdev); } +static Property vhost_vsock_common_properties[] = { + DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSockCommon, seqpacket, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_END_OF_LIST(), +}; + static void vhost_vsock_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + device_class_set_props(dc, vhost_vsock_common_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->guest_notifier_mask = vhost_vsock_common_guest_notifier_mask; vdc->guest_notifier_pending = vhost_vsock_common_guest_notifier_pending; diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index dade0da031..478c0c9a87 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -21,11 +21,6 @@ #include "hw/virtio/vhost-vsock.h" #include "monitor/monitor.h" -const int feature_bits[] = { - VIRTIO_VSOCK_F_SEQPACKET, - VHOST_INVALID_FEATURE_BIT -}; - static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config) { VHostVSock *vsock = VHOST_VSOCK(vdev); @@ -113,22 +108,7 @@ static uint64_t vhost_vsock_get_features(VirtIODevice *vdev, uint64_t requested_features, Error **errp) { - VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); - VHostVSock *vsock = VHOST_VSOCK(vdev); - - if (vsock->seqpacket != ON_OFF_AUTO_OFF) { - virtio_add_feature(&requested_features, VIRTIO_VSOCK_F_SEQPACKET); - } - - requested_features = vhost_get_features(&vvc->vhost_dev, feature_bits, - requested_features); - - if (vsock->seqpacket == ON_OFF_AUTO_ON && - !virtio_has_feature(requested_features, VIRTIO_VSOCK_F_SEQPACKET)) { - error_setg(errp, "vhost-vsock backend doesn't support seqpacket"); - } - - return requested_features; + return vhost_vsock_common_get_features(vdev, requested_features, errp); } static const VMStateDescription vmstate_virtio_vhost_vsock = { @@ -229,8 +209,6 @@ static void vhost_vsock_device_unrealize(DeviceState *dev) static Property vhost_vsock_properties[] = { DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0), DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd), - DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSock, seqpacket, - ON_OFF_AUTO_AUTO), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/virtio/vhost-vsock-common.h b/include/hw/virtio/vhost-vsock-common.h index e412b5ee98..d8b565b4da 100644 --- a/include/hw/virtio/vhost-vsock-common.h +++ b/include/hw/virtio/vhost-vsock-common.h @@ -35,6 +35,9 @@ struct VHostVSockCommon { VirtQueue *trans_vq; QEMUTimer *post_load_timer; + + /* features */ + OnOffAuto seqpacket; }; int vhost_vsock_common_start(VirtIODevice *vdev); @@ -43,5 +46,7 @@ int vhost_vsock_common_pre_save(void *opaque); int vhost_vsock_common_post_load(void *opaque, int version_id); void vhost_vsock_common_realize(VirtIODevice *vdev, const char *name); void vhost_vsock_common_unrealize(VirtIODevice *vdev); +uint64_t vhost_vsock_common_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp); #endif /* _QEMU_VHOST_VSOCK_COMMON_H */ diff --git a/include/hw/virtio/vhost-vsock.h b/include/hw/virtio/vhost-vsock.h index 3f121a624f..84f4e727c7 100644 --- a/include/hw/virtio/vhost-vsock.h +++ b/include/hw/virtio/vhost-vsock.h @@ -30,9 +30,6 @@ struct VHostVSock { VHostVSockCommon parent; VHostVSockConf conf; - /* features */ - OnOffAuto seqpacket; - /*< public >*/ }; From c151fd87102cbb4082ac5dbcd704196b0495d28a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:28 -0400 Subject: [PATCH 0238/1334] acpi: add helper routines to initialize ACPI tables Patch introduces acpi_table_begin()/ acpi_table_end() API that hides pointer/offset arithmetic from user as opposed to build_header(), to prevent errors caused by it [1]. acpi_table_begin(): initializes table header and keeps track of table data/offsets acpi_table_end(): sets actual table length and tells bios loader where table is for the later initialization on guest side. 1) commits bb9feea43179 x86: acpi: use offset instead of pointer when using build_header() 4d027afeb3a9 Virt: ACPI: fix qemu assert due to re-assigned table data address Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-2-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Tested-by: Stefan Berger Tested-by: Yanan Wang --- hw/acpi/aml-build.c | 62 +++++++++++++++++++++++++++++++++++++ include/hw/acpi/aml-build.h | 31 +++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index d5103e6d7b..229a3eb654 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -52,6 +52,19 @@ static void build_append_byte(GArray *array, uint8_t val) g_array_append_val(array, val); } +static void build_append_padded_str(GArray *array, const char *str, + size_t maxlen, char pad) +{ + size_t i; + size_t len = strlen(str); + + g_assert(len <= maxlen); + g_array_append_vals(array, str, len); + for (i = maxlen - len; i > 0; i--) { + g_array_append_val(array, pad); + } +} + static void build_append_array(GArray *array, GArray *val) { g_array_append_vals(array, val->data, val->len); @@ -1692,6 +1705,55 @@ Aml *aml_object_type(Aml *object) return var; } +void acpi_table_begin(AcpiTable *desc, GArray *array) +{ + + desc->array = array; + desc->table_offset = array->len; + + /* + * ACPI spec 1.0b + * 5.2.3 System Description Table Header + */ + g_assert(strlen(desc->sig) == 4); + g_array_append_vals(array, desc->sig, 4); /* Signature */ + /* + * reserve space for Length field, which will be patched by + * acpi_table_end() when the table creation is finished. + */ + build_append_int_noprefix(array, 0, 4); /* Length */ + build_append_int_noprefix(array, desc->rev, 1); /* Revision */ + build_append_int_noprefix(array, 0, 1); /* Checksum */ + build_append_padded_str(array, desc->oem_id, 6, ' '); /* OEMID */ + /* OEM Table ID */ + build_append_padded_str(array, desc->oem_table_id, 8, ' '); + build_append_int_noprefix(array, 1, 4); /* OEM Revision */ + g_array_append_vals(array, ACPI_BUILD_APPNAME8, 4); /* Creator ID */ + build_append_int_noprefix(array, 1, 4); /* Creator Revision */ +} + +void acpi_table_end(BIOSLinker *linker, AcpiTable *desc) +{ + /* + * ACPI spec 1.0b + * 5.2.3 System Description Table Header + * Table 5-2 DESCRIPTION_HEADER Fields + */ + const unsigned checksum_offset = 9; + uint32_t table_len = desc->array->len - desc->table_offset; + uint32_t table_len_le = cpu_to_le32(table_len); + gchar *len_ptr = &desc->array->data[desc->table_offset + 4]; + + /* patch "Length" field that has been reserved by acpi_table_begin() + * to the actual length, i.e. accumulated table length from + * acpi_table_begin() till acpi_table_end() + */ + memcpy(len_ptr, &table_len_le, sizeof table_len_le); + + bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE, + desc->table_offset, table_len, desc->table_offset + checksum_offset); +} + void build_header(BIOSLinker *linker, GArray *table_data, AcpiTableHeader *h, const char *sig, int len, uint8_t rev, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 471266d739..4242382399 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -413,6 +413,37 @@ Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target); Aml *aml_object_type(Aml *object); void build_append_int_noprefix(GArray *table, uint64_t value, int size); + +typedef struct AcpiTable { + const char *sig; + const uint8_t rev; + const char *oem_id; + const char *oem_table_id; + /* private vars tracking table state */ + GArray *array; + unsigned table_offset; +} AcpiTable; + +/** + * acpi_table_begin: + * initializes table header and keeps track of + * table data/offsets + * @desc: ACPI table descriptor + * @array: blob where the ACPI table will be composed/stored. + */ +void acpi_table_begin(AcpiTable *desc, GArray *array); + +/** + * acpi_table_end: + * sets actual table length and tells bios loader + * where table is for the later initialization on + * guest side. + * @linker: reference to BIOSLinker object to use for the table + * @table: ACPI table descriptor that was used with @acpi_table_begin + * counterpart + */ +void acpi_table_end(BIOSLinker *linker, AcpiTable *table); + void build_header(BIOSLinker *linker, GArray *table_data, AcpiTableHeader *h, const char *sig, int len, uint8_t rev, From ea298e83a7fb435e57913dd755b53e6b2264feed Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:29 -0400 Subject: [PATCH 0239/1334] acpi: build_rsdt: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offests magic from API user. While at it switch to build_append_int_noprefix() to build entries to other tables (which also removes some manual offset calculations). Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-3-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 29 ++++++++++++++--------------- include/hw/acpi/acpi-defs.h | 10 ---------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 229a3eb654..616a292229 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1884,33 +1884,32 @@ build_rsdp(GArray *tbl, BIOSLinker *linker, AcpiRsdpData *rsdp_data) 32); } -/* Build rsdt table */ +/* + * ACPI 1.0 Root System Description Table (RSDT) + */ void build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, const char *oem_id, const char *oem_table_id) { int i; - unsigned rsdt_entries_offset; - AcpiRsdtDescriptorRev1 *rsdt; - int rsdt_start = table_data->len; - const unsigned table_data_len = (sizeof(uint32_t) * table_offsets->len); - const unsigned rsdt_entry_size = sizeof(rsdt->table_offset_entry[0]); - const size_t rsdt_len = sizeof(*rsdt) + table_data_len; + AcpiTable table = { .sig = "RSDT", .rev = 1, + .oem_id = oem_id, .oem_table_id = oem_table_id }; - rsdt = acpi_data_push(table_data, rsdt_len); - rsdt_entries_offset = (char *)rsdt->table_offset_entry - table_data->data; + acpi_table_begin(&table, table_data); for (i = 0; i < table_offsets->len; ++i) { uint32_t ref_tbl_offset = g_array_index(table_offsets, uint32_t, i); - uint32_t rsdt_entry_offset = rsdt_entries_offset + rsdt_entry_size * i; + uint32_t rsdt_entry_offset = table.array->len; - /* rsdt->table_offset_entry to be filled by Guest linker */ + /* reserve space for entry */ + build_append_int_noprefix(table.array, 0, 4); + + /* mark position of RSDT entry to be filled by Guest linker */ bios_linker_loader_add_pointer(linker, - ACPI_BUILD_TABLE_FILE, rsdt_entry_offset, rsdt_entry_size, + ACPI_BUILD_TABLE_FILE, rsdt_entry_offset, 4, ACPI_BUILD_TABLE_FILE, ref_tbl_offset); + } - build_header(linker, table_data, - (void *)(table_data->data + rsdt_start), - "RSDT", rsdt_len, 1, oem_id, oem_table_id); + acpi_table_end(linker, &table); } /* Build xsdt table */ diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index cf9f44299c..ccfa3382aa 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -149,16 +149,6 @@ struct AcpiSerialPortConsoleRedirection { typedef struct AcpiSerialPortConsoleRedirection AcpiSerialPortConsoleRedirection; -/* - * ACPI 1.0 Root System Description Table (RSDT) - */ -struct AcpiRsdtDescriptorRev1 { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint32_t table_offset_entry[]; /* Array of pointers to other */ - /* ACPI tables */ -} QEMU_PACKED; -typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1; - /* * ACPI 2.0 eXtended System Description Table (XSDT) */ From f497b7cae1cd052fdbc023f02d37921c4069f7e6 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:30 -0400 Subject: [PATCH 0240/1334] acpi: build_xsdt: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. While at it switch to build_append_int_noprefix() to build entries to other tables (which also removes some manual offset calculations). Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-4-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 29 ++++++++++++++--------------- include/hw/acpi/acpi-defs.h | 10 ---------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 616a292229..86b8322ee1 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1912,33 +1912,32 @@ build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, acpi_table_end(linker, &table); } -/* Build xsdt table */ +/* + * ACPI 2.0 eXtended System Description Table (XSDT) + */ void build_xsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, const char *oem_id, const char *oem_table_id) { int i; - unsigned xsdt_entries_offset; - AcpiXsdtDescriptorRev2 *xsdt; - int xsdt_start = table_data->len; - const unsigned table_data_len = (sizeof(uint64_t) * table_offsets->len); - const unsigned xsdt_entry_size = sizeof(xsdt->table_offset_entry[0]); - const size_t xsdt_len = sizeof(*xsdt) + table_data_len; + AcpiTable table = { .sig = "XSDT", .rev = 1, + .oem_id = oem_id, .oem_table_id = oem_table_id }; + + acpi_table_begin(&table, table_data); - xsdt = acpi_data_push(table_data, xsdt_len); - xsdt_entries_offset = (char *)xsdt->table_offset_entry - table_data->data; for (i = 0; i < table_offsets->len; ++i) { uint64_t ref_tbl_offset = g_array_index(table_offsets, uint32_t, i); - uint64_t xsdt_entry_offset = xsdt_entries_offset + xsdt_entry_size * i; + uint64_t xsdt_entry_offset = table.array->len; - /* xsdt->table_offset_entry to be filled by Guest linker */ + /* reserve space for entry */ + build_append_int_noprefix(table.array, 0, 8); + + /* mark position of RSDT entry to be filled by Guest linker */ bios_linker_loader_add_pointer(linker, - ACPI_BUILD_TABLE_FILE, xsdt_entry_offset, xsdt_entry_size, + ACPI_BUILD_TABLE_FILE, xsdt_entry_offset, 8, ACPI_BUILD_TABLE_FILE, ref_tbl_offset); } - build_header(linker, table_data, - (void *)(table_data->data + xsdt_start), - "XSDT", xsdt_len, 1, oem_id, oem_table_id); + acpi_table_end(linker, &table); } void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index ccfa3382aa..f6d2ca172b 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -149,16 +149,6 @@ struct AcpiSerialPortConsoleRedirection { typedef struct AcpiSerialPortConsoleRedirection AcpiSerialPortConsoleRedirection; -/* - * ACPI 2.0 eXtended System Description Table (XSDT) - */ -struct AcpiXsdtDescriptorRev2 { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint64_t table_offset_entry[]; /* Array of pointers to other */ - /* ACPI tables */ -} QEMU_PACKED; -typedef struct AcpiXsdtDescriptorRev2 AcpiXsdtDescriptorRev2; - /* * ACPI 1.0 Firmware ACPI Control Structure (FACS) */ From 7469f1991dc5e061ff724070c65e3fef96602bca Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:31 -0400 Subject: [PATCH 0241/1334] acpi: build_slit: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-5-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 86b8322ee1..ec870f53ba 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1958,11 +1958,12 @@ void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, const char *oem_id, const char *oem_table_id) { - int slit_start, i, j; - slit_start = table_data->len; + int i, j; int nb_numa_nodes = ms->numa_state->num_nodes; + AcpiTable table = { .sig = "SLIT", .rev = 1, + .oem_id = oem_id, .oem_table_id = oem_table_id }; - acpi_data_push(table_data, sizeof(AcpiTableHeader)); + acpi_table_begin(&table, table_data); build_append_int_noprefix(table_data, nb_numa_nodes, 8); for (i = 0; i < nb_numa_nodes; i++) { @@ -1973,11 +1974,7 @@ void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, 1); } } - - build_header(linker, table_data, - (void *)(table_data->data + slit_start), - "SLIT", - table_data->len - slit_start, 1, oem_id, oem_table_id); + acpi_table_end(linker, &table); } /* build rev1/rev3/rev5.1 FADT */ From 4b56e1e4eb08e923f68203f0f92a1d3053bceb33 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:32 -0400 Subject: [PATCH 0242/1334] acpi: build_fadt: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-6-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index ec870f53ba..4d0ff6e5f3 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1982,9 +1982,10 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, const char *oem_id, const char *oem_table_id) { int off; - int fadt_start = tbl->len; + AcpiTable table = { .sig = "FACP", .rev = f->rev, + .oem_id = oem_id, .oem_table_id = oem_table_id }; - acpi_data_push(tbl, sizeof(AcpiTableHeader)); + acpi_table_begin(&table, tbl); /* FACS address to be filled by Guest linker at runtime */ off = tbl->len; @@ -2048,7 +2049,7 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, build_append_int_noprefix(tbl, f->flags, 4); /* Flags */ if (f->rev == 1) { - goto build_hdr; + goto done; } build_append_gas_from_struct(tbl, &f->reset_reg); /* RESET_REG */ @@ -2085,7 +2086,7 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0); /* X_GPE1_BLK */ if (f->rev <= 4) { - goto build_hdr; + goto done; } /* SLEEP_CONTROL_REG */ @@ -2096,9 +2097,8 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, /* TODO: extra fields need to be added to support revisions above rev5 */ assert(f->rev == 5); -build_hdr: - build_header(linker, tbl, (void *)(tbl->data + fadt_start), - "FACP", tbl->len - fadt_start, f->rev, oem_id, oem_table_id); +done: + acpi_table_end(linker, &table); } #ifdef CONFIG_TPM From 3e39c1ed7b642a20b63afaeb3742e0917d25dbe0 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:33 -0400 Subject: [PATCH 0243/1334] acpi: build_tpm2: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Tested-by: Stefan Berger Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-7-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 4d0ff6e5f3..23cda45ee0 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -2111,13 +2111,14 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, const char *oem_id, const char *oem_table_id) { uint8_t start_method_params[12] = {}; - unsigned log_addr_offset, tpm2_start; + unsigned log_addr_offset; uint64_t control_area_start_address; TPMIf *tpmif = tpm_find(); uint32_t start_method; + AcpiTable table = { .sig = "TPM2", .rev = 4, + .oem_id = oem_id, .oem_table_id = oem_table_id }; - tpm2_start = table_data->len; - acpi_data_push(table_data, sizeof(AcpiTableHeader)); + acpi_table_begin(&table, table_data); /* Platform Class */ build_append_int_noprefix(table_data, TPM2_ACPI_CLASS_CLIENT, 2); @@ -2155,9 +2156,7 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, log_addr_offset, 8, ACPI_BUILD_TPMLOG_FILE, 0); - build_header(linker, table_data, - (void *)(table_data->data + tpm2_start), - "TPM2", table_data->len - tpm2_start, 4, oem_id, oem_table_id); + acpi_table_end(linker, &table); } #endif From 13229858cf76b9648ba20be93b70fca4429d7dcd Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:34 -0400 Subject: [PATCH 0244/1334] acpi: acpi_build_hest: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Dongjiu Geng Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-8-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ghes.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index a749b84d62..45d9a809cc 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -362,18 +362,16 @@ static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) void acpi_build_hest(GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { - uint64_t hest_start = table_data->len; + AcpiTable table = { .sig = "HEST", .rev = 1, + .oem_id = oem_id, .oem_table_id = oem_table_id }; - /* Hardware Error Source Table header*/ - acpi_data_push(table_data, sizeof(AcpiTableHeader)); + acpi_table_begin(&table, table_data); /* Error Source Count */ build_append_int_noprefix(table_data, ACPI_GHES_ERROR_SOURCE_COUNT, 4); - build_ghes_v2(table_data, ACPI_HEST_SRC_ID_SEA, linker); - build_header(linker, table_data, (void *)(table_data->data + hest_start), - "HEST", table_data->len - hest_start, 1, oem_id, oem_table_id); + acpi_table_end(linker, &table); } void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, From 578bc7a06462fd71c66d1562c457b01da307a7b6 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:35 -0400 Subject: [PATCH 0245/1334] acpi: build_mcfg: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-9-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/acpi/pci.c b/hw/acpi/pci.c index 75b1103ec4..20b70dcd81 100644 --- a/hw/acpi/pci.c +++ b/hw/acpi/pci.c @@ -28,19 +28,20 @@ #include "hw/acpi/pci.h" #include "hw/pci/pcie_host.h" +/* + * PCI Firmware Specification, Revision 3.0 + * 4.1.2 MCFG Table Description. + */ void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, const char *oem_id, const char *oem_table_id) { - int mcfg_start = table_data->len; + AcpiTable table = { .sig = "MCFG", .rev = 1, + .oem_id = oem_id, .oem_table_id = oem_table_id }; + + acpi_table_begin(&table, table_data); - /* - * PCI Firmware Specification, Revision 3.0 - * 4.1.2 MCFG Table Description. - */ - acpi_data_push(table_data, sizeof(AcpiTableHeader)); /* Reserved */ build_append_int_noprefix(table_data, 0, 8); - /* * Memory Mapped Enhanced Configuration Space Base Address Allocation * Structure @@ -56,6 +57,5 @@ void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, /* Reserved */ build_append_int_noprefix(table_data, 0, 4); - build_header(linker, table_data, (void *)(table_data->data + mcfg_start), - "MCFG", table_data->len - mcfg_start, 1, oem_id, oem_table_id); + acpi_table_end(linker, &table); } From 689ef4721a06dd724e447d8b8e2dc3622c866557 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:36 -0400 Subject: [PATCH 0246/1334] acpi: build_hmat: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Also since acpi_table_begin() reserves space only for standard header while previous acpi_data_push() reserved the header + 4 bytes field, add 4 bytes 'Reserved' field into hmat_build_table_structs() which didn have it. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-10-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/hmat.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hw/acpi/hmat.c b/hw/acpi/hmat.c index edb3fd91b2..6913ebf730 100644 --- a/hw/acpi/hmat.c +++ b/hw/acpi/hmat.c @@ -200,6 +200,8 @@ static void hmat_build_table_structs(GArray *table_data, NumaState *numa_state) HMAT_LB_Info *hmat_lb; NumaHmatCacheOptions *hmat_cache; + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + for (i = 0; i < numa_state->num_nodes; i++) { flags = 0; @@ -256,14 +258,10 @@ static void hmat_build_table_structs(GArray *table_data, NumaState *numa_state) void build_hmat(GArray *table_data, BIOSLinker *linker, NumaState *numa_state, const char *oem_id, const char *oem_table_id) { - int hmat_start = table_data->len; - - /* reserve space for HMAT header */ - acpi_data_push(table_data, 40); + AcpiTable table = { .sig = "HMAT", .rev = 2, + .oem_id = oem_id, .oem_table_id = oem_table_id }; + acpi_table_begin(&table, table_data); hmat_build_table_structs(table_data, numa_state); - - build_header(linker, table_data, - (void *)(table_data->data + hmat_start), - "HMAT", table_data->len - hmat_start, 2, oem_id, oem_table_id); + acpi_table_end(linker, &table); } From 7d1823beeffc43669aaf7c5bc274c30e1c16e6c2 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:37 -0400 Subject: [PATCH 0247/1334] acpi: nvdimm_build_nfit: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Also since acpi_table_begin() reserves space only for standard header while previous acpi_data_push() reserved the header + 4 bytes field, add 4 bytes 'Reserved' field into nvdimm_build_nfit() which didn't have it. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-11-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/nvdimm.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index e3d5fe1939..15f6ca82ca 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -44,22 +44,6 @@ static const uint8_t nvdimm_nfit_spa_uuid[] = UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, 0x18, 0xb7, 0x8c, 0xdb); -/* - * NVDIMM Firmware Interface Table - * @signature: "NFIT" - * - * It provides information that allows OSPM to enumerate NVDIMM present in - * the platform and associate system physical address ranges created by the - * NVDIMMs. - * - * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) - */ -struct NvdimmNfitHeader { - ACPI_TABLE_HEADER_DEF - uint32_t reserved; -} QEMU_PACKED; -typedef struct NvdimmNfitHeader NvdimmNfitHeader; - /* * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware * Interface Table (NFIT). @@ -401,25 +385,33 @@ void nvdimm_plug(NVDIMMState *state) nvdimm_build_fit_buffer(state); } +/* + * NVDIMM Firmware Interface Table + * @signature: "NFIT" + * + * It provides information that allows OSPM to enumerate NVDIMM present in + * the platform and associate system physical address ranges created by the + * NVDIMMs. + * + * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) + */ + static void nvdimm_build_nfit(NVDIMMState *state, GArray *table_offsets, GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { NvdimmFitBuffer *fit_buf = &state->fit_buf; - unsigned int header; + AcpiTable table = { .sig = "NFIT", .rev = 1, + .oem_id = oem_id, .oem_table_id = oem_table_id }; acpi_add_table(table_offsets, table_data); - /* NFIT header. */ - header = table_data->len; - acpi_data_push(table_data, sizeof(NvdimmNfitHeader)); + acpi_table_begin(&table, table_data); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); /* NVDIMM device structures. */ g_array_append_vals(table_data, fit_buf->fit->data, fit_buf->fit->len); - - build_header(linker, table_data, - (void *)(table_data->data + header), "NFIT", - sizeof(NvdimmNfitHeader) + fit_buf->fit->len, 1, oem_id, - oem_table_id); + acpi_table_end(linker, &table); } #define NVDIMM_DSM_MEMORY_SIZE 4096 From de67dd1be0af2b7fb02af441254ea05cd9bc1007 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:38 -0400 Subject: [PATCH 0248/1334] acpi: nvdimm_build_ssdt: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-12-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/nvdimm.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 15f6ca82ca..a7539cfe89 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -1274,14 +1274,15 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, NVDIMMState *nvdimm_state, uint32_t ram_slots, const char *oem_id) { + int mem_addr_offset; Aml *ssdt, *sb_scope, *dev; - int mem_addr_offset, nvdimm_ssdt; + AcpiTable table = { .sig = "SSDT", .rev = 1, + .oem_id = oem_id, .oem_table_id = "NVDIMM" }; acpi_add_table(table_offsets, table_data); + acpi_table_begin(&table, table_data); ssdt = init_aml_allocator(); - acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); - sb_scope = aml_scope("\\_SB"); dev = aml_device("NVDR"); @@ -1310,8 +1311,6 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, aml_append(sb_scope, dev); aml_append(ssdt, sb_scope); - nvdimm_ssdt = table_data->len; - /* copy AML table into ACPI tables blob and patch header there */ g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); mem_addr_offset = build_append_named_dword(table_data, @@ -1323,10 +1322,13 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, mem_addr_offset, sizeof(uint32_t), NVDIMM_DSM_MEM_FILE, 0); - build_header(linker, table_data, - (void *)(table_data->data + nvdimm_ssdt), - "SSDT", table_data->len - nvdimm_ssdt, 1, oem_id, "NVDIMM"); free_aml_allocator(); + /* + * must be executed as the last so that pointer patching command above + * would be executed by guest before it recalculated checksum which were + * scheduled by acpi_table_end() + */ + acpi_table_end(linker, &table); } void nvdimm_build_srat(GArray *table_data) From b25681c358bccb8700d742acb742dc1c2374acb7 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:39 -0400 Subject: [PATCH 0249/1334] acpi: vmgenid_build_acpi: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-13-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/vmgenid.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 4f41a13ea0..0c9f158ac9 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -29,6 +29,8 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid, Aml *ssdt, *dev, *scope, *method, *addr, *if_ctx; uint32_t vgia_offset; QemuUUID guid_le; + AcpiTable table = { .sig = "SSDT", .rev = 1, + .oem_id = oem_id, .oem_table_id = "VMGENID" }; /* Fill in the GUID values. These need to be converted to little-endian * first, since that's what the guest expects @@ -42,12 +44,10 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid, g_array_insert_vals(guid, VMGENID_GUID_OFFSET, guid_le.data, ARRAY_SIZE(guid_le.data)); - /* Put this in a separate SSDT table */ + /* Put VMGNEID into a separate SSDT table */ + acpi_table_begin(&table, table_data); ssdt = init_aml_allocator(); - /* Reserve space for header */ - acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); - /* Storage for the GUID address */ vgia_offset = table_data->len + build_append_named_dword(ssdt->buf, "VGIA"); @@ -116,9 +116,8 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid, ACPI_BUILD_TABLE_FILE, vgia_offset, sizeof(uint32_t), VMGENID_GUID_FW_CFG_FILE, 0); - build_header(linker, table_data, - (void *)(table_data->data + table_data->len - ssdt->buf->len), - "SSDT", ssdt->buf->len, 1, oem_id, "VMGENID"); + /* must be called after above command to ensure correct table checksum */ + acpi_table_end(linker, &table); free_aml_allocator(); } From 5c142bc48f973e348e6ae13495ce370b172abaa4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:40 -0400 Subject: [PATCH 0250/1334] acpi: x86: build_dsdt: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-14-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index f4d6ae3d02..e17451bc6d 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1405,12 +1405,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, #endif int i; VMBusBridge *vmbus_bridge = vmbus_bridge_find(); + AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = x86ms->oem_id, + .oem_table_id = x86ms->oem_table_id }; + acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); - /* Reserve space for header */ - acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader)); - build_dbg_aml(dsdt); if (misc->is_piix4) { sb_scope = aml_scope("_SB"); @@ -1867,9 +1867,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, /* copy AML table into ACPI tables blob and patch header there */ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); - build_header(linker, table_data, - (void *)(table_data->data + table_data->len - dsdt->buf->len), - "DSDT", dsdt->buf->len, 1, x86ms->oem_id, x86ms->oem_table_id); + acpi_table_end(linker, &table); free_aml_allocator(); } From 43dde1705c610710389491e5989ae6f5b5cf6fb9 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:41 -0400 Subject: [PATCH 0251/1334] acpi: build_hpet: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. while at it convert build_hpet() to endian agnostic build_append_FOO() API Signed-off-by: Igor Mammedov Message-Id: <20210924122802.1455362-15-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 26 ++++++++++++++++++-------- include/hw/acpi/acpi-defs.h | 13 ------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index e17451bc6d..12d743d529 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1871,22 +1871,32 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, free_aml_allocator(); } +/* + * IA-PC HPET (High Precision Event Timers) Specification (Revision: 1.0a) + * 3.2.4The ACPI 2.0 HPET Description Table (HPET) + */ static void build_hpet(GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { - Acpi20Hpet *hpet; - int hpet_start = table_data->len; + AcpiTable table = { .sig = "HPET", .rev = 1, + .oem_id = oem_id, .oem_table_id = oem_table_id }; - hpet = acpi_data_push(table_data, sizeof(*hpet)); + acpi_table_begin(&table, table_data); /* Note timer_block_id value must be kept in sync with value advertised by * emulated hpet */ - hpet->timer_block_id = cpu_to_le32(0x8086a201); - hpet->addr.address = cpu_to_le64(HPET_BASE); - build_header(linker, table_data, - (void *)(table_data->data + hpet_start), - "HPET", sizeof(*hpet), 1, oem_id, oem_table_id); + /* Event Timer Block ID */ + build_append_int_noprefix(table_data, 0x8086a201, 4); + /* BASE_ADDRESS */ + build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 0, 0, 0, HPET_BASE); + /* HPET Number */ + build_append_int_noprefix(table_data, 0, 1); + /* Main Counter Minimum Clock_tick in Periodic Mode */ + build_append_int_noprefix(table_data, 0, 2); + /* Page Protection And OEM Attribute */ + build_append_int_noprefix(table_data, 0, 1); + acpi_table_end(linker, &table); } #ifdef CONFIG_TPM diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index f6d2ca172b..4d8f8b34b0 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -358,19 +358,6 @@ struct AcpiGenericTimerTable { } QEMU_PACKED; typedef struct AcpiGenericTimerTable AcpiGenericTimerTable; -/* - * HPET Description Table - */ -struct Acpi20Hpet { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint32_t timer_block_id; - struct AcpiGenericAddress addr; - uint8_t hpet_number; - uint16_t min_tick; - uint8_t page_protect; -} QEMU_PACKED; -typedef struct Acpi20Hpet Acpi20Hpet; - /* * SRAT (NUMA topology description) table */ From 57cb8cfbf2cda59fe829fee54115f72dd7998645 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:42 -0400 Subject: [PATCH 0252/1334] acpi: build_tpm_tcpa: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. While at it switch to build_append_int_noprefix() to build table entries (which also removes some manual offset calculations). Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-16-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 38 ++++++++++++++++++++++--------------- include/hw/acpi/acpi-defs.h | 14 -------------- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 12d743d529..3310a3efc3 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1900,31 +1900,39 @@ build_hpet(GArray *table_data, BIOSLinker *linker, const char *oem_id, } #ifdef CONFIG_TPM +/* + * TCPA Description Table + * + * Following Level 00, Rev 00.37 of specs: + * http://www.trustedcomputinggroup.org/resources/tcg_acpi_specification + * 7.1.2 ACPI Table Layout + */ static void build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, const char *oem_id, const char *oem_table_id) { - int tcpa_start = table_data->len; - Acpi20Tcpa *tcpa = acpi_data_push(table_data, sizeof *tcpa); - unsigned log_addr_size = sizeof(tcpa->log_area_start_address); - unsigned log_addr_offset = - (char *)&tcpa->log_area_start_address - table_data->data; + unsigned log_addr_offset; + AcpiTable table = { .sig = "TCPA", .rev = 2, + .oem_id = oem_id, .oem_table_id = oem_table_id }; - tcpa->platform_class = cpu_to_le16(TPM_TCPA_ACPI_CLASS_CLIENT); - tcpa->log_area_minimum_length = cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); - acpi_data_push(tcpalog, le32_to_cpu(tcpa->log_area_minimum_length)); + acpi_table_begin(&table, table_data); + /* Platform Class */ + build_append_int_noprefix(table_data, TPM_TCPA_ACPI_CLASS_CLIENT, 2); + /* Log Area Minimum Length (LAML) */ + build_append_int_noprefix(table_data, TPM_LOG_AREA_MINIMUM_SIZE, 4); + /* Log Area Start Address (LASA) */ + log_addr_offset = table_data->len; + build_append_int_noprefix(table_data, 0, 8); + /* allocate/reserve space for TPM log area */ + acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE); bios_linker_loader_alloc(linker, ACPI_BUILD_TPMLOG_FILE, tcpalog, 1, false /* high memory */); - /* log area start address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, - ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size, - ACPI_BUILD_TPMLOG_FILE, 0); + bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, + log_addr_offset, 8, ACPI_BUILD_TPMLOG_FILE, 0); - build_header(linker, table_data, - (void *)(table_data->data + tcpa_start), - "TCPA", sizeof(*tcpa), 2, oem_id, oem_table_id); + acpi_table_end(linker, &table); } #endif diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 4d8f8b34b0..3b42b138f0 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -418,20 +418,6 @@ struct AcpiSratProcessorGiccAffinity { typedef struct AcpiSratProcessorGiccAffinity AcpiSratProcessorGiccAffinity; -/* - * TCPA Description Table - * - * Following Level 00, Rev 00.37 of specs: - * http://www.trustedcomputinggroup.org/resources/tcg_acpi_specification - */ -struct Acpi20Tcpa { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint16_t platform_class; - uint32_t log_area_minimum_length; - uint64_t log_area_start_address; -} QEMU_PACKED; -typedef struct Acpi20Tcpa Acpi20Tcpa; - /* DMAR - DMA Remapping table r2.2 */ struct AcpiTableDmar { ACPI_TABLE_HEADER_DEF From 255bf20f2e047be1068f90cf8eaf3fc07cbba7d5 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:43 -0400 Subject: [PATCH 0253/1334] acpi: arm/x86: build_srat: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. While at it switch to build_append_int_noprefix() to build table entries (which also removes some manual offset calculations) Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-17-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 15 +++++++-------- hw/i386/acpi-build.c | 18 +++++++----------- include/hw/acpi/acpi-defs.h | 11 ----------- 3 files changed, 14 insertions(+), 30 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 037cc1fd82..21efe7fe34 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -477,18 +477,19 @@ build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) static void build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { - AcpiSystemResourceAffinityTable *srat; AcpiSratProcessorGiccAffinity *core; AcpiSratMemoryAffinity *numamem; - int i, srat_start; + int i; uint64_t mem_base; MachineClass *mc = MACHINE_GET_CLASS(vms); MachineState *ms = MACHINE(vms); const CPUArchIdList *cpu_list = mc->possible_cpu_arch_ids(ms); + AcpiTable table = { .sig = "SRAT", .rev = 3, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; - srat_start = table_data->len; - srat = acpi_data_push(table_data, sizeof(*srat)); - srat->reserved1 = cpu_to_le32(1); + acpi_table_begin(&table, table_data); + build_append_int_noprefix(table_data, 1, 4); /* Reserved */ + build_append_int_noprefix(table_data, 0, 8); /* Reserved */ for (i = 0; i < cpu_list->len; ++i) { core = acpi_data_push(table_data, sizeof(*core)); @@ -522,9 +523,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } - build_header(linker, table_data, (void *)(table_data->data + srat_start), - "SRAT", table_data->len - srat_start, 3, vms->oem_id, - vms->oem_table_id); + acpi_table_end(linker, &table); } /* GTDT */ diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 3310a3efc3..1ce808c576 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1942,11 +1942,10 @@ build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, static void build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { - AcpiSystemResourceAffinityTable *srat; AcpiSratMemoryAffinity *numamem; int i; - int srat_start, numa_start, slots; + int numa_start, slots; uint64_t mem_len, mem_base, next_base; MachineClass *mc = MACHINE_GET_CLASS(machine); X86MachineState *x86ms = X86_MACHINE(machine); @@ -1957,11 +1956,12 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) ram_addr_t hotpluggable_address_space_size = object_property_get_int(OBJECT(pcms), PC_MACHINE_DEVMEM_REGION_SIZE, NULL); + AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = x86ms->oem_id, + .oem_table_id = x86ms->oem_table_id }; - srat_start = table_data->len; - - srat = acpi_data_push(table_data, sizeof *srat); - srat->reserved1 = cpu_to_le32(1); + acpi_table_begin(&table, table_data); + build_append_int_noprefix(table_data, 1, 4); /* Reserved */ + build_append_int_noprefix(table_data, 0, 8); /* Reserved */ for (i = 0; i < apic_ids->len; i++) { int node_id = apic_ids->cpus[i].props.node_id; @@ -2067,11 +2067,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } - build_header(linker, table_data, - (void *)(table_data->data + srat_start), - "SRAT", - table_data->len - srat_start, 1, x86ms->oem_id, - x86ms->oem_table_id); + acpi_table_end(linker, &table); } /* diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 3b42b138f0..5826ee04b6 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -358,17 +358,6 @@ struct AcpiGenericTimerTable { } QEMU_PACKED; typedef struct AcpiGenericTimerTable AcpiGenericTimerTable; -/* - * SRAT (NUMA topology description) table - */ - -struct AcpiSystemResourceAffinityTable { - ACPI_TABLE_HEADER_DEF - uint32_t reserved1; - uint32_t reserved2[2]; -} QEMU_PACKED; -typedef struct AcpiSystemResourceAffinityTable AcpiSystemResourceAffinityTable; - #define ACPI_SRAT_PROCESSOR_APIC 0 #define ACPI_SRAT_MEMORY 1 #define ACPI_SRAT_PROCESSOR_x2APIC 2 From e5b6d55a6e5e13002cf79a42a0b78b3188e88a1f Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:44 -0400 Subject: [PATCH 0254/1334] acpi: use build_append_int_noprefix() API to compose SRAT table Drop usage of packed structures and explicit endian conversions when building SRAT tables for arm/x86 and use endian agnostic build_append_int_noprefix() API to build it. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-18-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 24 ++++++++---- hw/acpi/nvdimm.c | 4 +- hw/arm/virt-acpi-build.c | 29 ++++++++------ hw/i386/acpi-build.c | 78 +++++++++++++++++++++---------------- include/hw/acpi/acpi-defs.h | 49 ----------------------- include/hw/acpi/aml-build.h | 2 +- 6 files changed, 80 insertions(+), 106 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 23cda45ee0..c0f339847a 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1940,15 +1940,25 @@ build_xsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, acpi_table_end(linker, &table); } -void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, +/* + * ACPI spec, Revision 4.0 + * 5.2.16.2 Memory Affinity Structure + */ +void build_srat_memory(GArray *table_data, uint64_t base, uint64_t len, int node, MemoryAffinityFlags flags) { - numamem->type = ACPI_SRAT_MEMORY; - numamem->length = sizeof(*numamem); - numamem->proximity = cpu_to_le32(node); - numamem->flags = cpu_to_le32(flags); - numamem->base_addr = cpu_to_le64(base); - numamem->range_length = cpu_to_le64(len); + build_append_int_noprefix(table_data, 1, 1); /* Type */ + build_append_int_noprefix(table_data, 40, 1); /* Length */ + build_append_int_noprefix(table_data, node, 4); /* Proximity Domain */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, base, 4); /* Base Address Low */ + /* Base Address High */ + build_append_int_noprefix(table_data, base >> 32, 4); + build_append_int_noprefix(table_data, len, 4); /* Length Low */ + build_append_int_noprefix(table_data, len >> 32, 4); /* Length High */ + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, flags, 4); /* Flags */ + build_append_int_noprefix(table_data, 0, 8); /* Reserved */ } /* diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index a7539cfe89..5f9b552d6a 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -1336,7 +1336,6 @@ void nvdimm_build_srat(GArray *table_data) GSList *device_list = nvdimm_get_device_list(); for (; device_list; device_list = device_list->next) { - AcpiSratMemoryAffinity *numamem = NULL; DeviceState *dev = device_list->data; Object *obj = OBJECT(dev); uint64_t addr, size; @@ -1346,8 +1345,7 @@ void nvdimm_build_srat(GArray *table_data) addr = object_property_get_uint(obj, PC_DIMM_ADDR_PROP, &error_abort); size = object_property_get_uint(obj, PC_DIMM_SIZE_PROP, &error_abort); - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, addr, size, node, + build_srat_memory(table_data, addr, size, node, MEM_AFFINITY_ENABLED | MEM_AFFINITY_NON_VOLATILE); } g_slist_free(device_list); diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 21efe7fe34..6ba02cf281 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -474,11 +474,13 @@ build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) vms->oem_table_id); } +/* + * ACPI spec, Revision 5.1 + * 5.2.16 System Resource Affinity Table (SRAT) + */ static void build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { - AcpiSratProcessorGiccAffinity *core; - AcpiSratMemoryAffinity *numamem; int i; uint64_t mem_base; MachineClass *mc = MACHINE_GET_CLASS(vms); @@ -492,19 +494,23 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0, 8); /* Reserved */ for (i = 0; i < cpu_list->len; ++i) { - core = acpi_data_push(table_data, sizeof(*core)); - core->type = ACPI_SRAT_PROCESSOR_GICC; - core->length = sizeof(*core); - core->proximity = cpu_to_le32(cpu_list->cpus[i].props.node_id); - core->acpi_processor_uid = cpu_to_le32(i); - core->flags = cpu_to_le32(1); + uint32_t nodeid = cpu_list->cpus[i].props.node_id; + /* + * 5.2.16.4 GICC Affinity Structure + */ + build_append_int_noprefix(table_data, 3, 1); /* Type */ + build_append_int_noprefix(table_data, 18, 1); /* Length */ + build_append_int_noprefix(table_data, nodeid, 4); /* Proximity Domain */ + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ + /* Flags, Table 5-76 */ + build_append_int_noprefix(table_data, 1 /* Enabled */, 4); + build_append_int_noprefix(table_data, 0, 4); /* Clock Domain */ } mem_base = vms->memmap[VIRT_MEM].base; for (i = 0; i < ms->numa_state->num_nodes; ++i) { if (ms->numa_state->nodes[i].node_mem > 0) { - numamem = acpi_data_push(table_data, sizeof(*numamem)); - build_srat_memory(numamem, mem_base, + build_srat_memory(table_data, mem_base, ms->numa_state->nodes[i].node_mem, i, MEM_AFFINITY_ENABLED); mem_base += ms->numa_state->nodes[i].node_mem; @@ -516,8 +522,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } if (ms->device_memory) { - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, ms->device_memory->base, + build_srat_memory(table_data, ms->device_memory->base, memory_region_size(&ms->device_memory->mr), ms->numa_state->num_nodes - 1, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1ce808c576..078097a060 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1939,13 +1939,15 @@ build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, #define HOLE_640K_START (640 * KiB) #define HOLE_640K_END (1 * MiB) +/* + * ACPI spec, Revision 3.0 + * 5.2.15 System Resource Affinity Table (SRAT) + */ static void build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { - AcpiSratMemoryAffinity *numamem; - int i; - int numa_start, slots; + int numa_mem_start, slots; uint64_t mem_len, mem_base, next_base; MachineClass *mc = MACHINE_GET_CLASS(machine); X86MachineState *x86ms = X86_MACHINE(machine); @@ -1968,34 +1970,41 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) uint32_t apic_id = apic_ids->cpus[i].arch_id; if (apic_id < 255) { - AcpiSratProcessorAffinity *core; - - core = acpi_data_push(table_data, sizeof *core); - core->type = ACPI_SRAT_PROCESSOR_APIC; - core->length = sizeof(*core); - core->local_apic_id = apic_id; - core->proximity_lo = node_id; - memset(core->proximity_hi, 0, 3); - core->local_sapic_eid = 0; - core->flags = cpu_to_le32(1); + /* 5.2.15.1 Processor Local APIC/SAPIC Affinity Structure */ + build_append_int_noprefix(table_data, 0, 1); /* Type */ + build_append_int_noprefix(table_data, 16, 1); /* Length */ + /* Proximity Domain [7:0] */ + build_append_int_noprefix(table_data, node_id, 1); + build_append_int_noprefix(table_data, apic_id, 1); /* APIC ID */ + /* Flags, Table 5-36 */ + build_append_int_noprefix(table_data, 1, 4); + build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */ + /* Proximity Domain [31:8] */ + build_append_int_noprefix(table_data, 0, 3); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ } else { - AcpiSratProcessorX2ApicAffinity *core; - - core = acpi_data_push(table_data, sizeof *core); - core->type = ACPI_SRAT_PROCESSOR_x2APIC; - core->length = sizeof(*core); - core->x2apic_id = cpu_to_le32(apic_id); - core->proximity_domain = cpu_to_le32(node_id); - core->flags = cpu_to_le32(1); + /* + * ACPI spec, Revision 4.0 + * 5.2.16.3 Processor Local x2APIC Affinity Structure + */ + build_append_int_noprefix(table_data, 2, 1); /* Type */ + build_append_int_noprefix(table_data, 24, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + /* Proximity Domain */ + build_append_int_noprefix(table_data, node_id, 4); + build_append_int_noprefix(table_data, apic_id, 4); /* X2APIC ID */ + /* Flags, Table 5-39 */ + build_append_int_noprefix(table_data, 1 /* Enabled */, 4); + build_append_int_noprefix(table_data, 0, 4); /* Clock Domain */ + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ } } - /* the memory map is a bit tricky, it contains at least one hole * from 640k-1M and possibly another one from 3.5G-4G. */ next_base = 0; - numa_start = table_data->len; + numa_mem_start = table_data->len; for (i = 1; i < nb_numa_nodes + 1; ++i) { mem_base = next_base; @@ -2007,8 +2016,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) next_base > HOLE_640K_START) { mem_len -= next_base - HOLE_640K_START; if (mem_len > 0) { - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, mem_base, mem_len, i - 1, + build_srat_memory(table_data, mem_base, mem_len, i - 1, MEM_AFFINITY_ENABLED); } @@ -2026,8 +2034,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) next_base > x86ms->below_4g_mem_size) { mem_len -= next_base - x86ms->below_4g_mem_size; if (mem_len > 0) { - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, mem_base, mem_len, i - 1, + build_srat_memory(table_data, mem_base, mem_len, i - 1, MEM_AFFINITY_ENABLED); } mem_base = 1ULL << 32; @@ -2036,8 +2043,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) } if (mem_len > 0) { - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, mem_base, mem_len, i - 1, + build_srat_memory(table_data, mem_base, mem_len, i - 1, MEM_AFFINITY_ENABLED); } } @@ -2046,10 +2052,15 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) nvdimm_build_srat(table_data); } - slots = (table_data->len - numa_start) / sizeof *numamem; + /* + * TODO: this part is not in ACPI spec and current linux kernel boots fine + * without these entries. But I recall there were issues the last time I + * tried to remove it with some ancient guest OS, however I can't remember + * what that was so keep this around for now + */ + slots = (table_data->len - numa_mem_start) / 40 /* mem affinity len */; for (; slots < nb_numa_nodes + 2; slots++) { - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); + build_srat_memory(table_data, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } /* @@ -2061,8 +2072,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) * providing _PXM method if necessary. */ if (hotpluggable_address_space_size) { - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, machine->device_memory->base, + build_srat_memory(table_data, machine->device_memory->base, hotpluggable_address_space_size, nb_numa_nodes - 1, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 5826ee04b6..d293304f9c 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -358,55 +358,6 @@ struct AcpiGenericTimerTable { } QEMU_PACKED; typedef struct AcpiGenericTimerTable AcpiGenericTimerTable; -#define ACPI_SRAT_PROCESSOR_APIC 0 -#define ACPI_SRAT_MEMORY 1 -#define ACPI_SRAT_PROCESSOR_x2APIC 2 -#define ACPI_SRAT_PROCESSOR_GICC 3 - -struct AcpiSratProcessorAffinity { - ACPI_SUB_HEADER_DEF - uint8_t proximity_lo; - uint8_t local_apic_id; - uint32_t flags; - uint8_t local_sapic_eid; - uint8_t proximity_hi[3]; - uint32_t reserved; -} QEMU_PACKED; -typedef struct AcpiSratProcessorAffinity AcpiSratProcessorAffinity; - -struct AcpiSratProcessorX2ApicAffinity { - ACPI_SUB_HEADER_DEF - uint16_t reserved; - uint32_t proximity_domain; - uint32_t x2apic_id; - uint32_t flags; - uint32_t clk_domain; - uint32_t reserved2; -} QEMU_PACKED; -typedef struct AcpiSratProcessorX2ApicAffinity AcpiSratProcessorX2ApicAffinity; - -struct AcpiSratMemoryAffinity { - ACPI_SUB_HEADER_DEF - uint32_t proximity; - uint16_t reserved1; - uint64_t base_addr; - uint64_t range_length; - uint32_t reserved2; - uint32_t flags; - uint32_t reserved3[2]; -} QEMU_PACKED; -typedef struct AcpiSratMemoryAffinity AcpiSratMemoryAffinity; - -struct AcpiSratProcessorGiccAffinity { - ACPI_SUB_HEADER_DEF - uint32_t proximity; - uint32_t acpi_processor_uid; - uint32_t flags; - uint32_t clock_domain; -} QEMU_PACKED; - -typedef struct AcpiSratProcessorGiccAffinity AcpiSratProcessorGiccAffinity; - /* DMAR - DMA Remapping table r2.2 */ struct AcpiTableDmar { ACPI_TABLE_HEADER_DEF diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 4242382399..6e1f42e119 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -487,7 +487,7 @@ Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set, uint32_t io_offset, uint32_t mmio32_offset, uint64_t mmio64_offset, uint16_t bus_nr_offset); -void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, +void build_srat_memory(GArray *table_data, uint64_t base, uint64_t len, int node, MemoryAffinityFlags flags); void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, From 91a6b9756970f70d933ea3c01fb5306ea6e5b331 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:45 -0400 Subject: [PATCH 0255/1334] acpi: build_dmar_q35: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. While at it switch to build_append_int_noprefix() to build table entries tables. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-19-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 85 ++++++++++++++++++++----------------- include/hw/acpi/acpi-defs.h | 68 ----------------------------- 2 files changed, 47 insertions(+), 106 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 078097a060..c65ab1d6a5 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2086,8 +2086,9 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) static void insert_scope(PCIBus *bus, PCIDevice *dev, void *opaque) { + const size_t device_scope_size = 6 /* device scope structure */ + + 2 /* 1 path entry */; GArray *scope_blob = opaque; - AcpiDmarDeviceScope *scope = NULL; if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { /* Dmar Scope Type: 0x02 for PCI Bridge */ @@ -2098,8 +2099,7 @@ insert_scope(PCIBus *bus, PCIDevice *dev, void *opaque) } /* length */ - build_append_int_noprefix(scope_blob, - sizeof(*scope) + sizeof(scope->path[0]), 1); + build_append_int_noprefix(scope_blob, device_scope_size, 1); /* reserved */ build_append_int_noprefix(scope_blob, 0, 2); /* enumeration_id */ @@ -2131,26 +2131,26 @@ dmar_host_bridges(Object *obj, void *opaque) } /* - * VT-d spec 8.1 DMA Remapping Reporting Structure - * (version Oct. 2014 or later) + * Intel ® Virtualization Technology for Directed I/O + * Architecture Specification. Revision 3.3 + * 8.1 DMA Remapping Reporting Structure */ static void build_dmar_q35(GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { - int dmar_start = table_data->len; - - AcpiTableDmar *dmar; - AcpiDmarHardwareUnit *drhd; - AcpiDmarRootPortATS *atsr; uint8_t dmar_flags = 0; + uint8_t rsvd10[10] = {}; + /* Root complex IOAPIC uses one path only */ + const size_t ioapic_scope_size = 6 /* device scope structure */ + + 2 /* 1 path entry */; X86IOMMUState *iommu = x86_iommu_get_default(); - AcpiDmarDeviceScope *scope = NULL; - /* Root complex IOAPIC use one path[0] only */ - size_t ioapic_scope_size = sizeof(*scope) + sizeof(scope->path[0]); IntelIOMMUState *intel_iommu = INTEL_IOMMU_DEVICE(iommu); GArray *scope_blob = g_array_new(false, true, 1); + AcpiTable table = { .sig = "DMAR", .rev = 1, .oem_id = oem_id, + .oem_table_id = oem_table_id }; + /* * A PCI bus walk, for each PCI host bridge. * Insert scope for each PCI bridge and endpoint device which @@ -2164,43 +2164,52 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker, const char *oem_id, dmar_flags |= 0x1; /* Flags: 0x1: INT_REMAP */ } - dmar = acpi_data_push(table_data, sizeof(*dmar)); - dmar->host_address_width = intel_iommu->aw_bits - 1; - dmar->flags = dmar_flags; + acpi_table_begin(&table, table_data); + /* Host Address Width */ + build_append_int_noprefix(table_data, intel_iommu->aw_bits - 1, 1); + build_append_int_noprefix(table_data, dmar_flags, 1); /* Flags */ + g_array_append_vals(table_data, rsvd10, sizeof(rsvd10)); /* Reserved */ - /* DMAR Remapping Hardware Unit Definition structure */ - drhd = acpi_data_push(table_data, sizeof(*drhd) + ioapic_scope_size); - drhd->type = cpu_to_le16(ACPI_DMAR_TYPE_HARDWARE_UNIT); - drhd->length = - cpu_to_le16(sizeof(*drhd) + ioapic_scope_size + scope_blob->len); - drhd->flags = 0; /* Don't include all pci device */ - drhd->pci_segment = cpu_to_le16(0); - drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR); + /* 8.3 DMAR Remapping Hardware Unit Definition structure */ + build_append_int_noprefix(table_data, 0, 2); /* Type */ + /* Length */ + build_append_int_noprefix(table_data, + 16 + ioapic_scope_size + scope_blob->len, 2); + /* Flags */ + build_append_int_noprefix(table_data, 0 /* Don't include all pci device */ , + 1); + build_append_int_noprefix(table_data, 0 , 1); /* Reserved */ + build_append_int_noprefix(table_data, 0 , 2); /* Segment Number */ + /* Register Base Address */ + build_append_int_noprefix(table_data, Q35_HOST_BRIDGE_IOMMU_ADDR , 8); /* Scope definition for the root-complex IOAPIC. See VT-d spec * 8.3.1 (version Oct. 2014 or later). */ - scope = &drhd->scope[0]; - scope->entry_type = 0x03; /* Type: 0x03 for IOAPIC */ - scope->length = ioapic_scope_size; - scope->enumeration_id = ACPI_BUILD_IOAPIC_ID; - scope->bus = Q35_PSEUDO_BUS_PLATFORM; - scope->path[0].device = PCI_SLOT(Q35_PSEUDO_DEVFN_IOAPIC); - scope->path[0].function = PCI_FUNC(Q35_PSEUDO_DEVFN_IOAPIC); + build_append_int_noprefix(table_data, 0x03 /* IOAPIC */, 1); /* Type */ + build_append_int_noprefix(table_data, ioapic_scope_size, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + /* Enumeration ID */ + build_append_int_noprefix(table_data, ACPI_BUILD_IOAPIC_ID, 1); + /* Start Bus Number */ + build_append_int_noprefix(table_data, Q35_PSEUDO_BUS_PLATFORM, 1); + /* Path, {Device, Function} pair */ + build_append_int_noprefix(table_data, PCI_SLOT(Q35_PSEUDO_DEVFN_IOAPIC), 1); + build_append_int_noprefix(table_data, PCI_FUNC(Q35_PSEUDO_DEVFN_IOAPIC), 1); /* Add scope found above */ g_array_append_vals(table_data, scope_blob->data, scope_blob->len); g_array_free(scope_blob, true); if (iommu->dt_supported) { - atsr = acpi_data_push(table_data, sizeof(*atsr)); - atsr->type = cpu_to_le16(ACPI_DMAR_TYPE_ATSR); - atsr->length = cpu_to_le16(sizeof(*atsr)); - atsr->flags = ACPI_DMAR_ATSR_ALL_PORTS; - atsr->pci_segment = cpu_to_le16(0); + /* 8.5 Root Port ATS Capability Reporting Structure */ + build_append_int_noprefix(table_data, 2, 2); /* Type */ + build_append_int_noprefix(table_data, 8, 2); /* Length */ + build_append_int_noprefix(table_data, 1 /* ALL_PORTS */, 1); /* Flags */ + build_append_int_noprefix(table_data, 0, 1); /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); /* Segment Number */ } - build_header(linker, table_data, (void *)(table_data->data + dmar_start), - "DMAR", table_data->len - dmar_start, 1, oem_id, oem_table_id); + acpi_table_end(linker, &table); } /* diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index d293304f9c..c4f0a202e8 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -358,74 +358,6 @@ struct AcpiGenericTimerTable { } QEMU_PACKED; typedef struct AcpiGenericTimerTable AcpiGenericTimerTable; -/* DMAR - DMA Remapping table r2.2 */ -struct AcpiTableDmar { - ACPI_TABLE_HEADER_DEF - uint8_t host_address_width; /* Maximum DMA physical addressability */ - uint8_t flags; - uint8_t reserved[10]; -} QEMU_PACKED; -typedef struct AcpiTableDmar AcpiTableDmar; - -/* Masks for Flags field above */ -#define ACPI_DMAR_INTR_REMAP 1 -#define ACPI_DMAR_X2APIC_OPT_OUT (1 << 1) - -/* Values for sub-structure type for DMAR */ -enum { - ACPI_DMAR_TYPE_HARDWARE_UNIT = 0, /* DRHD */ - ACPI_DMAR_TYPE_RESERVED_MEMORY = 1, /* RMRR */ - ACPI_DMAR_TYPE_ATSR = 2, /* ATSR */ - ACPI_DMAR_TYPE_HARDWARE_AFFINITY = 3, /* RHSR */ - ACPI_DMAR_TYPE_ANDD = 4, /* ANDD */ - ACPI_DMAR_TYPE_RESERVED = 5 /* Reserved for furture use */ -}; - -/* - * Sub-structures for DMAR - */ - -/* Device scope structure for DRHD. */ -struct AcpiDmarDeviceScope { - uint8_t entry_type; - uint8_t length; - uint16_t reserved; - uint8_t enumeration_id; - uint8_t bus; - struct { - uint8_t device; - uint8_t function; - } path[]; -} QEMU_PACKED; -typedef struct AcpiDmarDeviceScope AcpiDmarDeviceScope; - -/* Type 0: Hardware Unit Definition */ -struct AcpiDmarHardwareUnit { - uint16_t type; - uint16_t length; - uint8_t flags; - uint8_t reserved; - uint16_t pci_segment; /* The PCI Segment associated with this unit */ - uint64_t address; /* Base address of remapping hardware register-set */ - AcpiDmarDeviceScope scope[]; -} QEMU_PACKED; -typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit; - -/* Type 2: Root Port ATS Capability Reporting Structure */ -struct AcpiDmarRootPortATS { - uint16_t type; - uint16_t length; - uint8_t flags; - uint8_t reserved; - uint16_t pci_segment; - AcpiDmarDeviceScope scope[]; -} QEMU_PACKED; -typedef struct AcpiDmarRootPortATS AcpiDmarRootPortATS; - -/* Masks for Flags field above */ -#define ACPI_DMAR_INCLUDE_PCI_ALL 1 -#define ACPI_DMAR_ATSR_ALL_PORTS 1 - /* * Input Output Remapping Table (IORT) * Conforms to "IO Remapping Table System Software on ARM Platforms", From eaa507646d039556ee2a10514b0a0acfe8f8362d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:46 -0400 Subject: [PATCH 0256/1334] acpi: build_waet: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-20-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index c65ab1d6a5..e5cc4f7daa 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2223,10 +2223,10 @@ static void build_waet(GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { - int waet_start = table_data->len; + AcpiTable table = { .sig = "WAET", .rev = 1, .oem_id = oem_id, + .oem_table_id = oem_table_id }; - /* WAET header */ - acpi_data_push(table_data, sizeof(AcpiTableHeader)); + acpi_table_begin(&table, table_data); /* * Set "ACPI PM timer good" flag. * @@ -2235,9 +2235,7 @@ build_waet(GArray *table_data, BIOSLinker *linker, const char *oem_id, * Which avoids costly VMExits caused by guest re-reading it unnecessarily. */ build_append_int_noprefix(table_data, 1 << 1 /* ACPI PM timer good */, 4); - - build_header(linker, table_data, (void *)(table_data->data + waet_start), - "WAET", table_data->len - waet_start, 1, oem_id, oem_table_id); + acpi_table_end(linker, &table); } /* From b0a45ff60e822c26fbf412d3b82bd5748e4f8fb0 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:47 -0400 Subject: [PATCH 0257/1334] acpi: build_amd_iommu: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-21-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index e5cc4f7daa..d9e2b5dc30 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2341,12 +2341,12 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { int ivhd_table_len = 24; - int iommu_start = table_data->len; AMDVIState *s = AMD_IOMMU_DEVICE(x86_iommu_get_default()); GArray *ivhd_blob = g_array_new(false, true, 1); + AcpiTable table = { .sig = "IVRS", .rev = 1, .oem_id = oem_id, + .oem_table_id = oem_table_id }; - /* IVRS header */ - acpi_data_push(table_data, sizeof(AcpiTableHeader)); + acpi_table_begin(&table, table_data); /* IVinfo - IO virtualization information common to all * IOMMU units in a system */ @@ -2431,10 +2431,7 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, 0x48, /* special device */ 8); } - - build_header(linker, table_data, (void *)(table_data->data + iommu_start), - "IVRS", table_data->len - iommu_start, 1, oem_id, - oem_table_id); + acpi_table_end(linker, &table); } typedef From 99a7545f92378765ca98eb1b54b1087c9d2567ec Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:48 -0400 Subject: [PATCH 0258/1334] acpi: madt: arm/x86: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-22-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 19 +++++++++++-------- hw/i386/acpi-common.c | 19 +++++++++++-------- include/hw/acpi/acpi-defs.h | 9 --------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 6ba02cf281..e3bdcd44e8 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -567,19 +567,26 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) vms->oem_table_id); } -/* MADT */ +/* + * ACPI spec, Revision 5.0 + * 5.2.12 Multiple APIC Description Table (MADT) + */ static void build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); - int madt_start = table_data->len; const MemMapEntry *memmap = vms->memmap; const int *irqmap = vms->irqmap; AcpiMadtGenericDistributor *gicd; AcpiMadtGenericMsiFrame *gic_msi; int i; + AcpiTable table = { .sig = "APIC", .rev = 3, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; - acpi_data_push(table_data, sizeof(AcpiMultipleApicTable)); + acpi_table_begin(&table, table_data); + /* Local Interrupt Controller Address */ + build_append_int_noprefix(table_data, 0, 4); + build_append_int_noprefix(table_data, 0, 4); /* Flags */ gicd = acpi_data_push(table_data, sizeof *gicd); gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR; @@ -650,11 +657,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS); gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE); } - - build_header(linker, table_data, - (void *)(table_data->data + madt_start), "APIC", - table_data->len - madt_start, 3, vms->oem_id, - vms->oem_table_id); + acpi_table_end(linker, &table); } /* FADT */ diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 1f5947fcf9..a0cde1d874 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -71,24 +71,29 @@ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, } } +/* + * ACPI spec, Revision 1.0b + * 5.2.8 Multiple APIC Description Table + */ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, X86MachineState *x86ms, AcpiDeviceIf *adev, const char *oem_id, const char *oem_table_id) { MachineClass *mc = MACHINE_GET_CLASS(x86ms); const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(x86ms)); - int madt_start = table_data->len; AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(adev); bool x2apic_mode = false; - AcpiMultipleApicTable *madt; AcpiMadtIoApic *io_apic; AcpiMadtIntsrcovr *intsrcovr; int i; + AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = oem_id, + .oem_table_id = oem_table_id }; - madt = acpi_data_push(table_data, sizeof *madt); - madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS); - madt->flags = cpu_to_le32(1); + acpi_table_begin(&table, table_data); + /* Local APIC Address */ + build_append_int_noprefix(table_data, APIC_DEFAULT_ADDRESS, 4); + build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ for (i = 0; i < apic_ids->len; i++) { adevc->madt_cpu(adev, i, apic_ids, table_data); @@ -156,8 +161,6 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, local_nmi->lint = 1; /* ACPI_LINT1 */ } - build_header(linker, table_data, - (void *)(table_data->data + madt_start), "APIC", - table_data->len - madt_start, 1, oem_id, oem_table_id); + acpi_table_end(linker, &table); } diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index c4f0a202e8..c7fa5caa06 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -176,15 +176,6 @@ typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1; #define ACPI_DUAL_PIC 0 #define ACPI_MULTIPLE_APIC 1 -/* Master MADT */ - -struct AcpiMultipleApicTable { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint32_t local_apic_address; /* Physical address of local APIC */ - uint32_t flags; -} QEMU_PACKED; -typedef struct AcpiMultipleApicTable AcpiMultipleApicTable; - /* Values for Type in APIC sub-headers */ #define ACPI_APIC_PROCESSOR 0 From b10e7f4f8f05b3956eabf0661320c71f1e3bab10 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:49 -0400 Subject: [PATCH 0259/1334] acpi: x86: remove dead code Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-23-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/acpi/acpi-defs.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index c7fa5caa06..af4fa412a5 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -163,19 +163,6 @@ struct AcpiFacsDescriptorRev1 { } QEMU_PACKED; typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1; -/* - * Differentiated System Description Table (DSDT) - */ - -/* - * MADT values and structures - */ - -/* Values for MADT PCATCompat */ - -#define ACPI_DUAL_PIC 0 -#define ACPI_MULTIPLE_APIC 1 - /* Values for Type in APIC sub-headers */ #define ACPI_APIC_PROCESSOR 0 From d0aa026a498cdd4e082f12618767f19a2532712a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:50 -0400 Subject: [PATCH 0260/1334] acpi: x86: set enabled when composing _MAT entries Instead of composing disabled _MAT entry and then later on patching it to enabled for hotpluggbale CPUs in DSDT, set it to enabled at the time _MAT entry is built. It will allow to drop usage of packed structures in following patches when build_madt() is switched to use build_append_int_noprefix() API. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-24-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/acpi-x86-stub.c | 3 ++- hw/acpi/cpu.c | 17 ++--------------- hw/i386/acpi-common.c | 18 ++++++------------ include/hw/acpi/acpi_dev_interface.h | 3 ++- include/hw/i386/pc.h | 6 +++--- 5 files changed, 15 insertions(+), 32 deletions(-) diff --git a/hw/acpi/acpi-x86-stub.c b/hw/acpi/acpi-x86-stub.c index e9e46c5c5f..3df1e090f4 100644 --- a/hw/acpi/acpi-x86-stub.c +++ b/hw/acpi/acpi-x86-stub.c @@ -3,7 +3,8 @@ #include "hw/i386/acpi-build.h" void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry) + const CPUArchIdList *apic_ids, GArray *entry, + bool force_enabled) { } diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index f82e9512fd..b20903ea30 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -669,21 +669,8 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, /* build _MAT object */ assert(adevc && adevc->madt_cpu); - adevc->madt_cpu(adev, i, arch_ids, madt_buf); - switch (madt_buf->data[0]) { - case ACPI_APIC_PROCESSOR: { - AcpiMadtProcessorApic *apic = (void *)madt_buf->data; - apic->flags = cpu_to_le32(1); - break; - } - case ACPI_APIC_LOCAL_X2APIC: { - AcpiMadtProcessorX2Apic *apic = (void *)madt_buf->data; - apic->flags = cpu_to_le32(1); - break; - } - default: - assert(0); - } + adevc->madt_cpu(adev, i, arch_ids, madt_buf, + true); /* set enabled flag */ aml_append(dev, aml_name_decl("_MAT", aml_buffer(madt_buf->len, (uint8_t *)madt_buf->data))); g_array_free(madt_buf, true); diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index a0cde1d874..7983a13a93 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -34,9 +34,11 @@ #include "acpi-common.h" void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry) + const CPUArchIdList *apic_ids, GArray *entry, + bool force_enabled) { uint32_t apic_id = apic_ids->cpus[uid].arch_id; + uint32_t flags = apic_ids->cpus[uid].cpu != NULL || force_enabled ? 1 : 0; /* ACPI spec says that LAPIC entry for non present * CPU may be omitted from MADT or it must be marked @@ -51,11 +53,7 @@ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, apic->length = sizeof(*apic); apic->processor_id = uid; apic->local_apic_id = apic_id; - if (apic_ids->cpus[uid].cpu != NULL) { - apic->flags = cpu_to_le32(1); - } else { - apic->flags = cpu_to_le32(0); - } + apic->flags = cpu_to_le32(flags); } else { AcpiMadtProcessorX2Apic *apic = acpi_data_push(entry, sizeof *apic); @@ -63,11 +61,7 @@ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, apic->length = sizeof(*apic); apic->uid = cpu_to_le32(uid); apic->x2apic_id = cpu_to_le32(apic_id); - if (apic_ids->cpus[uid].cpu != NULL) { - apic->flags = cpu_to_le32(1); - } else { - apic->flags = cpu_to_le32(0); - } + apic->flags = cpu_to_le32(flags); } } @@ -96,7 +90,7 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ for (i = 0; i < apic_ids->len; i++) { - adevc->madt_cpu(adev, i, apic_ids, table_data); + adevc->madt_cpu(adev, i, apic_ids, table_data, false); if (apic_ids->cpus[i].arch_id > 254) { x2apic_mode = true; } diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index 769ff55c7e..ea6056ab92 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -53,6 +53,7 @@ struct AcpiDeviceIfClass { void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list); void (*send_event)(AcpiDeviceIf *adev, AcpiEventStatusBits ev); void (*madt_cpu)(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry); + const CPUArchIdList *apic_ids, GArray *entry, + bool force_enabled); }; #endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 5748d7c55f..11426e26dc 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -190,10 +190,10 @@ bool pc_system_ovmf_table_find(const char *entry, uint8_t **data, int *data_len); void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); - -/* acpi-build.c */ +/* hw/i386/acpi-common.c */ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry); + const CPUArchIdList *apic_ids, GArray *entry, + bool force_enabled); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); From dd092b9c6008149785d7789c729f468fb762aa0b Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:51 -0400 Subject: [PATCH 0261/1334] acpi: x86: madt: use build_append_int_noprefix() API to compose MADT table Drop usage of packed structures and explicit endian conversions when building MADT table for arm/x86 and use endian agnostic build_append_int_noprefix() API to build it. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-25-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-common.c | 129 +++++++++++++++++++----------------- include/hw/acpi/acpi-defs.h | 64 ------------------ 2 files changed, 67 insertions(+), 126 deletions(-) diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 7983a13a93..4aaafbdd7b 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -38,7 +38,9 @@ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, bool force_enabled) { uint32_t apic_id = apic_ids->cpus[uid].arch_id; - uint32_t flags = apic_ids->cpus[uid].cpu != NULL || force_enabled ? 1 : 0; + /* Flags – Local APIC Flags */ + uint32_t flags = apic_ids->cpus[uid].cpu != NULL || force_enabled ? + 1 /* Enabled */ : 0; /* ACPI spec says that LAPIC entry for non present * CPU may be omitted from MADT or it must be marked @@ -47,24 +49,47 @@ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, * should be put in MADT but kept disabled. */ if (apic_id < 255) { - AcpiMadtProcessorApic *apic = acpi_data_push(entry, sizeof *apic); - - apic->type = ACPI_APIC_PROCESSOR; - apic->length = sizeof(*apic); - apic->processor_id = uid; - apic->local_apic_id = apic_id; - apic->flags = cpu_to_le32(flags); + /* Rev 1.0b, Table 5-13 Processor Local APIC Structure */ + build_append_int_noprefix(entry, 0, 1); /* Type */ + build_append_int_noprefix(entry, 8, 1); /* Length */ + build_append_int_noprefix(entry, uid, 1); /* ACPI Processor ID */ + build_append_int_noprefix(entry, apic_id, 1); /* APIC ID */ + build_append_int_noprefix(entry, flags, 4); /* Flags */ } else { - AcpiMadtProcessorX2Apic *apic = acpi_data_push(entry, sizeof *apic); - - apic->type = ACPI_APIC_LOCAL_X2APIC; - apic->length = sizeof(*apic); - apic->uid = cpu_to_le32(uid); - apic->x2apic_id = cpu_to_le32(apic_id); - apic->flags = cpu_to_le32(flags); + /* Rev 4.0, 5.2.12.12 Processor Local x2APIC Structure */ + build_append_int_noprefix(entry, 9, 1); /* Type */ + build_append_int_noprefix(entry, 16, 1); /* Length */ + build_append_int_noprefix(entry, 0, 2); /* Reserved */ + build_append_int_noprefix(entry, apic_id, 4); /* X2APIC ID */ + build_append_int_noprefix(entry, flags, 4); /* Flags */ + build_append_int_noprefix(entry, uid, 4); /* ACPI Processor UID */ } } +static void build_ioapic(GArray *entry, uint8_t id, uint32_t addr, uint32_t irq) +{ + /* Rev 1.0b, 5.2.8.2 IO APIC */ + build_append_int_noprefix(entry, 1, 1); /* Type */ + build_append_int_noprefix(entry, 12, 1); /* Length */ + build_append_int_noprefix(entry, id, 1); /* IO APIC ID */ + build_append_int_noprefix(entry, 0, 1); /* Reserved */ + build_append_int_noprefix(entry, addr, 4); /* IO APIC Address */ + build_append_int_noprefix(entry, irq, 4); /* System Vector Base */ +} + +static void +build_xrupt_override(GArray *entry, uint8_t src, uint32_t gsi, uint16_t flags) +{ + /* Rev 1.0b, 5.2.8.3.1 Interrupt Source Overrides */ + build_append_int_noprefix(entry, 2, 1); /* Type */ + build_append_int_noprefix(entry, 10, 1); /* Length */ + build_append_int_noprefix(entry, 0, 1); /* Bus */ + build_append_int_noprefix(entry, src, 1); /* Source */ + /* Global System Interrupt Vector */ + build_append_int_noprefix(entry, gsi, 4); + build_append_int_noprefix(entry, flags, 2); /* Flags */ +} + /* * ACPI spec, Revision 1.0b * 5.2.8 Multiple APIC Description Table @@ -73,14 +98,11 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, X86MachineState *x86ms, AcpiDeviceIf *adev, const char *oem_id, const char *oem_table_id) { + int i; + bool x2apic_mode = false; MachineClass *mc = MACHINE_GET_CLASS(x86ms); const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(x86ms)); AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(adev); - bool x2apic_mode = false; - - AcpiMadtIoApic *io_apic; - AcpiMadtIntsrcovr *intsrcovr; - int i; AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; @@ -96,30 +118,15 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, } } - io_apic = acpi_data_push(table_data, sizeof *io_apic); - io_apic->type = ACPI_APIC_IO; - io_apic->length = sizeof(*io_apic); - io_apic->io_apic_id = ACPI_BUILD_IOAPIC_ID; - io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); - io_apic->interrupt = cpu_to_le32(0); - + build_ioapic(table_data, ACPI_BUILD_IOAPIC_ID, IO_APIC_DEFAULT_ADDRESS, 0); if (x86ms->ioapic2) { - AcpiMadtIoApic *io_apic2; - io_apic2 = acpi_data_push(table_data, sizeof *io_apic); - io_apic2->type = ACPI_APIC_IO; - io_apic2->length = sizeof(*io_apic); - io_apic2->io_apic_id = ACPI_BUILD_IOAPIC_ID + 1; - io_apic2->address = cpu_to_le32(IO_APIC_SECONDARY_ADDRESS); - io_apic2->interrupt = cpu_to_le32(IO_APIC_SECONDARY_IRQBASE); + build_ioapic(table_data, ACPI_BUILD_IOAPIC_ID + 1, + IO_APIC_SECONDARY_ADDRESS, IO_APIC_SECONDARY_IRQBASE); } if (x86ms->apic_xrupt_override) { - intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); - intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; - intsrcovr->length = sizeof(*intsrcovr); - intsrcovr->source = 0; - intsrcovr->gsi = cpu_to_le32(2); - intsrcovr->flags = cpu_to_le16(0); /* conforms to bus specifications */ + build_xrupt_override(table_data, 0, 2, + 0 /* Flags: Conforms to the specifications of the bus */); } for (i = 1; i < 16; i++) { @@ -127,32 +134,30 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, /* No need for a INT source override structure. */ continue; } - intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); - intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; - intsrcovr->length = sizeof(*intsrcovr); - intsrcovr->source = i; - intsrcovr->gsi = cpu_to_le32(i); - intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ + build_xrupt_override(table_data, i, i, + 0xd /* Flags: Active high, Level Triggered */); } if (x2apic_mode) { - AcpiMadtLocalX2ApicNmi *local_nmi; - - local_nmi = acpi_data_push(table_data, sizeof *local_nmi); - local_nmi->type = ACPI_APIC_LOCAL_X2APIC_NMI; - local_nmi->length = sizeof(*local_nmi); - local_nmi->uid = 0xFFFFFFFF; /* all processors */ - local_nmi->flags = cpu_to_le16(0); - local_nmi->lint = 1; /* ACPI_LINT1 */ + /* Rev 4.0, 5.2.12.13 Local x2APIC NMI Structure*/ + build_append_int_noprefix(table_data, 0xA, 1); /* Type */ + build_append_int_noprefix(table_data, 12, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Flags */ + /* ACPI Processor UID */ + build_append_int_noprefix(table_data, 0xFFFFFFFF /* all processors */, + 4); + /* Local x2APIC LINT# */ + build_append_int_noprefix(table_data, 1 /* ACPI_LINT1 */, 1); + build_append_int_noprefix(table_data, 0, 3); /* Reserved */ } else { - AcpiMadtLocalNmi *local_nmi; - - local_nmi = acpi_data_push(table_data, sizeof *local_nmi); - local_nmi->type = ACPI_APIC_LOCAL_NMI; - local_nmi->length = sizeof(*local_nmi); - local_nmi->processor_id = 0xff; /* all processors */ - local_nmi->flags = cpu_to_le16(0); - local_nmi->lint = 1; /* ACPI_LINT1 */ + /* Rev 1.0b, 5.2.8.3.3 Local APIC NMI */ + build_append_int_noprefix(table_data, 4, 1); /* Type */ + build_append_int_noprefix(table_data, 6, 1); /* Length */ + /* ACPI Processor ID */ + build_append_int_noprefix(table_data, 0xFF /* all processors */, 1); + build_append_int_noprefix(table_data, 0, 2); /* Flags */ + /* Local APIC INTI# */ + build_append_int_noprefix(table_data, 1 /* ACPI_LINT1 */, 1); } acpi_table_end(linker, &table); diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index af4fa412a5..3f174ba208 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -165,17 +165,6 @@ typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1; /* Values for Type in APIC sub-headers */ -#define ACPI_APIC_PROCESSOR 0 -#define ACPI_APIC_IO 1 -#define ACPI_APIC_XRUPT_OVERRIDE 2 -#define ACPI_APIC_NMI 3 -#define ACPI_APIC_LOCAL_NMI 4 -#define ACPI_APIC_ADDRESS_OVERRIDE 5 -#define ACPI_APIC_IO_SAPIC 6 -#define ACPI_APIC_LOCAL_SAPIC 7 -#define ACPI_APIC_XRUPT_SOURCE 8 -#define ACPI_APIC_LOCAL_X2APIC 9 -#define ACPI_APIC_LOCAL_X2APIC_NMI 10 #define ACPI_APIC_GENERIC_CPU_INTERFACE 11 #define ACPI_APIC_GENERIC_DISTRIBUTOR 12 #define ACPI_APIC_GENERIC_MSI_FRAME 13 @@ -192,59 +181,6 @@ typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1; /* Sub-structures for MADT */ -struct AcpiMadtProcessorApic { - ACPI_SUB_HEADER_DEF - uint8_t processor_id; /* ACPI processor id */ - uint8_t local_apic_id; /* Processor's local APIC id */ - uint32_t flags; -} QEMU_PACKED; -typedef struct AcpiMadtProcessorApic AcpiMadtProcessorApic; - -struct AcpiMadtIoApic { - ACPI_SUB_HEADER_DEF - uint8_t io_apic_id; /* I/O APIC ID */ - uint8_t reserved; /* Reserved - must be zero */ - uint32_t address; /* APIC physical address */ - uint32_t interrupt; /* Global system interrupt where INTI - * lines start */ -} QEMU_PACKED; -typedef struct AcpiMadtIoApic AcpiMadtIoApic; - -struct AcpiMadtIntsrcovr { - ACPI_SUB_HEADER_DEF - uint8_t bus; - uint8_t source; - uint32_t gsi; - uint16_t flags; -} QEMU_PACKED; -typedef struct AcpiMadtIntsrcovr AcpiMadtIntsrcovr; - -struct AcpiMadtLocalNmi { - ACPI_SUB_HEADER_DEF - uint8_t processor_id; /* ACPI processor id */ - uint16_t flags; /* MPS INTI flags */ - uint8_t lint; /* Local APIC LINT# */ -} QEMU_PACKED; -typedef struct AcpiMadtLocalNmi AcpiMadtLocalNmi; - -struct AcpiMadtProcessorX2Apic { - ACPI_SUB_HEADER_DEF - uint16_t reserved; - uint32_t x2apic_id; /* Processor's local x2APIC ID */ - uint32_t flags; - uint32_t uid; /* Processor object _UID */ -} QEMU_PACKED; -typedef struct AcpiMadtProcessorX2Apic AcpiMadtProcessorX2Apic; - -struct AcpiMadtLocalX2ApicNmi { - ACPI_SUB_HEADER_DEF - uint16_t flags; /* MPS INTI flags */ - uint32_t uid; /* Processor object _UID */ - uint8_t lint; /* Local APIC LINT# */ - uint8_t reserved[3]; /* Local APIC LINT# */ -} QEMU_PACKED; -typedef struct AcpiMadtLocalX2ApicNmi AcpiMadtLocalX2ApicNmi; - struct AcpiMadtGenericCpuInterface { ACPI_SUB_HEADER_DEF uint16_t reserved; From 37f33084ed2eb0867e3a3f501c6279c7c5f666c6 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:52 -0400 Subject: [PATCH 0262/1334] acpi: arm/virt: madt: use build_append_int_noprefix() API to compose MADT table Drop usage of packed structures and explicit endian conversions when building MADT table for arm/x86 and use endian agnostic build_append_int_noprefix() API to build it. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-26-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 148 ++++++++++++++++++++++-------------- include/hw/acpi/acpi-defs.h | 84 -------------------- 2 files changed, 89 insertions(+), 143 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index e3bdcd44e8..a9a78d904a 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -568,94 +568,124 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } /* - * ACPI spec, Revision 5.0 + * ACPI spec, Revision 5.1 Errata A * 5.2.12 Multiple APIC Description Table (MADT) */ +static void build_append_gicr(GArray *table_data, uint64_t base, uint32_t size) +{ + build_append_int_noprefix(table_data, 0xE, 1); /* Type */ + build_append_int_noprefix(table_data, 16, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + /* Discovery Range Base Addres */ + build_append_int_noprefix(table_data, base, 8); + build_append_int_noprefix(table_data, size, 4); /* Discovery Range Length */ +} + static void build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { + int i; VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); const MemMapEntry *memmap = vms->memmap; - const int *irqmap = vms->irqmap; - AcpiMadtGenericDistributor *gicd; - AcpiMadtGenericMsiFrame *gic_msi; - int i; AcpiTable table = { .sig = "APIC", .rev = 3, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; acpi_table_begin(&table, table_data); /* Local Interrupt Controller Address */ build_append_int_noprefix(table_data, 0, 4); - build_append_int_noprefix(table_data, 0, 4); /* Flags */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ - gicd = acpi_data_push(table_data, sizeof *gicd); - gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR; - gicd->length = sizeof(*gicd); - gicd->base_address = cpu_to_le64(memmap[VIRT_GIC_DIST].base); - gicd->version = vms->gic_version; + /* 5.2.12.15 GIC Distributor Structure */ + build_append_int_noprefix(table_data, 0xC, 1); /* Type */ + build_append_int_noprefix(table_data, 24, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); /* GIC ID */ + /* Physical Base Address */ + build_append_int_noprefix(table_data, memmap[VIRT_GIC_DIST].base, 8); + build_append_int_noprefix(table_data, 0, 4); /* System Vector Base */ + /* GIC version */ + build_append_int_noprefix(table_data, vms->gic_version, 1); + build_append_int_noprefix(table_data, 0, 3); /* Reserved */ for (i = 0; i < MACHINE(vms)->smp.cpus; i++) { - AcpiMadtGenericCpuInterface *gicc = acpi_data_push(table_data, - sizeof(*gicc)); ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); + uint64_t physical_base_address = 0, gich = 0, gicv = 0; + uint32_t vgic_interrupt = vms->virt ? PPI(ARCH_GIC_MAINT_IRQ) : 0; + uint32_t pmu_interrupt = arm_feature(&armcpu->env, ARM_FEATURE_PMU) ? + PPI(VIRTUAL_PMU_IRQ) : 0; - gicc->type = ACPI_APIC_GENERIC_CPU_INTERFACE; - gicc->length = sizeof(*gicc); if (vms->gic_version == 2) { - gicc->base_address = cpu_to_le64(memmap[VIRT_GIC_CPU].base); - gicc->gich_base_address = cpu_to_le64(memmap[VIRT_GIC_HYP].base); - gicc->gicv_base_address = cpu_to_le64(memmap[VIRT_GIC_VCPU].base); + physical_base_address = memmap[VIRT_GIC_CPU].base; + gicv = memmap[VIRT_GIC_VCPU].base; + gich = memmap[VIRT_GIC_HYP].base; } - gicc->cpu_interface_number = cpu_to_le32(i); - gicc->arm_mpidr = cpu_to_le64(armcpu->mp_affinity); - gicc->uid = cpu_to_le32(i); - gicc->flags = cpu_to_le32(ACPI_MADT_GICC_ENABLED); - if (arm_feature(&armcpu->env, ARM_FEATURE_PMU)) { - gicc->performance_interrupt = cpu_to_le32(PPI(VIRTUAL_PMU_IRQ)); - } - if (vms->virt) { - gicc->vgic_interrupt = cpu_to_le32(PPI(ARCH_GIC_MAINT_IRQ)); - } + /* 5.2.12.14 GIC Structure */ + build_append_int_noprefix(table_data, 0xB, 1); /* Type */ + build_append_int_noprefix(table_data, 76, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, i, 4); /* GIC ID */ + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ + /* Flags */ + build_append_int_noprefix(table_data, 1, 4); /* Enabled */ + /* Parking Protocol Version */ + build_append_int_noprefix(table_data, 0, 4); + /* Performance Interrupt GSIV */ + build_append_int_noprefix(table_data, pmu_interrupt, 4); + build_append_int_noprefix(table_data, 0, 8); /* Parked Address */ + /* Physical Base Address */ + build_append_int_noprefix(table_data, physical_base_address, 8); + build_append_int_noprefix(table_data, gicv, 8); /* GICV */ + build_append_int_noprefix(table_data, gich, 8); /* GICH */ + /* VGIC Maintenance interrupt */ + build_append_int_noprefix(table_data, vgic_interrupt, 4); + build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/ + /* MPIDR */ + build_append_int_noprefix(table_data, armcpu->mp_affinity, 8); } if (vms->gic_version == 3) { - AcpiMadtGenericTranslator *gic_its; - int nb_redist_regions = virt_gicv3_redist_region_count(vms); - AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data, - sizeof *gicr); - - gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR; - gicr->length = sizeof(*gicr); - gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base); - gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size); - - if (nb_redist_regions == 2) { - gicr = acpi_data_push(table_data, sizeof(*gicr)); - gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR; - gicr->length = sizeof(*gicr); - gicr->base_address = - cpu_to_le64(memmap[VIRT_HIGH_GIC_REDIST2].base); - gicr->range_length = - cpu_to_le32(memmap[VIRT_HIGH_GIC_REDIST2].size); + build_append_gicr(table_data, memmap[VIRT_GIC_REDIST].base, + memmap[VIRT_GIC_REDIST].size); + if (virt_gicv3_redist_region_count(vms) == 2) { + build_append_gicr(table_data, memmap[VIRT_HIGH_GIC_REDIST2].base, + memmap[VIRT_HIGH_GIC_REDIST2].size); } if (its_class_name() && !vmc->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); + /* + * FIXME: Structure is from Revision 6.0 where 'GIC Structure' + * has additional fields on top of implemented 5.1 Errata A, + * to make it consistent with v6.0 we need to bump everything + * to v6.0 + */ + /* + * ACPI spec, Revision 6.0 Errata A + * (original 6.0 definition has invalid Length) + * 5.2.12.18 GIC ITS Structure + */ + build_append_int_noprefix(table_data, 0xF, 1); /* Type */ + build_append_int_noprefix(table_data, 20, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); /* GIC ITS ID */ + /* Physical Base Address */ + build_append_int_noprefix(table_data, memmap[VIRT_GIC_ITS].base, 8); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ } } else { - gic_msi = acpi_data_push(table_data, sizeof *gic_msi); - gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME; - gic_msi->length = sizeof(*gic_msi); - gic_msi->gic_msi_frame_id = 0; - gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base); - gic_msi->flags = cpu_to_le32(1); - gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS); - gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE); + const uint16_t spi_base = vms->irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE; + + /* 5.2.12.16 GIC MSI Frame Structure */ + build_append_int_noprefix(table_data, 0xD, 1); /* Type */ + build_append_int_noprefix(table_data, 24, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); /* GIC MSI Frame ID */ + /* Physical Base Address */ + build_append_int_noprefix(table_data, memmap[VIRT_GIC_V2M].base, 8); + build_append_int_noprefix(table_data, 1, 4); /* Flags */ + /* SPI Count */ + build_append_int_noprefix(table_data, NUM_GICV2M_SPIS, 2); + build_append_int_noprefix(table_data, spi_base, 2); /* SPI Base */ } acpi_table_end(linker, &table); } diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 3f174ba208..bcada37601 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -163,90 +163,6 @@ struct AcpiFacsDescriptorRev1 { } QEMU_PACKED; typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1; -/* Values for Type in APIC sub-headers */ - -#define ACPI_APIC_GENERIC_CPU_INTERFACE 11 -#define ACPI_APIC_GENERIC_DISTRIBUTOR 12 -#define ACPI_APIC_GENERIC_MSI_FRAME 13 -#define ACPI_APIC_GENERIC_REDISTRIBUTOR 14 -#define ACPI_APIC_GENERIC_TRANSLATOR 15 -#define ACPI_APIC_RESERVED 16 /* 16 and greater are reserved */ - -/* - * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE) - */ -#define ACPI_SUB_HEADER_DEF /* Common ACPI sub-structure header */\ - uint8_t type; \ - uint8_t length; - -/* Sub-structures for MADT */ - -struct AcpiMadtGenericCpuInterface { - ACPI_SUB_HEADER_DEF - uint16_t reserved; - uint32_t cpu_interface_number; - uint32_t uid; - uint32_t flags; - uint32_t parking_version; - uint32_t performance_interrupt; - uint64_t parked_address; - uint64_t base_address; - uint64_t gicv_base_address; - uint64_t gich_base_address; - uint32_t vgic_interrupt; - uint64_t gicr_base_address; - uint64_t arm_mpidr; -} QEMU_PACKED; - -typedef struct AcpiMadtGenericCpuInterface AcpiMadtGenericCpuInterface; - -/* GICC CPU Interface Flags */ -#define ACPI_MADT_GICC_ENABLED 1 - -struct AcpiMadtGenericDistributor { - ACPI_SUB_HEADER_DEF - uint16_t reserved; - uint32_t gic_id; - uint64_t base_address; - uint32_t global_irq_base; - /* ACPI 5.1 Errata 1228 Present GIC version in MADT table */ - uint8_t version; - uint8_t reserved2[3]; -} QEMU_PACKED; - -typedef struct AcpiMadtGenericDistributor AcpiMadtGenericDistributor; - -struct AcpiMadtGenericMsiFrame { - ACPI_SUB_HEADER_DEF - uint16_t reserved; - uint32_t gic_msi_frame_id; - uint64_t base_address; - uint32_t flags; - uint16_t spi_count; - uint16_t spi_base; -} QEMU_PACKED; - -typedef struct AcpiMadtGenericMsiFrame AcpiMadtGenericMsiFrame; - -struct AcpiMadtGenericRedistributor { - ACPI_SUB_HEADER_DEF - uint16_t reserved; - uint64_t base_address; - uint32_t range_length; -} QEMU_PACKED; - -typedef struct AcpiMadtGenericRedistributor AcpiMadtGenericRedistributor; - -struct AcpiMadtGenericTranslator { - ACPI_SUB_HEADER_DEF - uint16_t reserved; - uint32_t translation_id; - uint64_t base_address; - uint32_t reserved2; -} QEMU_PACKED; - -typedef struct AcpiMadtGenericTranslator AcpiMadtGenericTranslator; - /* * Generic Timer Description Table (GTDT) */ From 8f20f9a7364f5324416c33a7d0aeccbddd5affcc Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:53 -0400 Subject: [PATCH 0263/1334] acpi: build_dsdt_microvm: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-27-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-microvm.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index 1a0f77b911..196d318499 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -113,16 +113,16 @@ build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, Aml *dsdt, *sb_scope, *scope, *pkg; bool ambiguous; Object *isabus; + AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = x86ms->oem_id, + .oem_table_id = x86ms->oem_table_id }; isabus = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous); assert(isabus); assert(!ambiguous); + acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); - /* Reserve space for header */ - acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader)); - sb_scope = aml_scope("_SB"); fw_cfg_add_acpi_dsdt(sb_scope, x86ms->fw_cfg); isa_build_aml(ISA_BUS(isabus), sb_scope); @@ -144,11 +144,10 @@ build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, aml_append(scope, aml_name_decl("_S5", pkg)); aml_append(dsdt, scope); - /* copy AML table into ACPI tables blob and patch header there */ + /* copy AML bytecode into ACPI tables blob */ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); - build_header(linker, table_data, - (void *)(table_data->data + table_data->len - dsdt->buf->len), - "DSDT", dsdt->buf->len, 2, x86ms->oem_id, x86ms->oem_table_id); + + acpi_table_end(linker, &table); free_aml_allocator(); } From fc02b86982df0628926869551901b64d2ab777e5 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:54 -0400 Subject: [PATCH 0264/1334] acpi: arm: virt: build_dsdt: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-28-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index a9a78d904a..4b9687439d 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -729,10 +729,11 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) MachineState *ms = MACHINE(vms); const MemMapEntry *memmap = vms->memmap; const int *irqmap = vms->irqmap; + AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; + acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); - /* Reserve space for header */ - acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader)); /* When booting the VM with UEFI, UEFI takes ownership of the RTC hardware. * While UEFI can use libfdt to disable the RTC device node in the DTB that @@ -779,12 +780,10 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) aml_append(dsdt, scope); - /* copy AML table into ACPI tables blob and patch header there */ + /* copy AML table into ACPI tables blob */ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); - build_header(linker, table_data, - (void *)(table_data->data + table_data->len - dsdt->buf->len), - "DSDT", dsdt->buf->len, 2, vms->oem_id, - vms->oem_table_id); + + acpi_table_end(linker, &table); free_aml_allocator(); } From 3548494e49dacd33bbd610876d1ac38f8f8b94bc Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:55 -0400 Subject: [PATCH 0265/1334] acpi: arm: virt: build_iort: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Tested-by: Eric Auger Message-Id: <20210924122802.1455362-29-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 44 +++++++++++++++---------------------- include/hw/acpi/acpi-defs.h | 14 ------------ 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 4b9687439d..42ea460313 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -273,20 +273,26 @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b) return idmap_a->input_base - idmap_b->input_base; } +/* + * Input Output Remapping Table (IORT) + * Conforms to "IO Remapping Table System Software on ARM Platforms", + * Document number: ARM DEN 0049B, October 2015 + */ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { - int i, nb_nodes, rc_mapping_count, iort_start = table_data->len; + int i, nb_nodes, rc_mapping_count; AcpiIortIdMapping *idmap; AcpiIortItsGroup *its; - AcpiIortTable *iort; AcpiIortSmmu3 *smmu; - size_t node_size, iort_node_offset, iort_length, smmu_offset = 0; AcpiIortRC *rc; + const uint32_t iort_node_offset = 48; + size_t node_size, smmu_offset = 0; GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); - iort = acpi_data_push(table_data, sizeof(*iort)); + AcpiTable table = { .sig = "IORT", .rev = 0, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; if (vms->iommu == VIRT_IOMMU_SMMUV3) { AcpiIortIdMapping next_range = {0}; @@ -325,18 +331,16 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) rc_mapping_count = 1; } - iort_length = sizeof(*iort); - iort->node_count = cpu_to_le32(nb_nodes); - /* - * Use a copy in case table_data->data moves during acpi_data_push - * operations. - */ - iort_node_offset = sizeof(*iort); - iort->node_offset = cpu_to_le32(iort_node_offset); + /* Table 2 The IORT */ + acpi_table_begin(&table, table_data); + /* Number of IORT Nodes */ + build_append_int_noprefix(table_data, nb_nodes, 4); + /* Offset to Array of IORT Nodes */ + build_append_int_noprefix(table_data, iort_node_offset, 4); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ /* ITS group node */ node_size = sizeof(*its) + sizeof(uint32_t); - iort_length += node_size; its = acpi_data_push(table_data, node_size); its->type = ACPI_IORT_NODE_ITS_GROUP; @@ -350,7 +354,6 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* SMMUv3 node */ smmu_offset = iort_node_offset + node_size; node_size = sizeof(*smmu) + sizeof(*idmap); - iort_length += node_size; smmu = acpi_data_push(table_data, node_size); smmu->type = ACPI_IORT_NODE_SMMU_V3; @@ -375,7 +378,6 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* Root Complex Node */ node_size = sizeof(*rc) + sizeof(*idmap) * rc_mapping_count; - iort_length += node_size; rc = acpi_data_push(table_data, node_size); rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX; @@ -424,19 +426,9 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) idmap->output_reference = cpu_to_le32(iort_node_offset); } + acpi_table_end(linker, &table); g_array_free(smmu_idmaps, true); g_array_free(its_idmaps, true); - - /* - * Update the pointer address in case table_data->data moves during above - * acpi_data_push operations. - */ - iort = (AcpiIortTable *)(table_data->data + iort_start); - iort->length = cpu_to_le32(iort_length); - - build_header(linker, table_data, (void *)(table_data->data + iort_start), - "IORT", table_data->len - iort_start, 0, vms->oem_id, - vms->oem_table_id); } static void diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index bcada37601..195f90caf6 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -188,20 +188,6 @@ struct AcpiGenericTimerTable { } QEMU_PACKED; typedef struct AcpiGenericTimerTable AcpiGenericTimerTable; -/* - * Input Output Remapping Table (IORT) - * Conforms to "IO Remapping Table System Software on ARM Platforms", - * Document number: ARM DEN 0049B, October 2015 - */ - -struct AcpiIortTable { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint32_t node_count; - uint32_t node_offset; - uint32_t reserved; -} QEMU_PACKED; -typedef struct AcpiIortTable AcpiIortTable; - /* * IORT node types */ From 271cbb2f2bfecfbb31175645f4e3da7ec626b810 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:56 -0400 Subject: [PATCH 0266/1334] acpi: arm/virt: convert build_iort() to endian agnostic build_append_FOO() API Drop usage of packed structures and explicit endian conversions when building IORT table use endian agnostic build_append_int_noprefix() API to build it. Signed-off-by: Igor Mammedov Message-Id: <20210924122802.1455362-30-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Eric Auger Tested-by: Eric Auger --- hw/arm/virt-acpi-build.c | 156 ++++++++++++++++++++---------------- include/hw/acpi/acpi-defs.h | 71 ---------------- 2 files changed, 89 insertions(+), 138 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 42ea460313..8c382915a9 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -240,6 +240,28 @@ static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) } #endif +#define ID_MAPPING_ENTRY_SIZE 20 +#define SMMU_V3_ENTRY_SIZE 60 +#define ROOT_COMPLEX_ENTRY_SIZE 32 +#define IORT_NODE_OFFSET 48 + +static void build_iort_id_mapping(GArray *table_data, uint32_t input_base, + uint32_t id_count, uint32_t out_ref) +{ + /* Identity RID mapping covering the whole input RID range */ + build_append_int_noprefix(table_data, input_base, 4); /* Input base */ + build_append_int_noprefix(table_data, id_count, 4); /* Number of IDs */ + build_append_int_noprefix(table_data, input_base, 4); /* Output base */ + build_append_int_noprefix(table_data, out_ref, 4); /* Output Reference */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ +} + +struct AcpiIortIdMapping { + uint32_t input_base; + uint32_t id_count; +}; +typedef struct AcpiIortIdMapping AcpiIortIdMapping; + /* Build the iort ID mapping to SMMUv3 for a given PCI host bridge */ static int iort_host_bridges(Object *obj, void *opaque) @@ -282,17 +304,16 @@ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i, nb_nodes, rc_mapping_count; - AcpiIortIdMapping *idmap; - AcpiIortItsGroup *its; - AcpiIortSmmu3 *smmu; - AcpiIortRC *rc; - const uint32_t iort_node_offset = 48; + const uint32_t iort_node_offset = IORT_NODE_OFFSET; size_t node_size, smmu_offset = 0; + AcpiIortIdMapping *idmap; GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); AcpiTable table = { .sig = "IORT", .rev = 0, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; + /* Table 2 The IORT */ + acpi_table_begin(&table, table_data); if (vms->iommu == VIRT_IOMMU_SMMUV3) { AcpiIortIdMapping next_range = {0}; @@ -330,100 +351,101 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) nb_nodes = 2; /* RC, ITS */ rc_mapping_count = 1; } - - /* Table 2 The IORT */ - acpi_table_begin(&table, table_data); /* Number of IORT Nodes */ build_append_int_noprefix(table_data, nb_nodes, 4); + /* Offset to Array of IORT Nodes */ - build_append_int_noprefix(table_data, iort_node_offset, 4); + build_append_int_noprefix(table_data, IORT_NODE_OFFSET, 4); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ - /* ITS group node */ - node_size = sizeof(*its) + sizeof(uint32_t); - its = acpi_data_push(table_data, node_size); - - its->type = ACPI_IORT_NODE_ITS_GROUP; - its->length = cpu_to_le16(node_size); - its->its_count = cpu_to_le32(1); - its->identifiers[0] = 0; /* MADT translation_id */ + /* 3.1.1.3 ITS group node */ + build_append_int_noprefix(table_data, 0 /* ITS Group */, 1); /* Type */ + node_size = 20 /* fixed header size */ + 4 /* 1 GIC ITS Identifier */; + build_append_int_noprefix(table_data, node_size, 2); /* Length */ + build_append_int_noprefix(table_data, 0, 1); /* Revision */ + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); /* Number of ID mappings */ + build_append_int_noprefix(table_data, 0, 4); /* Reference to ID Array */ + build_append_int_noprefix(table_data, 1, 4); /* Number of ITSs */ + /* GIC ITS Identifier Array */ + build_append_int_noprefix(table_data, 0 /* MADT translation_id */, 4); if (vms->iommu == VIRT_IOMMU_SMMUV3) { int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; - /* SMMUv3 node */ - smmu_offset = iort_node_offset + node_size; - node_size = sizeof(*smmu) + sizeof(*idmap); - smmu = acpi_data_push(table_data, node_size); + smmu_offset = table_data->len - table.table_offset; + /* 3.1.1.2 SMMUv3 */ + build_append_int_noprefix(table_data, 4 /* SMMUv3 */, 1); /* Type */ + node_size = SMMU_V3_ENTRY_SIZE + ID_MAPPING_ENTRY_SIZE; + build_append_int_noprefix(table_data, node_size, 2); /* Length */ + build_append_int_noprefix(table_data, 0, 1); /* Revision */ + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 1, 4); /* Number of ID mappings */ + /* Reference to ID Array */ + build_append_int_noprefix(table_data, SMMU_V3_ENTRY_SIZE, 4); + /* Base address */ + build_append_int_noprefix(table_data, vms->memmap[VIRT_SMMU].base, 8); + /* Flags */ + build_append_int_noprefix(table_data, 1 /* COHACC OverrideNote */, 4); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 0, 8); /* VATOS address */ + /* Model */ + build_append_int_noprefix(table_data, 0 /* Generic SMMU-v3 */, 4); + build_append_int_noprefix(table_data, irq, 4); /* Event */ + build_append_int_noprefix(table_data, irq + 1, 4); /* PRI */ + build_append_int_noprefix(table_data, irq + 3, 4); /* GERR */ + build_append_int_noprefix(table_data, irq + 2, 4); /* Sync */ - smmu->type = ACPI_IORT_NODE_SMMU_V3; - smmu->length = cpu_to_le16(node_size); - smmu->mapping_count = cpu_to_le32(1); - smmu->mapping_offset = cpu_to_le32(sizeof(*smmu)); - smmu->base_address = cpu_to_le64(vms->memmap[VIRT_SMMU].base); - smmu->flags = cpu_to_le32(ACPI_IORT_SMMU_V3_COHACC_OVERRIDE); - smmu->event_gsiv = cpu_to_le32(irq); - smmu->pri_gsiv = cpu_to_le32(irq + 1); - smmu->sync_gsiv = cpu_to_le32(irq + 2); - smmu->gerr_gsiv = cpu_to_le32(irq + 3); - - /* Identity RID mapping covering the whole input RID range */ - idmap = &smmu->id_mapping_array[0]; - idmap->input_base = 0; - idmap->id_count = cpu_to_le32(0xFFFF); - idmap->output_base = 0; /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort_node_offset); + build_iort_id_mapping(table_data, 0, 0xFFFF, IORT_NODE_OFFSET); } - /* Root Complex Node */ - node_size = sizeof(*rc) + sizeof(*idmap) * rc_mapping_count; - rc = acpi_data_push(table_data, node_size); + /* Table 16 Root Complex Node */ + build_append_int_noprefix(table_data, 2 /* Root complex */, 1); /* Type */ + node_size = ROOT_COMPLEX_ENTRY_SIZE + + ID_MAPPING_ENTRY_SIZE * rc_mapping_count; + build_append_int_noprefix(table_data, node_size, 2); /* Length */ + build_append_int_noprefix(table_data, 0, 1); /* Revision */ + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + /* Number of ID mappings */ + build_append_int_noprefix(table_data, rc_mapping_count, 4); + /* Reference to ID Array */ + build_append_int_noprefix(table_data, ROOT_COMPLEX_ENTRY_SIZE, 4); - rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX; - rc->length = cpu_to_le16(node_size); - rc->mapping_count = cpu_to_le32(rc_mapping_count); - rc->mapping_offset = cpu_to_le32(sizeof(*rc)); + /* Table 13 Memory access properties */ + /* CCA: Cache Coherent Attribute */ + build_append_int_noprefix(table_data, 1 /* fully coherent */, 4); + build_append_int_noprefix(table_data, 0, 1); /* AH: Note Allocation Hints */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + /* MAF: Note Memory Access Flags */ + build_append_int_noprefix(table_data, 0x3 /* CCA = CPM = DCAS = 1 */, 1); - /* fully coherent device */ - rc->memory_properties.cache_coherency = cpu_to_le32(1); - rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */ - rc->pci_segment_number = 0; /* MCFG pci_segment */ + build_append_int_noprefix(table_data, 0, 4); /* ATS Attribute */ + /* MCFG pci_segment */ + build_append_int_noprefix(table_data, 0, 4); /* PCI Segment number */ + /* Output Reference */ if (vms->iommu == VIRT_IOMMU_SMMUV3) { AcpiIortIdMapping *range; /* translated RIDs connect to SMMUv3 node: RC -> SMMUv3 -> ITS */ for (i = 0; i < smmu_idmaps->len; i++) { - idmap = &rc->id_mapping_array[i]; range = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i); - - idmap->input_base = cpu_to_le32(range->input_base); - idmap->id_count = cpu_to_le32(range->id_count); - idmap->output_base = cpu_to_le32(range->input_base); /* output IORT node is the smmuv3 node */ - idmap->output_reference = cpu_to_le32(smmu_offset); + build_iort_id_mapping(table_data, range->input_base, + range->id_count, smmu_offset); } /* bypassed RIDs connect to ITS group node directly: RC -> ITS */ for (i = 0; i < its_idmaps->len; i++) { - idmap = &rc->id_mapping_array[smmu_idmaps->len + i]; range = &g_array_index(its_idmaps, AcpiIortIdMapping, i); - - idmap->input_base = cpu_to_le32(range->input_base); - idmap->id_count = cpu_to_le32(range->id_count); - idmap->output_base = cpu_to_le32(range->input_base); /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort_node_offset); + build_iort_id_mapping(table_data, range->input_base, + range->id_count, iort_node_offset); } } else { - /* Identity RID mapping covering the whole input RID range */ - idmap = &rc->id_mapping_array[0]; - idmap->input_base = cpu_to_le32(0); - idmap->id_count = cpu_to_le32(0xFFFF); - idmap->output_base = cpu_to_le32(0); /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort_node_offset); + build_iort_id_mapping(table_data, 0, 0xFFFF, IORT_NODE_OFFSET); } acpi_table_end(linker, &table); diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 195f90caf6..6f2f08a9de 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -188,75 +188,4 @@ struct AcpiGenericTimerTable { } QEMU_PACKED; typedef struct AcpiGenericTimerTable AcpiGenericTimerTable; -/* - * IORT node types - */ - -#define ACPI_IORT_NODE_HEADER_DEF /* Node format common fields */ \ - uint8_t type; \ - uint16_t length; \ - uint8_t revision; \ - uint32_t reserved; \ - uint32_t mapping_count; \ - uint32_t mapping_offset; - -/* Values for node Type above */ -enum { - ACPI_IORT_NODE_ITS_GROUP = 0x00, - ACPI_IORT_NODE_NAMED_COMPONENT = 0x01, - ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02, - ACPI_IORT_NODE_SMMU = 0x03, - ACPI_IORT_NODE_SMMU_V3 = 0x04 -}; - -struct AcpiIortIdMapping { - uint32_t input_base; - uint32_t id_count; - uint32_t output_base; - uint32_t output_reference; - uint32_t flags; -} QEMU_PACKED; -typedef struct AcpiIortIdMapping AcpiIortIdMapping; - -struct AcpiIortMemoryAccess { - uint32_t cache_coherency; - uint8_t hints; - uint16_t reserved; - uint8_t memory_flags; -} QEMU_PACKED; -typedef struct AcpiIortMemoryAccess AcpiIortMemoryAccess; - -struct AcpiIortItsGroup { - ACPI_IORT_NODE_HEADER_DEF - uint32_t its_count; - uint32_t identifiers[]; -} QEMU_PACKED; -typedef struct AcpiIortItsGroup AcpiIortItsGroup; - -#define ACPI_IORT_SMMU_V3_COHACC_OVERRIDE 1 - -struct AcpiIortSmmu3 { - ACPI_IORT_NODE_HEADER_DEF - uint64_t base_address; - uint32_t flags; - uint32_t reserved2; - uint64_t vatos_address; - uint32_t model; - uint32_t event_gsiv; - uint32_t pri_gsiv; - uint32_t gerr_gsiv; - uint32_t sync_gsiv; - AcpiIortIdMapping id_mapping_array[]; -} QEMU_PACKED; -typedef struct AcpiIortSmmu3 AcpiIortSmmu3; - -struct AcpiIortRC { - ACPI_IORT_NODE_HEADER_DEF - AcpiIortMemoryAccess memory_properties; - uint32_t ats_attribute; - uint32_t pci_segment_number; - AcpiIortIdMapping id_mapping_array[]; -} QEMU_PACKED; -typedef struct AcpiIortRC AcpiIortRC; - #endif From 88b1045eadd699335ffab0c9235f096f3a8cf771 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:57 -0400 Subject: [PATCH 0267/1334] acpi: arm/virt: build_spcr: fix invalid cast implicit cast to structure uint8_t member didn't raise error when assigning value from incorrect enum, but when using build_append_gas() (next patch) it will error out with (clang): implicit conversion from enumeration type 'AmlRegionSpace' to different enumeration type 'AmlAddressSpace' fix cast error by using correct AML_AS_SYSTEM_MEMORY enum Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-31-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 8c382915a9..7b8706b305 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -465,7 +465,7 @@ build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) spcr->interface_type = 0x3; /* ARM PL011 UART */ - spcr->base_address.space_id = AML_SYSTEM_MEMORY; + spcr->base_address.space_id = AML_AS_SYSTEM_MEMORY; spcr->base_address.bit_width = 8; spcr->base_address.bit_offset = 0; spcr->base_address.access_width = 1; From a86d86ac0ae39b7e8fcce08fffd2e0ab5aa287df Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:58 -0400 Subject: [PATCH 0268/1334] acpi: arm/virt: build_spcr: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. while at it, replace packed structure with endian agnostic build_append_FOO() API. PS: Spec is Microsoft hosted, however 1.02 is no where to be found (MS lists only the current revision) and the current revision is 1.07, so bring comments in line with 1.07 as this is the only available spec. There is no content change between originally implemented 1.02 (using QEMU code as reference) and 1.07. The only change is renaming 'Reserved2' field to 'Language', with the same 0 value. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-32-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 68 ++++++++++++++++++++++--------------- include/hw/acpi/acpi-defs.h | 32 ----------------- 2 files changed, 41 insertions(+), 59 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 7b8706b305..7b79fae0ad 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -453,39 +453,53 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) g_array_free(its_idmaps, true); } +/* + * Serial Port Console Redirection Table (SPCR) + * Rev: 1.07 + */ static void build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { - AcpiSerialPortConsoleRedirection *spcr; - const MemMapEntry *uart_memmap = &vms->memmap[VIRT_UART]; - int irq = vms->irqmap[VIRT_UART] + ARM_SPI_BASE; - int spcr_start = table_data->len; + AcpiTable table = { .sig = "SPCR", .rev = 2, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; - spcr = acpi_data_push(table_data, sizeof(*spcr)); + acpi_table_begin(&table, table_data); - spcr->interface_type = 0x3; /* ARM PL011 UART */ + /* Interface Type */ + build_append_int_noprefix(table_data, 3, 1); /* ARM PL011 UART */ + build_append_int_noprefix(table_data, 0, 3); /* Reserved */ + /* Base Address */ + build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 8, 0, 1, + vms->memmap[VIRT_UART].base); + /* Interrupt Type */ + build_append_int_noprefix(table_data, + (1 << 3) /* Bit[3] ARMH GIC interrupt */, 1); + build_append_int_noprefix(table_data, 0, 1); /* IRQ */ + /* Global System Interrupt */ + build_append_int_noprefix(table_data, + vms->irqmap[VIRT_UART] + ARM_SPI_BASE, 4); + build_append_int_noprefix(table_data, 3 /* 9600 */, 1); /* Baud Rate */ + build_append_int_noprefix(table_data, 0 /* No Parity */, 1); /* Parity */ + /* Stop Bits */ + build_append_int_noprefix(table_data, 1 /* 1 Stop bit */, 1); + /* Flow Control */ + build_append_int_noprefix(table_data, + (1 << 1) /* RTS/CTS hardware flow control */, 1); + /* Terminal Type */ + build_append_int_noprefix(table_data, 0 /* VT100 */, 1); + build_append_int_noprefix(table_data, 0, 1); /* Language */ + /* PCI Device ID */ + build_append_int_noprefix(table_data, 0xffff /* not a PCI device*/, 2); + /* PCI Vendor ID */ + build_append_int_noprefix(table_data, 0xffff /* not a PCI device*/, 2); + build_append_int_noprefix(table_data, 0, 1); /* PCI Bus Number */ + build_append_int_noprefix(table_data, 0, 1); /* PCI Device Number */ + build_append_int_noprefix(table_data, 0, 1); /* PCI Function Number */ + build_append_int_noprefix(table_data, 0, 4); /* PCI Flags */ + build_append_int_noprefix(table_data, 0, 1); /* PCI Segment */ + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ - spcr->base_address.space_id = AML_AS_SYSTEM_MEMORY; - spcr->base_address.bit_width = 8; - spcr->base_address.bit_offset = 0; - spcr->base_address.access_width = 1; - spcr->base_address.address = cpu_to_le64(uart_memmap->base); - - spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */ - spcr->gsi = cpu_to_le32(irq); /* Global System Interrupt */ - - spcr->baud = 3; /* Baud Rate: 3 = 9600 */ - spcr->parity = 0; /* No Parity */ - spcr->stopbits = 1; /* 1 Stop bit */ - spcr->flowctrl = (1 << 1); /* Bit[1] = RTS/CTS hardware flow control */ - spcr->term_type = 0; /* Terminal Type: 0 = VT100 */ - - spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */ - spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */ - - build_header(linker, table_data, (void *)(table_data->data + spcr_start), - "SPCR", table_data->len - spcr_start, 2, vms->oem_id, - vms->oem_table_id); + acpi_table_end(linker, &table); } /* diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 6f2f08a9de..012c4ffb3a 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -117,38 +117,6 @@ typedef struct AcpiFadtData { #define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0) #define ACPI_FADT_ARM_PSCI_USE_HVC (1 << 1) -/* - * Serial Port Console Redirection Table (SPCR), Rev. 1.02 - * - * For .interface_type see Debug Port Table 2 (DBG2) serial port - * subtypes in Table 3, Rev. May 22, 2012 - */ -struct AcpiSerialPortConsoleRedirection { - ACPI_TABLE_HEADER_DEF - uint8_t interface_type; - uint8_t reserved1[3]; - struct AcpiGenericAddress base_address; - uint8_t interrupt_types; - uint8_t irq; - uint32_t gsi; - uint8_t baud; - uint8_t parity; - uint8_t stopbits; - uint8_t flowctrl; - uint8_t term_type; - uint8_t reserved2; - uint16_t pci_device_id; - uint16_t pci_vendor_id; - uint8_t pci_bus; - uint8_t pci_slot; - uint8_t pci_func; - uint32_t pci_flags; - uint8_t pci_seg; - uint32_t reserved3; -} QEMU_PACKED; -typedef struct AcpiSerialPortConsoleRedirection - AcpiSerialPortConsoleRedirection; - /* * ACPI 1.0 Firmware ACPI Control Structure (FACS) */ From 41041e57085a9454c45a6a751babfca70306f361 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:27:59 -0400 Subject: [PATCH 0269/1334] acpi: arm/virt: build_gtdt: use acpi_table_begin()/acpi_table_end() instead of build_header() it replaces error-prone pointer arithmetic for build_header() API, with 2 calls to start and finish table creation, which hides offsets magic from API user. while at it, replace packed structure with endian agnostic build_append_FOO() API. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-33-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 75 ++++++++++++++++++++++++------------- include/hw/acpi/acpi-defs.h | 25 ------------- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 7b79fae0ad..6cec97352b 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -559,40 +559,61 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_table_end(linker, &table); } -/* GTDT */ +/* + * ACPI spec, Revision 5.1 + * 5.2.24 Generic Timer Description Table (GTDT) + */ static void build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); - int gtdt_start = table_data->len; - AcpiGenericTimerTable *gtdt; - uint32_t irqflags; + /* + * Table 5-117 Flag Definitions + * set only "Timer interrupt Mode" and assume "Timer Interrupt + * polarity" bit as '0: Interrupt is Active high' + */ + uint32_t irqflags = vmc->claim_edge_triggered_timers ? + 1 : /* Interrupt is Edge triggered */ + 0; /* Interrupt is Level triggered */ + AcpiTable table = { .sig = "GTDT", .rev = 2, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; - if (vmc->claim_edge_triggered_timers) { - irqflags = ACPI_GTDT_INTERRUPT_MODE_EDGE; - } else { - irqflags = ACPI_GTDT_INTERRUPT_MODE_LEVEL; - } + acpi_table_begin(&table, table_data); - gtdt = acpi_data_push(table_data, sizeof *gtdt); - /* The interrupt values are the same with the device tree when adding 16 */ - gtdt->secure_el1_interrupt = cpu_to_le32(ARCH_TIMER_S_EL1_IRQ + 16); - gtdt->secure_el1_flags = cpu_to_le32(irqflags); + /* CntControlBase Physical Address */ + /* FIXME: invalid value, should be 0xFFFFFFFFFFFFFFFF if not impl. ? */ + build_append_int_noprefix(table_data, 0, 8); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + /* + * FIXME: clarify comment: + * The interrupt values are the same with the device tree when adding 16 + */ + /* Secure EL1 timer GSIV */ + build_append_int_noprefix(table_data, ARCH_TIMER_S_EL1_IRQ + 16, 4); + /* Secure EL1 timer Flags */ + build_append_int_noprefix(table_data, irqflags, 4); + /* Non-Secure EL1 timer GSIV */ + build_append_int_noprefix(table_data, ARCH_TIMER_NS_EL1_IRQ + 16, 4); + /* Non-Secure EL1 timer Flags */ + build_append_int_noprefix(table_data, irqflags | + 1UL << 2, /* Always-on Capability */ + 4); + /* Virtual timer GSIV */ + build_append_int_noprefix(table_data, ARCH_TIMER_VIRT_IRQ + 16, 4); + /* Virtual Timer Flags */ + build_append_int_noprefix(table_data, irqflags, 4); + /* Non-Secure EL2 timer GSIV */ + build_append_int_noprefix(table_data, ARCH_TIMER_NS_EL2_IRQ + 16, 4); + /* Non-Secure EL2 timer Flags */ + build_append_int_noprefix(table_data, irqflags, 4); + /* CntReadBase Physical address */ + build_append_int_noprefix(table_data, 0, 8); + /* Platform Timer Count */ + build_append_int_noprefix(table_data, 0, 4); + /* Platform Timer Offset */ + build_append_int_noprefix(table_data, 0, 4); - gtdt->non_secure_el1_interrupt = cpu_to_le32(ARCH_TIMER_NS_EL1_IRQ + 16); - gtdt->non_secure_el1_flags = cpu_to_le32(irqflags | - ACPI_GTDT_CAP_ALWAYS_ON); - - gtdt->virtual_timer_interrupt = cpu_to_le32(ARCH_TIMER_VIRT_IRQ + 16); - gtdt->virtual_timer_flags = cpu_to_le32(irqflags); - - gtdt->non_secure_el2_interrupt = cpu_to_le32(ARCH_TIMER_NS_EL2_IRQ + 16); - gtdt->non_secure_el2_flags = cpu_to_le32(irqflags); - - build_header(linker, table_data, - (void *)(table_data->data + gtdt_start), "GTDT", - table_data->len - gtdt_start, 2, vms->oem_id, - vms->oem_table_id); + acpi_table_end(linker, &table); } /* diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 012c4ffb3a..0b375e7589 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -131,29 +131,4 @@ struct AcpiFacsDescriptorRev1 { } QEMU_PACKED; typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1; -/* - * Generic Timer Description Table (GTDT) - */ -#define ACPI_GTDT_INTERRUPT_MODE_LEVEL (0 << 0) -#define ACPI_GTDT_INTERRUPT_MODE_EDGE (1 << 0) -#define ACPI_GTDT_CAP_ALWAYS_ON (1 << 2) - -struct AcpiGenericTimerTable { - ACPI_TABLE_HEADER_DEF - uint64_t counter_block_addresss; - uint32_t reserved; - uint32_t secure_el1_interrupt; - uint32_t secure_el1_flags; - uint32_t non_secure_el1_interrupt; - uint32_t non_secure_el1_flags; - uint32_t virtual_timer_interrupt; - uint32_t virtual_timer_flags; - uint32_t non_secure_el2_interrupt; - uint32_t non_secure_el2_flags; - uint64_t counter_read_block_address; - uint32_t platform_timer_count; - uint32_t platform_timer_offset; -} QEMU_PACKED; -typedef struct AcpiGenericTimerTable AcpiGenericTimerTable; - #endif From cf68410bc98914ee9be5cb5a127a1212056ab83a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:28:00 -0400 Subject: [PATCH 0270/1334] acpi: build_facs: use build_append_int_noprefix() API to compose table Drop usage of packed structures and explicit endian conversions when building table and use endian agnostic build_append_int_noprefix() API to build it. Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-34-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 18 ++++++++++++++---- include/hw/acpi/acpi-defs.h | 14 -------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index d9e2b5dc30..81418b7911 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -345,13 +345,23 @@ static void acpi_align_size(GArray *blob, unsigned align) g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); } -/* FACS */ +/* + * ACPI spec 1.0b, + * 5.2.6 Firmware ACPI Control Structure + */ static void build_facs(GArray *table_data) { - AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs); - memcpy(&facs->signature, "FACS", 4); - facs->length = cpu_to_le32(sizeof(*facs)); + const char *sig = "FACS"; + const uint8_t reserved[40] = {}; + + g_array_append_vals(table_data, sig, 4); /* Signature */ + build_append_int_noprefix(table_data, 64, 4); /* Length */ + build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */ + build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */ + build_append_int_noprefix(table_data, 0, 4); /* Global Lock */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + g_array_append_vals(table_data, reserved, 40); /* Reserved */ } static void build_append_pcihp_notify_entry(Aml *method, int slot) diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 0b375e7589..1a0774edd6 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -117,18 +117,4 @@ typedef struct AcpiFadtData { #define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0) #define ACPI_FADT_ARM_PSCI_USE_HVC (1 << 1) -/* - * ACPI 1.0 Firmware ACPI Control Structure (FACS) - */ -struct AcpiFacsDescriptorRev1 { - uint32_t signature; /* ACPI Signature */ - uint32_t length; /* Length of structure, in bytes */ - uint32_t hardware_signature; /* Hardware configuration signature */ - uint32_t firmware_waking_vector; /* ACPI OS waking vector */ - uint32_t global_lock; /* Global Lock */ - uint32_t flags; - uint8_t resverved3 [40]; /* Reserved - must be zero */ -} QEMU_PACKED; -typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1; - #endif From 538c2ecf1ab73fb2ecc2d50ef23b116e634b5392 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:28:01 -0400 Subject: [PATCH 0271/1334] acpi: remove no longer used build_header() Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-35-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 23 ----------------------- include/hw/acpi/acpi-defs.h | 25 ------------------------- include/hw/acpi/aml-build.h | 4 ---- 3 files changed, 52 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index c0f339847a..76af0ebaf9 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1754,29 +1754,6 @@ void acpi_table_end(BIOSLinker *linker, AcpiTable *desc) desc->table_offset, table_len, desc->table_offset + checksum_offset); } -void -build_header(BIOSLinker *linker, GArray *table_data, - AcpiTableHeader *h, const char *sig, int len, uint8_t rev, - const char *oem_id, const char *oem_table_id) -{ - unsigned tbl_offset = (char *)h - table_data->data; - unsigned checksum_offset = (char *)&h->checksum - table_data->data; - memcpy(&h->signature, sig, 4); - h->length = cpu_to_le32(len); - h->revision = rev; - - strpadcpy((char *)h->oem_id, sizeof h->oem_id, oem_id, ' '); - strpadcpy((char *)h->oem_table_id, sizeof h->oem_table_id, - oem_table_id, ' '); - - h->oem_revision = cpu_to_le32(1); - memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME8, 4); - h->asl_compiler_revision = cpu_to_le32(1); - /* Checksum to be filled in by Guest linker */ - bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE, - tbl_offset, len, checksum_offset); -} - void *acpi_data_push(GArray *table_data, unsigned size) { unsigned off = table_data->len; diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 1a0774edd6..ee733840aa 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -48,31 +48,6 @@ typedef struct AcpiRsdpData { unsigned *xsdt_tbl_offset; } AcpiRsdpData; -/* Table structure from Linux kernel (the ACPI tables are under the - BSD license) */ - - -#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \ - uint32_t signature; /* ACPI signature (4 ASCII characters) */ \ - uint32_t length; /* Length of table, in bytes, including header */ \ - uint8_t revision; /* ACPI Specification minor version # */ \ - uint8_t checksum; /* To make sum of entire table == 0 */ \ - uint8_t oem_id[6] \ - QEMU_NONSTRING; /* OEM identification */ \ - uint8_t oem_table_id[8] \ - QEMU_NONSTRING; /* OEM table identification */ \ - uint32_t oem_revision; /* OEM revision number */ \ - uint8_t asl_compiler_id[4] \ - QEMU_NONSTRING; /* ASL compiler vendor ID */ \ - uint32_t asl_compiler_revision; /* ASL compiler revision number */ - - -/* ACPI common table header */ -struct AcpiTableHeader { - ACPI_TABLE_HEADER_DEF -} QEMU_PACKED; -typedef struct AcpiTableHeader AcpiTableHeader; - struct AcpiGenericAddress { uint8_t space_id; /* Address space where struct or register exists */ uint8_t bit_width; /* Size in bits of given register */ diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 6e1f42e119..3cf6f2c1b9 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -444,10 +444,6 @@ void acpi_table_begin(AcpiTable *desc, GArray *array); */ void acpi_table_end(BIOSLinker *linker, AcpiTable *table); -void -build_header(BIOSLinker *linker, GArray *table_data, - AcpiTableHeader *h, const char *sig, int len, uint8_t rev, - const char *oem_id, const char *oem_table_id); void *acpi_data_push(GArray *table_data, unsigned size); unsigned acpi_data_len(GArray *table); void acpi_add_table(GArray *table_offsets, GArray *table_data); From a8a5768786f4182cb4f4b08e830150dc93d51964 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 24 Sep 2021 08:28:02 -0400 Subject: [PATCH 0272/1334] acpi: AcpiGenericAddress no longer used to map/access fields of MMIO, drop packed attribute Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Message-Id: <20210924122802.1455362-36-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/acpi/acpi-defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index ee733840aa..c97e8633ad 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -55,7 +55,7 @@ struct AcpiGenericAddress { uint8_t access_width; /* ACPI 3.0: Minimum Access size (ACPI 3.0), ACPI 2.0: Reserved, Table 5-1 */ uint64_t address; /* 64-bit address of struct or register */ -} QEMU_PACKED; +}; typedef struct AcpiFadtData { struct AcpiGenericAddress pm1a_cnt; /* PM1a_CNT_BLK */ From 9f29e872d5b3973003701401cf659cfb71c95013 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Thu, 16 Sep 2021 18:58:36 +0530 Subject: [PATCH 0273/1334] bios-tables-test: allow changes in DSDT ACPI tables for q35 We are going to commit a change to fix IO address range allocated for acpi pci hotplug in q35. This affects DSDT tables. This change allows DSDT table modification so that unit tests are not broken. Signed-off-by: Ani Sinha Acked-by: Igor Mammedov Message-Id: <20210916132838.3469580-2-ani@anisinha.ca> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..c06da38af3 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,13 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/DSDT", +"tests/data/acpi/q35/DSDT.tis.tpm12", +"tests/data/acpi/q35/DSDT.tis.tpm2", +"tests/data/acpi/q35/DSDT.bridge", +"tests/data/acpi/q35/DSDT.mmio64", +"tests/data/acpi/q35/DSDT.ipmibt", +"tests/data/acpi/q35/DSDT.cphp", +"tests/data/acpi/q35/DSDT.memhp", +"tests/data/acpi/q35/DSDT.numamem", +"tests/data/acpi/q35/DSDT.nohpet", +"tests/data/acpi/q35/DSDT.dimmpxm", +"tests/data/acpi/q35/DSDT.acpihmat", From 0e780da76a6fe283a20283856718bca3986c104f Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Thu, 16 Sep 2021 18:58:37 +0530 Subject: [PATCH 0274/1334] hw/i386/acpi: fix conflicting IO address range for acpi pci hotplug in q35 Change caf108bc58790 ("hw/i386/acpi-build: Add ACPI PCI hot-plug methods to Q35") selects an IO address range for acpi based PCI hotplug for q35 arbitrarily. It starts at address 0x0cc4 and ends at 0x0cdb. At the time when the patch was written but the final version of the patch was not yet pushed upstream, this address range was free and did not conflict with any other IO address ranges. However, with the following change, this address range was no longer conflict free as in this change, the IO address range (value of ACPI_PCIHP_SIZE) was incremented by four bytes: b32bd763a1ca92 ("pci: introduce acpi-index property for PCI device") This can be seen from the output of QMP command 'info mtree' : 0000000000000600-0000000000000603 (prio 0, i/o): acpi-evt 0000000000000604-0000000000000605 (prio 0, i/o): acpi-cnt 0000000000000608-000000000000060b (prio 0, i/o): acpi-tmr 0000000000000620-000000000000062f (prio 0, i/o): acpi-gpe0 0000000000000630-0000000000000637 (prio 0, i/o): acpi-smi 0000000000000cc4-0000000000000cdb (prio 0, i/o): acpi-pci-hotplug 0000000000000cd8-0000000000000ce3 (prio 0, i/o): acpi-cpu-hotplug It shows that there is a region of conflict between IO regions of acpi pci hotplug and acpi cpu hotplug. Unfortunately, the change caf108bc58790 did not update the IO address range appropriately before it was pushed upstream to accommodate the increased length of the IO address space introduced in change b32bd763a1ca92. Due to this bug, windows guests complain 'This device cannot find enough free resources it can use' in the device manager panel for extended IO buses. This issue also breaks the correct functioning of pci hotplug as the following shows that the IO space for pci hotplug has been truncated: (qemu) info mtree -f FlatView #0 AS "I/O", root: io Root memory region: io 0000000000000cc4-0000000000000cd7 (prio 0, i/o): acpi-pci-hotplug 0000000000000cd8-0000000000000cf7 (prio 0, i/o): acpi-cpu-hotplug Therefore, in this fix, we adjust the IO address range for the acpi pci hotplug so that it does not conflict with cpu hotplug and there is no truncation of IO spaces. The starting IO address of PCI hotplug region has been decremented by four bytes in order to accommodate four byte increment in the IO address space introduced by change b32bd763a1ca92 ("pci: introduce acpi-index property for PCI device") After fixing, the following are the corrected IO ranges: 0000000000000600-0000000000000603 (prio 0, i/o): acpi-evt 0000000000000604-0000000000000605 (prio 0, i/o): acpi-cnt 0000000000000608-000000000000060b (prio 0, i/o): acpi-tmr 0000000000000620-000000000000062f (prio 0, i/o): acpi-gpe0 0000000000000630-0000000000000637 (prio 0, i/o): acpi-smi 0000000000000cc0-0000000000000cd7 (prio 0, i/o): acpi-pci-hotplug 0000000000000cd8-0000000000000ce3 (prio 0, i/o): acpi-cpu-hotplug This change has been tested using a Windows Server 2019 guest VM. Windows no longer complains after this change. Fixes: caf108bc58790 ("hw/i386/acpi-build: Add ACPI PCI hot-plug methods to Q35") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/561 Signed-off-by: Ani Sinha Reviewed-by: Igor Mammedov Reviewed-by: Julia Suvorova Message-Id: <20210916132838.3469580-3-ani@anisinha.ca> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/acpi/ich9.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index a329ce43ab..f04f1791bd 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -29,7 +29,7 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/acpi/tco.h" -#define ACPI_PCIHP_ADDR_ICH9 0x0cc4 +#define ACPI_PCIHP_ADDR_ICH9 0x0cc0 typedef struct ICH9LPCPMRegs { /* From 500eb21cff08dfb0478db9b34f2fdba69eb31496 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Thu, 16 Sep 2021 18:58:38 +0530 Subject: [PATCH 0275/1334] bios-tables-test: Update ACPI DSDT table golden blobs for q35 We have modified the IO address range for ACPI pci hotplug in q35. See change: 5adcc9e39e6a5 ("hw/i386/acpi: fix conflicting IO address range for acpi pci hotplug in q35") The ACPI DSDT table golden blobs must be regenrated in order to make the unit tests pass. This change updates the golden ACPI DSDT table blobs. Following is the ASL diff between the blobs: @@ -1,30 +1,30 @@ /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20190509 (64-bit version) * Copyright (c) 2000 - 2019 Intel Corporation * * Disassembling to symbolic ASL+ operators * - * Disassembly of tests/data/acpi/q35/DSDT, Tue Sep 14 09:04:06 2021 + * Disassembly of /tmp/aml-52DP90, Tue Sep 14 09:04:06 2021 * * Original Table Header: * Signature "DSDT" * Length 0x00002061 (8289) * Revision 0x01 **** 32-bit table (V1), no 64-bit math support - * Checksum 0xE5 + * Checksum 0xF9 * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) * Compiler ID "BXPC" * Compiler Version 0x00000001 (1) */ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) { Scope (\) { OperationRegion (DBG, SystemIO, 0x0402, One) Field (DBG, ByteAcc, NoLock, Preserve) { DBGB, 8 } @@ -226,46 +226,46 @@ Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, 0x0070, // Range Minimum 0x0070, // Range Maximum 0x01, // Alignment 0x08, // Length ) IRQNoFlags () {8} }) } } Scope (_SB.PCI0) { - OperationRegion (PCST, SystemIO, 0x0CC4, 0x08) + OperationRegion (PCST, SystemIO, 0x0CC0, 0x08) Field (PCST, DWordAcc, NoLock, WriteAsZeros) { PCIU, 32, PCID, 32 } - OperationRegion (SEJ, SystemIO, 0x0CCC, 0x04) + OperationRegion (SEJ, SystemIO, 0x0CC8, 0x04) Field (SEJ, DWordAcc, NoLock, WriteAsZeros) { B0EJ, 32 } - OperationRegion (BNMR, SystemIO, 0x0CD4, 0x08) + OperationRegion (BNMR, SystemIO, 0x0CD0, 0x08) Field (BNMR, DWordAcc, NoLock, WriteAsZeros) { BNUM, 32, PIDX, 32 } Mutex (BLCK, 0x00) Method (PCEJ, 2, NotSerialized) { Acquire (BLCK, 0xFFFF) BNUM = Arg0 B0EJ = (One << Arg1) Release (BLCK) Return (Zero) } @@ -3185,34 +3185,34 @@ 0x0620, // Range Minimum 0x0620, // Range Maximum 0x01, // Alignment 0x10, // Length ) }) } Device (PHPR) { Name (_HID, "PNP0A06" /* Generic Container Device */) // _HID: Hardware ID Name (_UID, "PCI Hotplug resources") // _UID: Unique ID Name (_STA, 0x0B) // _STA: Status Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, - 0x0CC4, // Range Minimum - 0x0CC4, // Range Maximum + 0x0CC0, // Range Minimum + 0x0CC0, // Range Maximum 0x01, // Alignment 0x18, // Length ) }) } } Scope (\) { Name (_S3, Package (0x04) // _S3_: S3 System State { One, One, Zero, Zero }) Signed-off-by: Ani Sinha Acked-by: Igor Mammedov Message-Id: <20210916132838.3469580-4-ani@anisinha.ca> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/DSDT | Bin 8289 -> 8289 bytes tests/data/acpi/q35/DSDT.acpihmat | Bin 9614 -> 9614 bytes tests/data/acpi/q35/DSDT.bridge | Bin 11003 -> 11003 bytes tests/data/acpi/q35/DSDT.cphp | Bin 8753 -> 8753 bytes tests/data/acpi/q35/DSDT.dimmpxm | Bin 9943 -> 9943 bytes tests/data/acpi/q35/DSDT.ipmibt | Bin 8364 -> 8364 bytes tests/data/acpi/q35/DSDT.memhp | Bin 9648 -> 9648 bytes tests/data/acpi/q35/DSDT.mmio64 | Bin 9419 -> 9419 bytes tests/data/acpi/q35/DSDT.nohpet | Bin 8147 -> 8147 bytes tests/data/acpi/q35/DSDT.numamem | Bin 8295 -> 8295 bytes tests/data/acpi/q35/DSDT.tis.tpm12 | Bin 8894 -> 8894 bytes tests/data/acpi/q35/DSDT.tis.tpm2 | Bin 8894 -> 8894 bytes tests/qtest/bios-tables-test-allowed-diff.h | 12 ------------ 13 files changed, 12 deletions(-) diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT index 842533f53e6db40935c3cdecd1d182edba6c17d4..281fc82c03b2562d2e6b7caec0d817b034a47138 100644 GIT binary patch delta 65 zcmaFp@X&$FCD+tn9LO9j!+e0}z~trfy8v~b6W9O% delta 65 zcmaFp@X&$FCD+to9LO9j!+eD2$mHeny8v@q6W9O% diff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/q35/DSDT.acpihmat index 8d00f2ea0dd78f962e136273d68cb0c568e43c27..8c1e05a11a328ec1cc6f86e36e52c28f41f9744e 100644 GIT binary patch delta 65 zcmeD4?(^ny33dtTQ)OUa+_sU6pPBK%WGQA@sS`Y0EYXeJ!LDBM&Q1odUJB6-PJX^Y VjNBJC2QoJ(G9TbMF!_qgE&x%Q6Po}4 delta 65 zcmeD4?(^ny33dtTQ)OUaT(pskpPBK+tn9LO9j!+e0}z~<%hA>06yixa5; delta 66 zcmewz`a6`%CDF$oF?J&tKQrTj$x_U+QYU!0SfU%bgI&Giot+F^y%eGwocw%) V7`ZQO4rErAV?MxhU~;+QE&wzZ5?TNN delta 65 zcmdn!veAXhCDF$o(Q_jgKQrTz$x_U+QfGL$SfU%bgI&Giot+F^y%eGwocw%) V7`d-(4rErAV?M%jWOBLUE&wso5?TNN diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm index b062e30117f955c7a2ac9629a0512324bbd28bf2..fe5820d93d057ef09a001662369b15afbc5b87e2 100644 GIT binary patch delta 65 zcmccad)=4ICD+tn9LRi9iTMD}fyuIJy8v+to9LRi9iTMc6k;$@Zy8v&d6QKYA diff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/q35/DSDT.ipmibt index 1c5737692f56fc678e685a4ad0bb1df38d487a14..631741065860fd5036aa303904dabd1d2839f9c6 100644 GIT binary patch delta 89 zcmZ4ExWnjRFDA!6D88&YqzPK*B{Kx*^!rE1r@2 p1P>QWbR##2IIZ=^79R1njRFDA!6D88&YqzPK*B{Kx*^!rE1r@2 p3=bDebR##2IIZ=^79R1+tn9LO9g!+e0}z~r^^y8vk#6QBS9 delta 65 zcmaFv@Z5pRCDxg*6QBS9 diff --git a/tests/data/acpi/q35/DSDT.tis.tpm12 b/tests/data/acpi/q35/DSDT.tis.tpm12 index 6735e73971c6be95deceb23051a78af6a4573bd8..c96b5277a14ae98174408d690d6e0246bd932623 100644 GIT binary patch delta 66 zcmdnzy3du%CD+tn9LO9m!+e0}z~;^J+{^%EauT!v delta 66 zcmdnzy3du%CD+to9LO9m!+eD2$mY%R+{^%CE)ui= diff --git a/tests/data/acpi/q35/DSDT.tis.tpm2 b/tests/data/acpi/q35/DSDT.tis.tpm2 index d1433e3c14570bbd17b029a9aec6bc53134c3b7d..c92d4d29c79352a60974ea9f665d0b9a410a4bac 100644 GIT binary patch delta 66 zcmdnzy3du%CD Date: Tue, 14 Sep 2021 14:17:16 +0100 Subject: [PATCH 0276/1334] virtio-balloon: Fix page-poison subsection name The subsection name for page-poison was typo'd as: vitio-balloon-device/page-poison Note the missing 'r' in virtio. When we have a machine type that enables page poison, and the guest enables it (which needs a new kernel), things fail rather unpredictably. The fallout from this is that most of the other subsections fail to load, including things like the feature bits in the device, one possible fallout is that the physical addresses of the queues then get aligned differently and we fail with an error about last_avail_idx being wrong. It's not obvious to me why this doesn't produce a more obvious failure, but virtio's vmstate loading is a bit open-coded. Fixes: 7483cbbaf82 ("virtio-balloon: Implement support for page poison reporting feature") bz: https://bugzilla.redhat.com/show_bug.cgi?id=1984401 Signed-off-by: Dr. David Alan Gilbert Message-Id: <20210914131716.102851-1-dgilbert@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: David Hildenbrand --- hw/virtio/virtio-balloon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 5a69dce35d..c6962fcbfe 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -852,7 +852,7 @@ static const VMStateDescription vmstate_virtio_balloon_free_page_hint = { }; static const VMStateDescription vmstate_virtio_balloon_page_poison = { - .name = "vitio-balloon-device/page-poison", + .name = "virtio-balloon-device/page-poison", .version_id = 1, .minimum_version_id = 1, .needed = virtio_balloon_page_poison_support, From 5c243345236b058a3d84c8cbc62802f3fffb273c Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 24 Jun 2021 19:04:15 +0800 Subject: [PATCH 0277/1334] nvdimm: release the correct device list Signed-off-by: Li Zhijian Message-Id: <20210624110415.187164-1-lizhijian@cn.fujitsu.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Igor Mammedov --- hw/acpi/nvdimm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 5f9b552d6a..0d43da19ea 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -339,10 +339,10 @@ nvdimm_build_structure_caps(GArray *structures, uint32_t capabilities) static GArray *nvdimm_build_device_structure(NVDIMMState *state) { - GSList *device_list = nvdimm_get_device_list(); + GSList *device_list, *list = nvdimm_get_device_list(); GArray *structures = g_array_new(false, true /* clear */, 1); - for (; device_list; device_list = device_list->next) { + for (device_list = list; device_list; device_list = device_list->next) { DeviceState *dev = device_list->data; /* build System Physical Address Range Structure. */ @@ -357,7 +357,7 @@ static GArray *nvdimm_build_device_structure(NVDIMMState *state) /* build NVDIMM Control Region Structure. */ nvdimm_build_structure_dcr(structures, dev); } - g_slist_free(device_list); + g_slist_free(list); if (state->persistence) { nvdimm_build_structure_caps(structures, state->persistence); @@ -1333,9 +1333,9 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, void nvdimm_build_srat(GArray *table_data) { - GSList *device_list = nvdimm_get_device_list(); + GSList *device_list, *list = nvdimm_get_device_list(); - for (; device_list; device_list = device_list->next) { + for (device_list = list; device_list; device_list = device_list->next) { DeviceState *dev = device_list->data; Object *obj = OBJECT(dev); uint64_t addr, size; @@ -1348,7 +1348,7 @@ void nvdimm_build_srat(GArray *table_data) build_srat_memory(table_data, addr, size, node, MEM_AFFINITY_ENABLED | MEM_AFFINITY_NON_VOLATILE); } - g_slist_free(device_list); + g_slist_free(list); } void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, From 64cba40c4480b4716da7d26fbedc97f43aa9f8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Sep 2021 19:56:46 +0200 Subject: [PATCH 0278/1334] hw/i386/amd_iommu: Rename amdviPCI TypeInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per 'QEMU Coding Style': Naming ====== Variables are lower_case_with_underscores; easy to type and read. Rename amdviPCI variable as amdvi_pci. amdviPCI_register_types() register more than PCI types: TYPE_AMD_IOMMU_DEVICE inherits TYPE_X86_IOMMU_DEVICE which itself inherits TYPE_SYS_BUS_DEVICE. Rename it more generically as amdvi_register_types(). Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210926175648.1649075-2-f4bug@amsat.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 2801dff97c..0c994facde 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1621,7 +1621,7 @@ static const TypeInfo amdvi = { .class_init = amdvi_class_init }; -static const TypeInfo amdviPCI = { +static const TypeInfo amdvi_pci = { .name = TYPE_AMD_IOMMU_PCI, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AMDVIPCIState), @@ -1645,11 +1645,11 @@ static const TypeInfo amdvi_iommu_memory_region_info = { .class_init = amdvi_iommu_memory_region_class_init, }; -static void amdviPCI_register_types(void) +static void amdvi_register_types(void) { - type_register_static(&amdviPCI); + type_register_static(&amdvi_pci); type_register_static(&amdvi); type_register_static(&amdvi_iommu_memory_region_info); } -type_init(amdviPCI_register_types); +type_init(amdvi_register_types); From 8f6b7309c423a876dbb26975fdb48f582ebcabcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Sep 2021 19:56:47 +0200 Subject: [PATCH 0279/1334] hw/i386/amd_iommu: Rename SysBus specific functions as amdvi_sysbus_X() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Various functions are SysBus specific. Rename them using the consistent amdvi_sysbus_XXX() pattern, to differentiate them from PCI specific functions (which we'll add in the next commit). Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210926175648.1649075-3-f4bug@amsat.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 0c994facde..9014690ba3 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1526,7 +1526,7 @@ static void amdvi_init(AMDVIState *s) AMDVI_MAX_PH_ADDR | AMDVI_MAX_GVA_ADDR | AMDVI_MAX_VA_ADDR); } -static void amdvi_reset(DeviceState *dev) +static void amdvi_sysbus_reset(DeviceState *dev) { AMDVIState *s = AMD_IOMMU_DEVICE(dev); @@ -1534,7 +1534,7 @@ static void amdvi_reset(DeviceState *dev) amdvi_init(s); } -static void amdvi_realize(DeviceState *dev, Error **errp) +static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) { int ret = 0; AMDVIState *s = AMD_IOMMU_DEVICE(dev); @@ -1585,27 +1585,27 @@ static void amdvi_realize(DeviceState *dev, Error **errp) amdvi_init(s); } -static const VMStateDescription vmstate_amdvi = { +static const VMStateDescription vmstate_amdvi_sysbus = { .name = "amd-iommu", .unmigratable = 1 }; -static void amdvi_instance_init(Object *klass) +static void amdvi_sysbus_instance_init(Object *klass) { AMDVIState *s = AMD_IOMMU_DEVICE(klass); object_initialize(&s->pci, sizeof(s->pci), TYPE_AMD_IOMMU_PCI); } -static void amdvi_class_init(ObjectClass *klass, void* data) +static void amdvi_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); X86IOMMUClass *dc_class = X86_IOMMU_DEVICE_CLASS(klass); - dc->reset = amdvi_reset; - dc->vmsd = &vmstate_amdvi; + dc->reset = amdvi_sysbus_reset; + dc->vmsd = &vmstate_amdvi_sysbus; dc->hotpluggable = false; - dc_class->realize = amdvi_realize; + dc_class->realize = amdvi_sysbus_realize; dc_class->int_remap = amdvi_int_remap; /* Supported by the pc-q35-* machine types */ dc->user_creatable = true; @@ -1613,12 +1613,12 @@ static void amdvi_class_init(ObjectClass *klass, void* data) dc->desc = "AMD IOMMU (AMD-Vi) DMA Remapping device"; } -static const TypeInfo amdvi = { +static const TypeInfo amdvi_sysbus = { .name = TYPE_AMD_IOMMU_DEVICE, .parent = TYPE_X86_IOMMU_DEVICE, .instance_size = sizeof(AMDVIState), - .instance_init = amdvi_instance_init, - .class_init = amdvi_class_init + .instance_init = amdvi_sysbus_instance_init, + .class_init = amdvi_sysbus_class_init }; static const TypeInfo amdvi_pci = { @@ -1648,7 +1648,7 @@ static const TypeInfo amdvi_iommu_memory_region_info = { static void amdvi_register_types(void) { type_register_static(&amdvi_pci); - type_register_static(&amdvi); + type_register_static(&amdvi_sysbus); type_register_static(&amdvi_iommu_memory_region_info); } From 64bc656decc0fdf59e23753de8940c744b39b586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 26 Sep 2021 19:56:48 +0200 Subject: [PATCH 0280/1334] hw/i386/amd_iommu: Add description/category to TYPE_AMD_IOMMU_PCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE_AMD_IOMMU_PCI is user-creatable but not well described. Implement its class_init() handler to add it to the 'Misc devices' category, and add a description. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210926175648.1649075-4-f4bug@amsat.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 9014690ba3..9242a0d3ed 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1621,10 +1621,19 @@ static const TypeInfo amdvi_sysbus = { .class_init = amdvi_sysbus_class_init }; +static void amdvi_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->desc = "AMD IOMMU (AMD-Vi) DMA Remapping device"; +} + static const TypeInfo amdvi_pci = { .name = TYPE_AMD_IOMMU_PCI, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AMDVIPCIState), + .class_init = amdvi_pci_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, From e3acc2c1961cbe22ca474cd5da4163b7bbf7cea3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 5 Oct 2021 13:58:46 -0700 Subject: [PATCH 0281/1334] tests/docker/dockerfiles: Bump fedora-i386-cross to fedora 34 For unknown and unrepeatable reasons, the cross-i386-tci test has started failing. "Fix" this by updating the container to use fedora 34. Add sysprof-capture-devel as a new dependency of glib2-devel that was not correctly spelled out in the rpm rules. Use dnf update Just In Case -- there are presently out-of-date packages in the upstream docker registry. Reviewed-by: Paolo Bonzini Message-Id: <20211005205846.153724-1-richard.henderson@linaro.org> Signed-off-by: Richard Henderson --- tests/docker/dockerfiles/fedora-i386-cross.docker | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker index dbb8195eb1..84f2697b6c 100644 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -1,4 +1,5 @@ -FROM registry.fedoraproject.org/fedora:33 +FROM registry.fedoraproject.org/fedora:34 + ENV PACKAGES \ bzip2 \ ccache \ @@ -19,10 +20,11 @@ ENV PACKAGES \ nettle-devel.i686 \ perl-Test-Harness \ pixman-devel.i686 \ + sysprof-capture-devel.i686 \ zlib-devel.i686 ENV QEMU_CONFIGURE_OPTS --extra-cflags=-m32 --disable-vhost-user ENV PKG_CONFIG_PATH /usr/lib/pkgconfig -RUN dnf install -y $PACKAGES +RUN dnf update -y && dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt From 6a2b0fd171082eae19f1da043cf53b5a5a7b9c6c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Sep 2021 12:36:35 -0400 Subject: [PATCH 0282/1334] tests/docker: Remove fedora-i386-cross from DOCKER_PARTIAL_IMAGES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The image was upgraded to a full image in ee381b7fe146. This makes it possible to use docker-test@image syntax with this container. Signed-off-by: Richard Henderson Reviewed-by: Daniel P. Berrangé Message-Id: <20210930163636.721311-2-richard.henderson@linaro.org> --- tests/docker/Makefile.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index ff5d732889..0806c6f726 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -184,7 +184,7 @@ DOCKER_PARTIAL_IMAGES += debian-riscv64-cross DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-tricore-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross -DOCKER_PARTIAL_IMAGES += fedora-i386-cross fedora-cris-cross +DOCKER_PARTIAL_IMAGES += fedora-cris-cross # Rules for building linux-user powered images # From 08a13c4b247338329951238a6c47b94f70c387d2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Sep 2021 12:36:36 -0400 Subject: [PATCH 0283/1334] tests/docker: Fix fedora-i386-cross cross-compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By using PKG_CONFIG_PATH instead of PKG_CONFIG_LIBDIR, we were still including the 64-bit packages. Install pcre-devel.i686 to fill a missing glib2 dependency. By using --extra-cflags instead of --cpu, we incorrectly use the wrong probing during meson. Signed-off-by: Richard Henderson Reviewed-by: Richard W.M. Jones Reviewed-by: Daniel P. Berrangé Message-Id: <20210930163636.721311-3-richard.henderson@linaro.org> --- tests/docker/dockerfiles/fedora-i386-cross.docker | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker index 84f2697b6c..f62a71ce22 100644 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -18,13 +18,14 @@ ENV PACKAGES \ glibc-static.i686 \ gnutls-devel.i686 \ nettle-devel.i686 \ + pcre-devel.i686 \ perl-Test-Harness \ pixman-devel.i686 \ sysprof-capture-devel.i686 \ zlib-devel.i686 -ENV QEMU_CONFIGURE_OPTS --extra-cflags=-m32 --disable-vhost-user -ENV PKG_CONFIG_PATH /usr/lib/pkgconfig +ENV QEMU_CONFIGURE_OPTS --cpu=i386 --disable-vhost-user +ENV PKG_CONFIG_LIBDIR /usr/lib/pkgconfig RUN dnf update -y && dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt From db637f270b52f8c2a1c55e7e707532532295715c Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sun, 3 Oct 2021 23:42:42 +0200 Subject: [PATCH 0284/1334] tcg: add dup_const_tl wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dup_const always generates a uint64_t, which may exceed the size of a target_long (generating warnings with recent-enough compilers). To ensure that we can use dup_const both for 64bit and 32bit targets, this adds dup_const_tl, which either maps back to dup_const (for 64bit targets) or provides a similar implementation using 32bit constants. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philipp Tomsich Message-Id: <20211003214243.3813425-1-philipp.tomsich@vrull.eu> Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 44ccd86f3e..1bb6c0ce3e 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -1272,6 +1272,18 @@ uint64_t dup_const(unsigned vece, uint64_t c); : (qemu_build_not_reached_always(), 0)) \ : dup_const(VECE, C)) +#if TARGET_LONG_BITS == 64 +# define dup_const_tl dup_const +#else +# define dup_const_tl(VECE, C) \ + (__builtin_constant_p(VECE) \ + ? ( (VECE) == MO_8 ? 0x01010101ul * (uint8_t)(C) \ + : (VECE) == MO_16 ? 0x00010001ul * (uint16_t)(C) \ + : (VECE) == MO_32 ? 0x00000001ul * (uint32_t)(C) \ + : (qemu_build_not_reached_always(), 0)) \ + : (target_long)dup_const(VECE, C)) +#endif + /* * Memory helpers that will be used by TCG generated code. */ From c433e298d99228e41a78d480a505cfcc8c9ea067 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 25 Jul 2021 10:43:10 -1000 Subject: [PATCH 0285/1334] accel/tcg: Drop signness in tracing in cputlb.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are already inconsistent about whether or not MO_SIGN is set in trace_mem_get_info. Dropping it entirely allows some simplification. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 10 +++------- accel/tcg/user-exec.c | 45 ++++++------------------------------------- 2 files changed, 9 insertions(+), 46 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b1e5471f94..0a1fdbefdd 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2119,7 +2119,6 @@ static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, meminfo = trace_mem_get_info(op, mmu_idx, false); trace_guest_mem_before_exec(env_cpu(env), addr, meminfo); - op &= ~MO_SIGN; oi = make_memop_idx(op, mmu_idx); ret = full_load(env, addr, oi, retaddr); @@ -2137,8 +2136,7 @@ uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { - return (int8_t)cpu_load_helper(env, addr, mmu_idx, ra, MO_SB, - full_ldub_mmu); + return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra); } uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, @@ -2150,8 +2148,7 @@ uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { - return (int16_t)cpu_load_helper(env, addr, mmu_idx, ra, MO_BESW, - full_be_lduw_mmu); + return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra); } uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, @@ -2175,8 +2172,7 @@ uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { - return (int16_t)cpu_load_helper(env, addr, mmu_idx, ra, MO_LESW, - full_le_lduw_mmu); + return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra); } uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 8fed542622..8f2644f26e 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -899,13 +899,7 @@ uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr) int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr) { - int ret; - uint16_t meminfo = trace_mem_get_info(MO_SB, MMU_USER_IDX, false); - - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldsb_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); - return ret; + return (int8_t)cpu_ldub_data(env, ptr); } uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr) @@ -921,13 +915,7 @@ uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr) int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr) { - int ret; - uint16_t meminfo = trace_mem_get_info(MO_BESW, MMU_USER_IDX, false); - - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldsw_be_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); - return ret; + return (int16_t)cpu_lduw_be_data(env, ptr); } uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr) @@ -965,13 +953,7 @@ uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr) int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr) { - int ret; - uint16_t meminfo = trace_mem_get_info(MO_LESW, MMU_USER_IDX, false); - - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldsw_le_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); - return ret; + return (int16_t)cpu_lduw_le_data(env, ptr); } uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr) @@ -1008,12 +990,7 @@ uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) { - int ret; - - set_helper_retaddr(retaddr); - ret = cpu_ldsb_data(env, ptr); - clear_helper_retaddr(); - return ret; + return (int8_t)cpu_ldub_data_ra(env, ptr, retaddr); } uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) @@ -1028,12 +1005,7 @@ uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) { - int ret; - - set_helper_retaddr(retaddr); - ret = cpu_ldsw_be_data(env, ptr); - clear_helper_retaddr(); - return ret; + return (int16_t)cpu_lduw_be_data_ra(env, ptr, retaddr); } uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) @@ -1068,12 +1040,7 @@ uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) { - int ret; - - set_helper_retaddr(retaddr); - ret = cpu_ldsw_le_data(env, ptr); - clear_helper_retaddr(); - return ret; + return (int16_t)cpu_lduw_le_data_ra(env, ptr, retaddr); } uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) From 4b473e0c60d802bb69accab3177d350fc580e2a4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 26 Jul 2021 10:32:17 -1000 Subject: [PATCH 0286/1334] tcg: Expand MO_SIZE to 3 bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have lacked expressive support for memory sizes larger than 64-bits for a while. Fixing that requires adjustment to several points where we used this for array indexing, and two places that develop -Wswitch warnings after the change. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/memop.h | 14 +++++++++----- target/arm/translate-a64.c | 2 +- target/s390x/tcg/translate_vx.c.inc | 2 +- tcg/aarch64/tcg-target.c.inc | 4 ++-- tcg/arm/tcg-target.c.inc | 4 ++-- tcg/i386/tcg-target.c.inc | 4 ++-- tcg/mips/tcg-target.c.inc | 4 ++-- tcg/ppc/tcg-target.c.inc | 8 ++++---- tcg/riscv/tcg-target.c.inc | 4 ++-- tcg/s390/tcg-target.c.inc | 4 ++-- tcg/sparc/tcg-target.c.inc | 16 ++++++++-------- tcg/tcg-op.c | 13 ++++++++----- 12 files changed, 43 insertions(+), 36 deletions(-) diff --git a/include/exec/memop.h b/include/exec/memop.h index 529d07b02d..04264ffd6b 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -19,11 +19,15 @@ typedef enum MemOp { MO_16 = 1, MO_32 = 2, MO_64 = 3, - MO_SIZE = 3, /* Mask for the above. */ + MO_128 = 4, + MO_256 = 5, + MO_512 = 6, + MO_1024 = 7, + MO_SIZE = 0x07, /* Mask for the above. */ - MO_SIGN = 4, /* Sign-extended, otherwise zero-extended. */ + MO_SIGN = 0x08, /* Sign-extended, otherwise zero-extended. */ - MO_BSWAP = 8, /* Host reverse endian. */ + MO_BSWAP = 0x10, /* Host reverse endian. */ #ifdef HOST_WORDS_BIGENDIAN MO_LE = MO_BSWAP, MO_BE = 0, @@ -59,8 +63,8 @@ typedef enum MemOp { * - an alignment to a specified size, which may be more or less than * the access size (MO_ALIGN_x where 'x' is a size in bytes); */ - MO_ASHIFT = 4, - MO_AMASK = 7 << MO_ASHIFT, + MO_ASHIFT = 5, + MO_AMASK = 0x7 << MO_ASHIFT, #ifdef NEED_CPU_H #ifdef TARGET_ALIGNED_ONLY MO_ALIGN = 0, diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index ab6b346e35..717afd481c 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -1045,7 +1045,7 @@ static void read_vec_element(DisasContext *s, TCGv_i64 tcg_dest, int srcidx, int element, MemOp memop) { int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); - switch (memop) { + switch ((unsigned)memop) { case MO_8: tcg_gen_ld8u_i64(tcg_dest, cpu_env, vect_off); break; diff --git a/target/s390x/tcg/translate_vx.c.inc b/target/s390x/tcg/translate_vx.c.inc index 0afa46e463..28bf5a23b6 100644 --- a/target/s390x/tcg/translate_vx.c.inc +++ b/target/s390x/tcg/translate_vx.c.inc @@ -67,7 +67,7 @@ static void read_vec_element_i64(TCGv_i64 dst, uint8_t reg, uint8_t enr, { const int offs = vec_reg_offset(reg, enr, memop & MO_SIZE); - switch (memop) { + switch ((unsigned)memop) { case ES_8: tcg_gen_ld8u_i64(dst, cpu_env, offs); break; diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 5924977b42..6f43c048a5 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1547,7 +1547,7 @@ static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * TCGMemOpIdx oi, uintptr_t ra) */ -static void * const qemu_ld_helpers[4] = { +static void * const qemu_ld_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_ldub_mmu, #ifdef HOST_WORDS_BIGENDIAN [MO_16] = helper_be_lduw_mmu, @@ -1564,7 +1564,7 @@ static void * const qemu_ld_helpers[4] = { * uintxx_t val, TCGMemOpIdx oi, * uintptr_t ra) */ -static void * const qemu_st_helpers[4] = { +static void * const qemu_st_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_stb_mmu, #ifdef HOST_WORDS_BIGENDIAN [MO_16] = helper_be_stw_mmu, diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index d25e68b36b..d71f4a2317 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1437,7 +1437,7 @@ static void tcg_out_vldst(TCGContext *s, ARMInsn insn, /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) */ -static void * const qemu_ld_helpers[8] = { +static void * const qemu_ld_helpers[MO_SSIZE + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_SB] = helper_ret_ldsb_mmu, #ifdef HOST_WORDS_BIGENDIAN @@ -1458,7 +1458,7 @@ static void * const qemu_ld_helpers[8] = { /* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, * uintxx_t val, int mmu_idx, uintptr_t ra) */ -static void * const qemu_st_helpers[4] = { +static void * const qemu_st_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_stb_mmu, #ifdef HOST_WORDS_BIGENDIAN [MO_16] = helper_be_stw_mmu, diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 997510109d..4aabc62606 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1611,7 +1611,7 @@ static void tcg_out_nopn(TCGContext *s, int n) /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) */ -static void * const qemu_ld_helpers[16] = { +static void * const qemu_ld_helpers[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_LEUW] = helper_le_lduw_mmu, [MO_LEUL] = helper_le_ldul_mmu, @@ -1624,7 +1624,7 @@ static void * const qemu_ld_helpers[16] = { /* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, * uintxx_t val, int mmu_idx, uintptr_t ra) */ -static void * const qemu_st_helpers[16] = { +static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_stb_mmu, [MO_LEUW] = helper_le_stw_mmu, [MO_LEUL] = helper_le_stl_mmu, diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 41ffa28394..84aa775c0d 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1017,7 +1017,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg) #if defined(CONFIG_SOFTMMU) #include "../tcg-ldst.c.inc" -static void * const qemu_ld_helpers[16] = { +static void * const qemu_ld_helpers[(MO_SSIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_SB] = helper_ret_ldsb_mmu, [MO_LEUW] = helper_le_lduw_mmu, @@ -1034,7 +1034,7 @@ static void * const qemu_ld_helpers[16] = { #endif }; -static void * const qemu_st_helpers[16] = { +static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_stb_mmu, [MO_LEUW] = helper_le_stw_mmu, [MO_LEUL] = helper_le_stl_mmu, diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 5e1fac914a..7e8dee2cc6 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -1931,7 +1931,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target) #endif } -static const uint32_t qemu_ldx_opc[16] = { +static const uint32_t qemu_ldx_opc[(MO_SSIZE + MO_BSWAP) + 1] = { [MO_UB] = LBZX, [MO_UW] = LHZX, [MO_UL] = LWZX, @@ -1944,7 +1944,7 @@ static const uint32_t qemu_ldx_opc[16] = { [MO_BSWAP | MO_Q] = LDBRX, }; -static const uint32_t qemu_stx_opc[16] = { +static const uint32_t qemu_stx_opc[(MO_SIZE + MO_BSWAP) + 1] = { [MO_UB] = STBX, [MO_UW] = STHX, [MO_UL] = STWX, @@ -1965,7 +1965,7 @@ static const uint32_t qemu_exts_opc[4] = { /* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) */ -static void * const qemu_ld_helpers[16] = { +static void * const qemu_ld_helpers[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_LEUW] = helper_le_lduw_mmu, [MO_LEUL] = helper_le_ldul_mmu, @@ -1978,7 +1978,7 @@ static void * const qemu_ld_helpers[16] = { /* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, * uintxx_t val, int mmu_idx, uintptr_t ra) */ -static void * const qemu_st_helpers[16] = { +static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_stb_mmu, [MO_LEUW] = helper_le_stw_mmu, [MO_LEUL] = helper_le_stl_mmu, diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index dc8d8f1de2..da48f9a633 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -852,7 +852,7 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * TCGMemOpIdx oi, uintptr_t ra) */ -static void * const qemu_ld_helpers[8] = { +static void * const qemu_ld_helpers[MO_SSIZE + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_SB] = helper_ret_ldsb_mmu, #ifdef HOST_WORDS_BIGENDIAN @@ -878,7 +878,7 @@ static void * const qemu_ld_helpers[8] = { * uintxx_t val, TCGMemOpIdx oi, * uintptr_t ra) */ -static void * const qemu_st_helpers[4] = { +static void * const qemu_st_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_stb_mmu, #ifdef HOST_WORDS_BIGENDIAN [MO_16] = helper_be_stw_mmu, diff --git a/tcg/s390/tcg-target.c.inc b/tcg/s390/tcg-target.c.inc index b82cf19f09..67a2ba5ff3 100644 --- a/tcg/s390/tcg-target.c.inc +++ b/tcg/s390/tcg-target.c.inc @@ -350,7 +350,7 @@ static const uint8_t tcg_cond_to_ltr_cond[] = { }; #ifdef CONFIG_SOFTMMU -static void * const qemu_ld_helpers[16] = { +static void * const qemu_ld_helpers[(MO_SSIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_SB] = helper_ret_ldsb_mmu, [MO_LEUW] = helper_le_lduw_mmu, @@ -365,7 +365,7 @@ static void * const qemu_ld_helpers[16] = { [MO_BEQ] = helper_be_ldq_mmu, }; -static void * const qemu_st_helpers[16] = { +static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_stb_mmu, [MO_LEUW] = helper_le_stw_mmu, [MO_LEUL] = helper_le_stl_mmu, diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index 9720d76abd..43248776a1 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -855,8 +855,8 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) } #ifdef CONFIG_SOFTMMU -static const tcg_insn_unit *qemu_ld_trampoline[16]; -static const tcg_insn_unit *qemu_st_trampoline[16]; +static const tcg_insn_unit *qemu_ld_trampoline[(MO_SSIZE | MO_BSWAP) + 1]; +static const tcg_insn_unit *qemu_st_trampoline[(MO_SIZE | MO_BSWAP) + 1]; static void emit_extend(TCGContext *s, TCGReg r, int op) { @@ -883,7 +883,7 @@ static void emit_extend(TCGContext *s, TCGReg r, int op) static void build_trampolines(TCGContext *s) { - static void * const qemu_ld_helpers[16] = { + static void * const qemu_ld_helpers[] = { [MO_UB] = helper_ret_ldub_mmu, [MO_SB] = helper_ret_ldsb_mmu, [MO_LEUW] = helper_le_lduw_mmu, @@ -895,7 +895,7 @@ static void build_trampolines(TCGContext *s) [MO_BEUL] = helper_be_ldul_mmu, [MO_BEQ] = helper_be_ldq_mmu, }; - static void * const qemu_st_helpers[16] = { + static void * const qemu_st_helpers[] = { [MO_UB] = helper_ret_stb_mmu, [MO_LEUW] = helper_le_stw_mmu, [MO_LEUL] = helper_le_stl_mmu, @@ -908,7 +908,7 @@ static void build_trampolines(TCGContext *s) int i; TCGReg ra; - for (i = 0; i < 16; ++i) { + for (i = 0; i < ARRAY_SIZE(qemu_ld_helpers); ++i) { if (qemu_ld_helpers[i] == NULL) { continue; } @@ -936,7 +936,7 @@ static void build_trampolines(TCGContext *s) tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O7, ra); } - for (i = 0; i < 16; ++i) { + for (i = 0; i < ARRAY_SIZE(qemu_st_helpers); ++i) { if (qemu_st_helpers[i] == NULL) { continue; } @@ -1118,7 +1118,7 @@ static TCGReg tcg_out_tlb_load(TCGContext *s, TCGReg addr, int mem_index, } #endif /* CONFIG_SOFTMMU */ -static const int qemu_ld_opc[16] = { +static const int qemu_ld_opc[(MO_SSIZE | MO_BSWAP) + 1] = { [MO_UB] = LDUB, [MO_SB] = LDSB, @@ -1135,7 +1135,7 @@ static const int qemu_ld_opc[16] = { [MO_LEQ] = LDX_LE, }; -static const int qemu_st_opc[16] = { +static const int qemu_st_opc[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = STB, [MO_BEUW] = STH, diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index c754396575..e01f68f44d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2780,10 +2780,13 @@ static inline MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) } break; case MO_64: - if (!is64) { - tcg_abort(); + if (is64) { + op &= ~MO_SIGN; + break; } - break; + /* fall through */ + default: + g_assert_not_reached(); } if (st) { op &= ~MO_SIGN; @@ -3095,7 +3098,7 @@ typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv, # define WITH_ATOMIC64(X) #endif -static void * const table_cmpxchg[16] = { +static void * const table_cmpxchg[(MO_SIZE | MO_BSWAP) + 1] = { [MO_8] = gen_helper_atomic_cmpxchgb, [MO_16 | MO_LE] = gen_helper_atomic_cmpxchgw_le, [MO_16 | MO_BE] = gen_helper_atomic_cmpxchgw_be, @@ -3297,7 +3300,7 @@ static void do_atomic_op_i64(TCGv_i64 ret, TCGv addr, TCGv_i64 val, } #define GEN_ATOMIC_HELPER(NAME, OP, NEW) \ -static void * const table_##NAME[16] = { \ +static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ [MO_8] = gen_helper_atomic_##NAME##b, \ [MO_16 | MO_LE] = gen_helper_atomic_##NAME##w_le, \ [MO_16 | MO_BE] = gen_helper_atomic_##NAME##w_be, \ From 9002ffcb7264947d9a193567b457dea42f15c321 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 25 Jul 2021 12:06:49 -1000 Subject: [PATCH 0287/1334] tcg: Rename TCGMemOpIdx to MemOpIdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're about to move this out of tcg.h, so rename it as we did when moving MemOp. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/atomic_common.c.inc | 6 +-- accel/tcg/atomic_template.h | 24 +++++------ accel/tcg/cputlb.c | 78 +++++++++++++++++------------------ accel/tcg/user-exec.c | 2 +- include/tcg/tcg.h | 74 ++++++++++++++++----------------- target/arm/helper-a64.c | 16 +++---- target/arm/m_helper.c | 2 +- target/i386/tcg/mem_helper.c | 4 +- target/m68k/op_helper.c | 2 +- target/mips/tcg/msa_helper.c | 6 +-- target/s390x/tcg/mem_helper.c | 20 ++++----- target/sparc/ldst_helper.c | 2 +- tcg/aarch64/tcg-target.c.inc | 14 +++---- tcg/arm/tcg-target.c.inc | 10 ++--- tcg/i386/tcg-target.c.inc | 10 ++--- tcg/mips/tcg-target.c.inc | 12 +++--- tcg/optimize.c | 2 +- tcg/ppc/tcg-target.c.inc | 10 ++--- tcg/riscv/tcg-target.c.inc | 16 +++---- tcg/s390/tcg-target.c.inc | 10 ++--- tcg/sparc/tcg-target.c.inc | 4 +- tcg/tcg-ldst.c.inc | 2 +- tcg/tcg-op.c | 12 +++--- tcg/tcg.c | 2 +- tcg/tci.c | 14 +++---- 25 files changed, 177 insertions(+), 177 deletions(-) diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 6c0339f610..ebaa793464 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -14,7 +14,7 @@ */ static uint16_t atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi) + MemOpIdx oi) { CPUState *cpu = env_cpu(env); uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), false); @@ -34,7 +34,7 @@ static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, #if HAVE_ATOMIC128 static uint16_t atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi) + MemOpIdx oi) { uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), false); @@ -50,7 +50,7 @@ static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, } static uint16_t atomic_trace_st_pre(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi) + MemOpIdx oi) { uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), true); diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 8098a1be31..4230ff2957 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -72,7 +72,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ABI_TYPE cmpv, ABI_TYPE newv, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr); @@ -92,7 +92,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, #if DATA_SIZE >= 16 #if HAVE_ATOMIC128 ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ, retaddr); @@ -106,7 +106,7 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, } void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, retaddr); @@ -119,7 +119,7 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, #endif #else ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr); @@ -134,7 +134,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ - ABI_TYPE val, TCGMemOpIdx oi, uintptr_t retaddr) \ + ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ { \ DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ @@ -167,7 +167,7 @@ GEN_ATOMIC_HELPER(xor_fetch) */ #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ - ABI_TYPE xval, TCGMemOpIdx oi, uintptr_t retaddr) \ + ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ { \ XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ @@ -211,7 +211,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ABI_TYPE cmpv, ABI_TYPE newv, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr); @@ -231,7 +231,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, #if DATA_SIZE >= 16 #if HAVE_ATOMIC128 ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ, retaddr); @@ -245,7 +245,7 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, } void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, retaddr); @@ -259,7 +259,7 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, #endif #else ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr); @@ -274,7 +274,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ - ABI_TYPE val, TCGMemOpIdx oi, uintptr_t retaddr) \ + ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ { \ DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ @@ -304,7 +304,7 @@ GEN_ATOMIC_HELPER(xor_fetch) */ #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ - ABI_TYPE xval, TCGMemOpIdx oi, uintptr_t retaddr) \ + ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ { \ XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 0a1fdbefdd..d72f65f42b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1749,7 +1749,7 @@ bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx, * @prot may be PAGE_READ, PAGE_WRITE, or PAGE_READ|PAGE_WRITE. */ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, int size, int prot, + MemOpIdx oi, int size, int prot, uintptr_t retaddr) { size_t mmu_idx = get_mmuidx(oi); @@ -1850,7 +1850,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, */ typedef uint64_t FullLoadHelper(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); static inline uint64_t QEMU_ALWAYS_INLINE load_memop(const void *haddr, MemOp op) @@ -1876,7 +1876,7 @@ load_memop(const void *haddr, MemOp op) } static inline uint64_t QEMU_ALWAYS_INLINE -load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, +load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr, MemOp op, bool code_read, FullLoadHelper *full_load) { @@ -1991,78 +1991,78 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, */ static uint64_t full_ldub_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_UB, false, full_ldub_mmu); } tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return full_ldub_mmu(env, addr, oi, retaddr); } static uint64_t full_le_lduw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_LEUW, false, full_le_lduw_mmu); } tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return full_le_lduw_mmu(env, addr, oi, retaddr); } static uint64_t full_be_lduw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_BEUW, false, full_be_lduw_mmu); } tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return full_be_lduw_mmu(env, addr, oi, retaddr); } static uint64_t full_le_ldul_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_LEUL, false, full_le_ldul_mmu); } tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return full_le_ldul_mmu(env, addr, oi, retaddr); } static uint64_t full_be_ldul_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_BEUL, false, full_be_ldul_mmu); } tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return full_be_ldul_mmu(env, addr, oi, retaddr); } uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_LEQ, false, helper_le_ldq_mmu); } uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_BEQ, false, helper_be_ldq_mmu); @@ -2075,31 +2075,31 @@ uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr, tcg_target_ulong helper_ret_ldsb_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return (int8_t)helper_ret_ldub_mmu(env, addr, oi, retaddr); } tcg_target_ulong helper_le_ldsw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return (int16_t)helper_le_lduw_mmu(env, addr, oi, retaddr); } tcg_target_ulong helper_be_ldsw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return (int16_t)helper_be_lduw_mmu(env, addr, oi, retaddr); } tcg_target_ulong helper_le_ldsl_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return (int32_t)helper_le_ldul_mmu(env, addr, oi, retaddr); } tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return (int32_t)helper_be_ldul_mmu(env, addr, oi, retaddr); } @@ -2113,7 +2113,7 @@ static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, MemOp op, FullLoadHelper *full_load) { uint16_t meminfo; - TCGMemOpIdx oi; + MemOpIdx oi; uint64_t ret; meminfo = trace_mem_get_info(op, mmu_idx, false); @@ -2337,7 +2337,7 @@ store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val, uintptr_t index, index2; CPUTLBEntry *entry, *entry2; target_ulong page2, tlb_addr, tlb_addr2; - TCGMemOpIdx oi; + MemOpIdx oi; size_t size2; int i; @@ -2404,7 +2404,7 @@ store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val, static inline void QEMU_ALWAYS_INLINE store_helper(CPUArchState *env, target_ulong addr, uint64_t val, - TCGMemOpIdx oi, uintptr_t retaddr, MemOp op) + MemOpIdx oi, uintptr_t retaddr, MemOp op) { uintptr_t mmu_idx = get_mmuidx(oi); uintptr_t index = tlb_index(env, mmu_idx, addr); @@ -2502,43 +2502,43 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, void __attribute__((noinline)) helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { store_helper(env, addr, val, oi, retaddr, MO_UB); } void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { store_helper(env, addr, val, oi, retaddr, MO_LEUW); } void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { store_helper(env, addr, val, oi, retaddr, MO_BEUW); } void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { store_helper(env, addr, val, oi, retaddr, MO_LEUL); } void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { store_helper(env, addr, val, oi, retaddr, MO_BEUL); } void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { store_helper(env, addr, val, oi, retaddr, MO_LEQ); } void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { store_helper(env, addr, val, oi, retaddr, MO_BEQ); } @@ -2551,7 +2551,7 @@ static inline void QEMU_ALWAYS_INLINE cpu_store_helper(CPUArchState *env, target_ulong addr, uint64_t val, int mmu_idx, uintptr_t retaddr, MemOp op) { - TCGMemOpIdx oi; + MemOpIdx oi; uint16_t meminfo; meminfo = trace_mem_get_info(op, mmu_idx, true); @@ -2717,49 +2717,49 @@ void cpu_stq_le_data(CPUArchState *env, target_ulong ptr, uint64_t val) /* Code access functions. */ static uint64_t full_ldub_code(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_8, true, full_ldub_code); } uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr) { - TCGMemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(env, true)); + MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(env, true)); return full_ldub_code(env, addr, oi, 0); } static uint64_t full_lduw_code(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_TEUW, true, full_lduw_code); } uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr) { - TCGMemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(env, true)); + MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(env, true)); return full_lduw_code(env, addr, oi, 0); } static uint64_t full_ldl_code(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_TEUL, true, full_ldl_code); } uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr) { - TCGMemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(env, true)); + MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(env, true)); return full_ldl_code(env, addr, oi, 0); } static uint64_t full_ldq_code(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) + MemOpIdx oi, uintptr_t retaddr) { return load_helper(env, addr, oi, retaddr, MO_TEQ, true, full_ldq_code); } uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) { - TCGMemOpIdx oi = make_memop_idx(MO_TEQ, cpu_mmu_index(env, true)); + MemOpIdx oi = make_memop_idx(MO_TEQ, cpu_mmu_index(env, true)); return full_ldq_code(env, addr, oi, 0); } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 8f2644f26e..1187362a4c 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -1228,7 +1228,7 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) * @prot may be PAGE_READ, PAGE_WRITE, or PAGE_READ|PAGE_WRITE. */ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, int size, int prot, + MemOpIdx oi, int size, int prot, uintptr_t retaddr) { /* Enforce qemu required alignment. */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 1bb6c0ce3e..1a0da58f92 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -1148,7 +1148,7 @@ static inline size_t tcg_current_code_size(TCGContext *s) } /* Combine the MemOp and mmu_idx parameters into a single value. */ -typedef uint32_t TCGMemOpIdx; +typedef uint32_t MemOpIdx; /** * make_memop_idx @@ -1157,7 +1157,7 @@ typedef uint32_t TCGMemOpIdx; * * Encode these values into a single parameter. */ -static inline TCGMemOpIdx make_memop_idx(MemOp op, unsigned idx) +static inline MemOpIdx make_memop_idx(MemOp op, unsigned idx) { tcg_debug_assert(idx <= 15); return (op << 4) | idx; @@ -1169,7 +1169,7 @@ static inline TCGMemOpIdx make_memop_idx(MemOp op, unsigned idx) * * Extract the memory operation from the combined value. */ -static inline MemOp get_memop(TCGMemOpIdx oi) +static inline MemOp get_memop(MemOpIdx oi) { return oi >> 4; } @@ -1180,7 +1180,7 @@ static inline MemOp get_memop(TCGMemOpIdx oi) * * Extract the mmu index from the combined value. */ -static inline unsigned get_mmuidx(TCGMemOpIdx oi) +static inline unsigned get_mmuidx(MemOpIdx oi) { return oi & 15; } @@ -1290,46 +1290,46 @@ uint64_t dup_const(unsigned vece, uint64_t c); #ifdef CONFIG_SOFTMMU /* Value zero-extended to tcg register size. */ tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); /* Value sign-extended to tcg register size. */ tcg_target_ulong helper_ret_ldsb_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); tcg_target_ulong helper_le_ldsw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); tcg_target_ulong helper_le_ldsl_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); tcg_target_ulong helper_be_ldsw_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); /* Temporary aliases until backends are converted. */ #ifdef TARGET_WORDS_BIGENDIAN @@ -1357,30 +1357,30 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, target_ulong addr, uint32_t cmpv, uint32_t newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, target_ulong addr, uint32_t cmpv, uint32_t newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, target_ulong addr, uint32_t cmpv, uint32_t newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, target_ulong addr, uint64_t cmpv, uint64_t newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, target_ulong addr, uint32_t cmpv, uint32_t newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, target_ulong addr, uint32_t cmpv, uint32_t newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, target_ulong addr, uint64_t cmpv, uint64_t newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); #define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ TYPE cpu_atomic_ ## NAME ## SUFFIX ## _mmu \ (CPUArchState *env, target_ulong addr, TYPE val, \ - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); #ifdef CONFIG_ATOMIC64 #define GEN_ATOMIC_HELPER_ALL(NAME) \ @@ -1427,19 +1427,19 @@ GEN_ATOMIC_HELPER_ALL(xchg) Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, target_ulong addr, Int128 cmpv, Int128 newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, target_ulong addr, Int128 cmpv, Int128 newv, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); Int128 cpu_atomic_ldo_le_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); Int128 cpu_atomic_ldo_be_mmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void cpu_atomic_sto_le_mmu(CPUArchState *env, target_ulong addr, Int128 val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); void cpu_atomic_sto_be_mmu(CPUArchState *env, target_ulong addr, Int128 val, - TCGMemOpIdx oi, uintptr_t retaddr); + MemOpIdx oi, uintptr_t retaddr); #ifdef CONFIG_DEBUG_TCG void tcg_assert_listed_vecop(TCGOpcode); diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 19445b3c94..c5af779006 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -531,8 +531,8 @@ uint64_t HELPER(paired_cmpxchg64_le)(CPUARMState *env, uint64_t addr, clear_helper_retaddr(); #else int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi0 = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); - TCGMemOpIdx oi1 = make_memop_idx(MO_LEQ, mem_idx); + MemOpIdx oi0 = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + MemOpIdx oi1 = make_memop_idx(MO_LEQ, mem_idx); o0 = helper_le_ldq_mmu(env, addr + 0, oi0, ra); o1 = helper_le_ldq_mmu(env, addr + 8, oi1, ra); @@ -555,7 +555,7 @@ uint64_t HELPER(paired_cmpxchg64_le_parallel)(CPUARMState *env, uint64_t addr, uintptr_t ra = GETPC(); bool success; int mem_idx; - TCGMemOpIdx oi; + MemOpIdx oi; assert(HAVE_CMPXCHG128); @@ -601,8 +601,8 @@ uint64_t HELPER(paired_cmpxchg64_be)(CPUARMState *env, uint64_t addr, clear_helper_retaddr(); #else int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi0 = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); - TCGMemOpIdx oi1 = make_memop_idx(MO_BEQ, mem_idx); + MemOpIdx oi0 = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); + MemOpIdx oi1 = make_memop_idx(MO_BEQ, mem_idx); o1 = helper_be_ldq_mmu(env, addr + 0, oi0, ra); o0 = helper_be_ldq_mmu(env, addr + 8, oi1, ra); @@ -625,7 +625,7 @@ uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr, uintptr_t ra = GETPC(); bool success; int mem_idx; - TCGMemOpIdx oi; + MemOpIdx oi; assert(HAVE_CMPXCHG128); @@ -651,7 +651,7 @@ void HELPER(casp_le_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, Int128 oldv, cmpv, newv; uintptr_t ra = GETPC(); int mem_idx; - TCGMemOpIdx oi; + MemOpIdx oi; assert(HAVE_CMPXCHG128); @@ -672,7 +672,7 @@ void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, Int128 oldv, cmpv, newv; uintptr_t ra = GETPC(); int mem_idx; - TCGMemOpIdx oi; + MemOpIdx oi; assert(HAVE_CMPXCHG128); diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index 47903b3dc3..62aa12c9d8 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -1930,7 +1930,7 @@ static bool do_v7m_function_return(ARMCPU *cpu) { bool threadmode, spsel; - TCGMemOpIdx oi; + MemOpIdx oi; ARMMMUIdx mmu_idx; uint32_t *frame_sp_p; uint32_t frameptr; diff --git a/target/i386/tcg/mem_helper.c b/target/i386/tcg/mem_helper.c index 2da3cd14b6..0fd696f9c1 100644 --- a/target/i386/tcg/mem_helper.c +++ b/target/i386/tcg/mem_helper.c @@ -67,7 +67,7 @@ void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) { uintptr_t ra = GETPC(); int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_TEQ, mem_idx); + MemOpIdx oi = make_memop_idx(MO_TEQ, mem_idx); oldv = cpu_atomic_cmpxchgq_le_mmu(env, a0, cmpv, newv, oi, ra); } @@ -136,7 +136,7 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) Int128 newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + MemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); Int128 oldv = cpu_atomic_cmpxchgo_le_mmu(env, a0, cmpv, newv, oi, ra); if (int128_eq(oldv, cmpv)) { diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index 5d624838ae..c1bf73b6f9 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -775,7 +775,7 @@ static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2, uintptr_t ra = GETPC(); #if defined(CONFIG_ATOMIC64) int mmu_idx = cpu_mmu_index(env, 0); - TCGMemOpIdx oi = make_memop_idx(MO_BEQ, mmu_idx); + MemOpIdx oi = make_memop_idx(MO_BEQ, mmu_idx); #endif if (parallel) { diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index 04af54f66d..167d9a591c 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -8211,9 +8211,9 @@ void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df)) #if !defined(CONFIG_USER_ONLY) -#define MEMOP_IDX(DF) \ - TCGMemOpIdx oi = make_memop_idx(MO_TE | DF | MO_UNALN, \ - cpu_mmu_index(env, false)); +#define MEMOP_IDX(DF) \ + MemOpIdx oi = make_memop_idx(MO_TE | DF | MO_UNALN, \ + cpu_mmu_index(env, false)); #else #define MEMOP_IDX(DF) #endif diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 0bf775a37d..75f6735545 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -239,7 +239,7 @@ static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, g_assert(haddr); memset(haddr, byte, size); #else - TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); int i; if (likely(haddr)) { @@ -282,7 +282,7 @@ static uint8_t do_access_get_byte(CPUS390XState *env, vaddr vaddr, char **haddr, #ifdef CONFIG_USER_ONLY return ldub_p(*haddr + offset); #else - TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); uint8_t byte; if (likely(*haddr)) { @@ -316,7 +316,7 @@ static void do_access_set_byte(CPUS390XState *env, vaddr vaddr, char **haddr, #ifdef CONFIG_USER_ONLY stb_p(*haddr + offset, byte); #else - TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); if (likely(*haddr)) { stb_p(*haddr + offset, byte); @@ -1804,7 +1804,7 @@ void HELPER(cdsg_parallel)(CPUS390XState *env, uint64_t addr, Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]); Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]); int mem_idx; - TCGMemOpIdx oi; + MemOpIdx oi; Int128 oldv; bool fail; @@ -1884,7 +1884,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, uint32_t *haddr = g2h(env_cpu(env), a1); ov = qatomic_cmpxchg__nocheck(haddr, cv, nv); #else - TCGMemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx); + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx); ov = cpu_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi, ra); #endif } else { @@ -1904,7 +1904,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, if (parallel) { #ifdef CONFIG_ATOMIC64 - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN, mem_idx); + MemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN, mem_idx); ov = cpu_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi, ra); #else /* Note that we asserted !parallel above. */ @@ -1940,7 +1940,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra); cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra); } else if (HAVE_CMPXCHG128) { - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + MemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); ov = cpu_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra); cc = !int128_eq(ov, cv); } else { @@ -1979,7 +1979,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, cpu_stq_data_ra(env, a2 + 0, svh, ra); cpu_stq_data_ra(env, a2 + 8, svl, ra); } else if (HAVE_ATOMIC128) { - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + MemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); Int128 sv = int128_make128(svl, svh); cpu_atomic_sto_be_mmu(env, a2, sv, oi, ra); } else { @@ -2497,7 +2497,7 @@ uint64_t HELPER(lpq_parallel)(CPUS390XState *env, uint64_t addr) uintptr_t ra = GETPC(); uint64_t hi, lo; int mem_idx; - TCGMemOpIdx oi; + MemOpIdx oi; Int128 v; assert(HAVE_ATOMIC128); @@ -2528,7 +2528,7 @@ void HELPER(stpq_parallel)(CPUS390XState *env, uint64_t addr, { uintptr_t ra = GETPC(); int mem_idx; - TCGMemOpIdx oi; + MemOpIdx oi; Int128 v; assert(HAVE_ATOMIC128); diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 22327d7d72..abe2889d27 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -1318,7 +1318,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case ASI_SNF: case ASI_SNFL: { - TCGMemOpIdx oi; + MemOpIdx oi; int idx = (env->pstate & PS_PRIV ? (asi & 1 ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX) : (asi & 1 ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX)); diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 6f43c048a5..5edca8d44d 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1545,7 +1545,7 @@ static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, #include "../tcg-ldst.c.inc" /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * TCGMemOpIdx oi, uintptr_t ra) + * MemOpIdx oi, uintptr_t ra) */ static void * const qemu_ld_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_ldub_mmu, @@ -1561,7 +1561,7 @@ static void * const qemu_ld_helpers[MO_SIZE + 1] = { }; /* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, TCGMemOpIdx oi, + * uintxx_t val, MemOpIdx oi, * uintptr_t ra) */ static void * const qemu_st_helpers[MO_SIZE + 1] = { @@ -1586,7 +1586,7 @@ static inline void tcg_out_adr(TCGContext *s, TCGReg rd, const void *target) static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - TCGMemOpIdx oi = lb->oi; + MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); MemOp size = opc & MO_SIZE; @@ -1611,7 +1611,7 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - TCGMemOpIdx oi = lb->oi; + MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); MemOp size = opc & MO_SIZE; @@ -1629,7 +1629,7 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) return true; } -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, MemOpIdx oi, TCGType ext, TCGReg data_reg, TCGReg addr_reg, tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) { @@ -1778,7 +1778,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp memop, } static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOpIdx oi, TCGType ext) + MemOpIdx oi, TCGType ext) { MemOp memop = get_memop(oi); const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; @@ -1803,7 +1803,7 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, } static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOpIdx oi) + MemOpIdx oi) { MemOp memop = get_memop(oi); const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index d71f4a2317..633b8a37ba 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1632,7 +1632,7 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, /* Record the context of a call to the out of line helper code for the slow path for a load or store, so that we can later generate the correct helper code. */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, MemOpIdx oi, TCGReg datalo, TCGReg datahi, TCGReg addrlo, TCGReg addrhi, tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) @@ -1652,7 +1652,7 @@ static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { TCGReg argreg, datalo, datahi; - TCGMemOpIdx oi = lb->oi; + MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); void *func; @@ -1716,7 +1716,7 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { TCGReg argreg, datalo, datahi; - TCGMemOpIdx oi = lb->oi; + MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); if (!reloc_pc24(lb->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { @@ -1846,7 +1846,7 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) { TCGReg addrlo, datalo, datahi, addrhi __attribute__((unused)); - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc; #ifdef CONFIG_SOFTMMU int mem_index; @@ -1952,7 +1952,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) { TCGReg addrlo, datalo, datahi, addrhi __attribute__((unused)); - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc; #ifdef CONFIG_SOFTMMU int mem_index; diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 4aabc62606..84b109bb84 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1741,7 +1741,7 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, * for a load or store, so that we can later generate the correct helper code */ static void add_qemu_ldst_label(TCGContext *s, bool is_ld, bool is_64, - TCGMemOpIdx oi, + MemOpIdx oi, TCGReg datalo, TCGReg datahi, TCGReg addrlo, TCGReg addrhi, tcg_insn_unit *raddr, @@ -1768,7 +1768,7 @@ static void add_qemu_ldst_label(TCGContext *s, bool is_ld, bool is_64, */ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - TCGMemOpIdx oi = l->oi; + MemOpIdx oi = l->oi; MemOp opc = get_memop(oi); TCGReg data_reg; tcg_insn_unit **label_ptr = &l->label_ptr[0]; @@ -1853,7 +1853,7 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) */ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - TCGMemOpIdx oi = l->oi; + MemOpIdx oi = l->oi; MemOp opc = get_memop(oi); MemOp s_bits = opc & MO_SIZE; tcg_insn_unit **label_ptr = &l->label_ptr[0]; @@ -2054,7 +2054,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) { TCGReg datalo, datahi, addrlo; TCGReg addrhi __attribute__((unused)); - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc; #if defined(CONFIG_SOFTMMU) int mem_index; @@ -2143,7 +2143,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) { TCGReg datalo, datahi, addrlo; TCGReg addrhi __attribute__((unused)); - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc; #if defined(CONFIG_SOFTMMU) int mem_index; diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 84aa775c0d..d8f6914f03 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1120,7 +1120,7 @@ QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -32768); * Clobbers TMP0, TMP1, TMP2, TMP3. */ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, - TCGReg addrh, TCGMemOpIdx oi, + TCGReg addrh, MemOpIdx oi, tcg_insn_unit *label_ptr[2], bool is_load) { MemOp opc = get_memop(oi); @@ -1196,7 +1196,7 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_TMP2, addrl); } -static void add_qemu_ldst_label(TCGContext *s, int is_ld, TCGMemOpIdx oi, +static void add_qemu_ldst_label(TCGContext *s, int is_ld, MemOpIdx oi, TCGType ext, TCGReg datalo, TCGReg datahi, TCGReg addrlo, TCGReg addrhi, @@ -1221,7 +1221,7 @@ static void add_qemu_ldst_label(TCGContext *s, int is_ld, TCGMemOpIdx oi, static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { const tcg_insn_unit *tgt_rx = tcg_splitwx_to_rx(s->code_ptr); - TCGMemOpIdx oi = l->oi; + MemOpIdx oi = l->oi; MemOp opc = get_memop(oi); TCGReg v0; int i; @@ -1275,7 +1275,7 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { const tcg_insn_unit *tgt_rx = tcg_splitwx_to_rx(s->code_ptr); - TCGMemOpIdx oi = l->oi; + MemOpIdx oi = l->oi; MemOp opc = get_memop(oi); MemOp s_bits = opc & MO_SIZE; int i; @@ -1434,7 +1434,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) { TCGReg addr_regl, addr_regh __attribute__((unused)); TCGReg data_regl, data_regh; - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[2]; @@ -1536,7 +1536,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) { TCGReg addr_regl, addr_regh __attribute__((unused)); TCGReg data_regl, data_regh; - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[2]; diff --git a/tcg/optimize.c b/tcg/optimize.c index 9876ac52a8..c239c3bd07 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1023,7 +1023,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(qemu_ld): { - TCGMemOpIdx oi = op->args[nb_oargs + nb_iargs]; + MemOpIdx oi = op->args[nb_oargs + nb_iargs]; MemOp mop = get_memop(oi); if (!(mop & MO_SIGN)) { mask = (2ULL << ((8 << (mop & MO_SIZE)) - 1)) - 1; diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 7e8dee2cc6..3e4ca2be88 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2103,7 +2103,7 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, MemOp opc, /* Record the context of a call to the out of line helper code for the slow path for a load or store, so that we can later generate the correct helper code. */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, MemOpIdx oi, TCGReg datalo_reg, TCGReg datahi_reg, TCGReg addrlo_reg, TCGReg addrhi_reg, tcg_insn_unit *raddr, tcg_insn_unit *lptr) @@ -2122,7 +2122,7 @@ static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - TCGMemOpIdx oi = lb->oi; + MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); TCGReg hi, lo, arg = TCG_REG_R3; @@ -2169,7 +2169,7 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - TCGMemOpIdx oi = lb->oi; + MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); MemOp s_bits = opc & MO_SIZE; TCGReg hi, lo, arg = TCG_REG_R3; @@ -2233,7 +2233,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) { TCGReg datalo, datahi, addrlo, rbase; TCGReg addrhi __attribute__((unused)); - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc, s_bits; #ifdef CONFIG_SOFTMMU int mem_index; @@ -2308,7 +2308,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) { TCGReg datalo, datahi, addrlo, rbase; TCGReg addrhi __attribute__((unused)); - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc, s_bits; #ifdef CONFIG_SOFTMMU int mem_index; diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index da48f9a633..9b13a46fb4 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -850,7 +850,7 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) #include "../tcg-ldst.c.inc" /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * TCGMemOpIdx oi, uintptr_t ra) + * MemOpIdx oi, uintptr_t ra) */ static void * const qemu_ld_helpers[MO_SSIZE + 1] = { [MO_UB] = helper_ret_ldub_mmu, @@ -875,7 +875,7 @@ static void * const qemu_ld_helpers[MO_SSIZE + 1] = { }; /* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, TCGMemOpIdx oi, + * uintxx_t val, MemOpIdx oi, * uintptr_t ra) */ static void * const qemu_st_helpers[MO_SIZE + 1] = { @@ -906,7 +906,7 @@ static void tcg_out_goto(TCGContext *s, const tcg_insn_unit *target) } static void tcg_out_tlb_load(TCGContext *s, TCGReg addrl, - TCGReg addrh, TCGMemOpIdx oi, + TCGReg addrh, MemOpIdx oi, tcg_insn_unit **label_ptr, bool is_load) { MemOp opc = get_memop(oi); @@ -959,7 +959,7 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg addrl, tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, TCG_REG_TMP2, addrl); } -static void add_qemu_ldst_label(TCGContext *s, int is_ld, TCGMemOpIdx oi, +static void add_qemu_ldst_label(TCGContext *s, int is_ld, MemOpIdx oi, TCGType ext, TCGReg datalo, TCGReg datahi, TCGReg addrlo, TCGReg addrhi, @@ -980,7 +980,7 @@ static void add_qemu_ldst_label(TCGContext *s, int is_ld, TCGMemOpIdx oi, static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - TCGMemOpIdx oi = l->oi; + MemOpIdx oi = l->oi; MemOp opc = get_memop(oi); TCGReg a0 = tcg_target_call_iarg_regs[0]; TCGReg a1 = tcg_target_call_iarg_regs[1]; @@ -1012,7 +1012,7 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - TCGMemOpIdx oi = l->oi; + MemOpIdx oi = l->oi; MemOp opc = get_memop(oi); MemOp s_bits = opc & MO_SIZE; TCGReg a0 = tcg_target_call_iarg_regs[0]; @@ -1104,7 +1104,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) { TCGReg addr_regl, addr_regh __attribute__((unused)); TCGReg data_regl, data_regh; - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[1]; @@ -1170,7 +1170,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) { TCGReg addr_regl, addr_regh __attribute__((unused)); TCGReg data_regl, data_regh; - TCGMemOpIdx oi; + MemOpIdx oi; MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[1]; diff --git a/tcg/s390/tcg-target.c.inc b/tcg/s390/tcg-target.c.inc index 67a2ba5ff3..fd0b3316d2 100644 --- a/tcg/s390/tcg-target.c.inc +++ b/tcg/s390/tcg-target.c.inc @@ -1547,7 +1547,7 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, MemOp opc, return addr_reg; } -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, MemOpIdx oi, TCGReg data, TCGReg addr, tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) { @@ -1565,7 +1565,7 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { TCGReg addr_reg = lb->addrlo_reg; TCGReg data_reg = lb->datalo_reg; - TCGMemOpIdx oi = lb->oi; + MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); if (!patch_reloc(lb->label_ptr[0], R_390_PC16DBL, @@ -1590,7 +1590,7 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { TCGReg addr_reg = lb->addrlo_reg; TCGReg data_reg = lb->datalo_reg; - TCGMemOpIdx oi = lb->oi; + MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); if (!patch_reloc(lb->label_ptr[0], R_390_PC16DBL, @@ -1644,7 +1644,7 @@ static void tcg_prepare_user_ldst(TCGContext *s, TCGReg *addr_reg, #endif /* CONFIG_SOFTMMU */ static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOpIdx oi) + MemOpIdx oi) { MemOp opc = get_memop(oi); #ifdef CONFIG_SOFTMMU @@ -1671,7 +1671,7 @@ static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, } static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOpIdx oi) + MemOpIdx oi) { MemOp opc = get_memop(oi); #ifdef CONFIG_SOFTMMU diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index 43248776a1..9dd32ef95e 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -1148,7 +1148,7 @@ static const int qemu_st_opc[(MO_SIZE | MO_BSWAP) + 1] = { }; static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, - TCGMemOpIdx oi, bool is_64) + MemOpIdx oi, bool is_64) { MemOp memop = get_memop(oi); #ifdef CONFIG_SOFTMMU @@ -1230,7 +1230,7 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, } static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, - TCGMemOpIdx oi) + MemOpIdx oi) { MemOp memop = get_memop(oi); #ifdef CONFIG_SOFTMMU diff --git a/tcg/tcg-ldst.c.inc b/tcg/tcg-ldst.c.inc index c3ce88e69d..6c6848d034 100644 --- a/tcg/tcg-ldst.c.inc +++ b/tcg/tcg-ldst.c.inc @@ -22,7 +22,7 @@ typedef struct TCGLabelQemuLdst { bool is_ld; /* qemu_ld: true, qemu_st: false */ - TCGMemOpIdx oi; + MemOpIdx oi; TCGType type; /* result type of a load */ TCGReg addrlo_reg; /* reg index for low word of guest virtual addr */ TCGReg addrhi_reg; /* reg index for high word of guest virtual addr */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index e01f68f44d..e1490c372e 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2797,7 +2797,7 @@ static inline MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) static void gen_ldst_i32(TCGOpcode opc, TCGv_i32 val, TCGv addr, MemOp memop, TCGArg idx) { - TCGMemOpIdx oi = make_memop_idx(memop, idx); + MemOpIdx oi = make_memop_idx(memop, idx); #if TARGET_LONG_BITS == 32 tcg_gen_op3i_i32(opc, val, addr, oi); #else @@ -2812,7 +2812,7 @@ static void gen_ldst_i32(TCGOpcode opc, TCGv_i32 val, TCGv addr, static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 val, TCGv addr, MemOp memop, TCGArg idx) { - TCGMemOpIdx oi = make_memop_idx(memop, idx); + MemOpIdx oi = make_memop_idx(memop, idx); #if TARGET_LONG_BITS == 32 if (TCG_TARGET_REG_BITS == 32) { tcg_gen_op4i_i32(opc, TCGV_LOW(val), TCGV_HIGH(val), addr, oi); @@ -3132,7 +3132,7 @@ void tcg_gen_atomic_cmpxchg_i32(TCGv_i32 retv, TCGv addr, TCGv_i32 cmpv, tcg_temp_free_i32(t1); } else { gen_atomic_cx_i32 gen; - TCGMemOpIdx oi; + MemOpIdx oi; gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; tcg_debug_assert(gen != NULL); @@ -3171,7 +3171,7 @@ void tcg_gen_atomic_cmpxchg_i64(TCGv_i64 retv, TCGv addr, TCGv_i64 cmpv, } else if ((memop & MO_SIZE) == MO_64) { #ifdef CONFIG_ATOMIC64 gen_atomic_cx_i64 gen; - TCGMemOpIdx oi; + MemOpIdx oi; gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; tcg_debug_assert(gen != NULL); @@ -3227,7 +3227,7 @@ static void do_atomic_op_i32(TCGv_i32 ret, TCGv addr, TCGv_i32 val, TCGArg idx, MemOp memop, void * const table[]) { gen_atomic_op_i32 gen; - TCGMemOpIdx oi; + MemOpIdx oi; memop = tcg_canonicalize_memop(memop, 0, 0); @@ -3269,7 +3269,7 @@ static void do_atomic_op_i64(TCGv_i64 ret, TCGv addr, TCGv_i64 val, if ((memop & MO_SIZE) == MO_64) { #ifdef CONFIG_ATOMIC64 gen_atomic_op_i64 gen; - TCGMemOpIdx oi; + MemOpIdx oi; gen = table[memop & (MO_SIZE | MO_BSWAP)]; tcg_debug_assert(gen != NULL); diff --git a/tcg/tcg.c b/tcg/tcg.c index 4142d42d77..658be0c6b6 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1910,7 +1910,7 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) case INDEX_op_qemu_ld_i64: case INDEX_op_qemu_st_i64: { - TCGMemOpIdx oi = op->args[k++]; + MemOpIdx oi = op->args[k++]; MemOp op = get_memop(oi); unsigned ix = get_mmuidx(oi); diff --git a/tcg/tci.c b/tcg/tci.c index b672c7cae5..5c08dc0a9a 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -61,7 +61,7 @@ static uint64_t tci_uint64(uint32_t high, uint32_t low) * i = immediate (uint32_t) * I = immediate (tcg_target_ulong) * l = label or pointer - * m = immediate (TCGMemOpIdx) + * m = immediate (MemOpIdx) * n = immediate (call return length) * r = register * s = signed ldst offset @@ -105,7 +105,7 @@ static void tci_args_ri(uint32_t insn, TCGReg *r0, tcg_target_ulong *i1) } static void tci_args_rrm(uint32_t insn, TCGReg *r0, - TCGReg *r1, TCGMemOpIdx *m2) + TCGReg *r1, MemOpIdx *m2) { *r0 = extract32(insn, 8, 4); *r1 = extract32(insn, 12, 4); @@ -145,7 +145,7 @@ static void tci_args_rrrc(uint32_t insn, } static void tci_args_rrrm(uint32_t insn, - TCGReg *r0, TCGReg *r1, TCGReg *r2, TCGMemOpIdx *m3) + TCGReg *r0, TCGReg *r1, TCGReg *r2, MemOpIdx *m3) { *r0 = extract32(insn, 8, 4); *r1 = extract32(insn, 12, 4); @@ -289,7 +289,7 @@ static bool tci_compare64(uint64_t u0, uint64_t u1, TCGCond condition) } static uint64_t tci_qemu_ld(CPUArchState *env, target_ulong taddr, - TCGMemOpIdx oi, const void *tb_ptr) + MemOpIdx oi, const void *tb_ptr) { MemOp mop = get_memop(oi) & (MO_BSWAP | MO_SSIZE); uintptr_t ra = (uintptr_t)tb_ptr; @@ -374,7 +374,7 @@ static uint64_t tci_qemu_ld(CPUArchState *env, target_ulong taddr, } static void tci_qemu_st(CPUArchState *env, target_ulong taddr, uint64_t val, - TCGMemOpIdx oi, const void *tb_ptr) + MemOpIdx oi, const void *tb_ptr) { MemOp mop = get_memop(oi) & (MO_BSWAP | MO_SSIZE); uintptr_t ra = (uintptr_t)tb_ptr; @@ -482,7 +482,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, uint32_t tmp32; uint64_t tmp64; uint64_t T1, T2; - TCGMemOpIdx oi; + MemOpIdx oi; int32_t ofs; void *ptr; @@ -1148,7 +1148,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) tcg_target_ulong i1; int32_t s2; TCGCond c; - TCGMemOpIdx oi; + MemOpIdx oi; uint8_t pos, len; void *ptr; From abe2e23eb703b7e2b0479b4672d087cc37b0e667 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 25 Jul 2021 11:47:35 -1000 Subject: [PATCH 0288/1334] tcg: Split out MemOpIdx to exec/memopidx.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move this code from tcg/tcg.h to its own header. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/memopidx.h | 55 +++++++++++++++++++++++++++++++++++++++++ include/tcg/tcg.h | 39 +---------------------------- 2 files changed, 56 insertions(+), 38 deletions(-) create mode 100644 include/exec/memopidx.h diff --git a/include/exec/memopidx.h b/include/exec/memopidx.h new file mode 100644 index 0000000000..83bce97874 --- /dev/null +++ b/include/exec/memopidx.h @@ -0,0 +1,55 @@ +/* + * Combine the MemOp and mmu_idx parameters into a single value. + * + * Authors: + * Richard Henderson + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef EXEC_MEMOPIDX_H +#define EXEC_MEMOPIDX_H 1 + +#include "exec/memop.h" + +typedef uint32_t MemOpIdx; + +/** + * make_memop_idx + * @op: memory operation + * @idx: mmu index + * + * Encode these values into a single parameter. + */ +static inline MemOpIdx make_memop_idx(MemOp op, unsigned idx) +{ +#ifdef CONFIG_DEBUG_TCG + assert(idx <= 15); +#endif + return (op << 4) | idx; +} + +/** + * get_memop + * @oi: combined op/idx parameter + * + * Extract the memory operation from the combined value. + */ +static inline MemOp get_memop(MemOpIdx oi) +{ + return oi >> 4; +} + +/** + * get_mmuidx + * @oi: combined op/idx parameter + * + * Extract the mmu index from the combined value. + */ +static inline unsigned get_mmuidx(MemOpIdx oi) +{ + return oi & 15; +} + +#endif diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 1a0da58f92..ba13ab1151 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -27,6 +27,7 @@ #include "cpu.h" #include "exec/memop.h" +#include "exec/memopidx.h" #include "qemu/bitops.h" #include "qemu/plugin.h" #include "qemu/queue.h" @@ -1147,44 +1148,6 @@ static inline size_t tcg_current_code_size(TCGContext *s) return tcg_ptr_byte_diff(s->code_ptr, s->code_buf); } -/* Combine the MemOp and mmu_idx parameters into a single value. */ -typedef uint32_t MemOpIdx; - -/** - * make_memop_idx - * @op: memory operation - * @idx: mmu index - * - * Encode these values into a single parameter. - */ -static inline MemOpIdx make_memop_idx(MemOp op, unsigned idx) -{ - tcg_debug_assert(idx <= 15); - return (op << 4) | idx; -} - -/** - * get_memop - * @oi: combined op/idx parameter - * - * Extract the memory operation from the combined value. - */ -static inline MemOp get_memop(MemOpIdx oi) -{ - return oi >> 4; -} - -/** - * get_mmuidx - * @oi: combined op/idx parameter - * - * Extract the mmu index from the combined value. - */ -static inline unsigned get_mmuidx(MemOpIdx oi) -{ - return oi & 15; -} - /** * tcg_qemu_tb_exec: * @env: pointer to CPUArchState for the CPU From b0702c91c66a9a9d8831ecb3d08f511e7d167489 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 25 Jul 2021 12:44:12 -1000 Subject: [PATCH 0289/1334] trace/mem: Pass MemOpIdx to trace_mem_get_info We (will) often have the complete MemOpIdx handy, so use that. Reviewed-by: Peter Maydell Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- accel/tcg/atomic_common.c.inc | 6 ++--- accel/tcg/cputlb.c | 12 ++++------ accel/tcg/user-exec.c | 42 +++++++++++++++++++++++------------ tcg/tcg-op.c | 8 +++---- trace/mem.h | 32 +++++++++----------------- 5 files changed, 49 insertions(+), 51 deletions(-) diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index ebaa793464..6019a957b9 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -17,7 +17,7 @@ static uint16_t atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, MemOpIdx oi) { CPUState *cpu = env_cpu(env); - uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), false); + uint16_t info = trace_mem_get_info(oi, false); trace_guest_mem_before_exec(cpu, addr, info); trace_guest_mem_before_exec(cpu, addr, info | TRACE_MEM_ST); @@ -36,7 +36,7 @@ static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, static uint16_t atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, MemOpIdx oi) { - uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), false); + uint16_t info = trace_mem_get_info(oi, false); trace_guest_mem_before_exec(env_cpu(env), addr, info); @@ -52,7 +52,7 @@ static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, static uint16_t atomic_trace_st_pre(CPUArchState *env, target_ulong addr, MemOpIdx oi) { - uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), true); + uint16_t info = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), addr, info); diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index d72f65f42b..0aa6157ec4 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2112,14 +2112,12 @@ static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t retaddr, MemOp op, FullLoadHelper *full_load) { - uint16_t meminfo; - MemOpIdx oi; + MemOpIdx oi = make_memop_idx(op, mmu_idx); + uint16_t meminfo = trace_mem_get_info(oi, false); uint64_t ret; - meminfo = trace_mem_get_info(op, mmu_idx, false); trace_guest_mem_before_exec(env_cpu(env), addr, meminfo); - oi = make_memop_idx(op, mmu_idx); ret = full_load(env, addr, oi, retaddr); qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, meminfo); @@ -2551,13 +2549,11 @@ static inline void QEMU_ALWAYS_INLINE cpu_store_helper(CPUArchState *env, target_ulong addr, uint64_t val, int mmu_idx, uintptr_t retaddr, MemOp op) { - MemOpIdx oi; - uint16_t meminfo; + MemOpIdx oi = make_memop_idx(op, mmu_idx); + uint16_t meminfo = trace_mem_get_info(oi, true); - meminfo = trace_mem_get_info(op, mmu_idx, true); trace_guest_mem_before_exec(env_cpu(env), addr, meminfo); - oi = make_memop_idx(op, mmu_idx); store_helper(env, addr, val, oi, retaddr, op); qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, meminfo); diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 1187362a4c..3ba7acf7f4 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -888,8 +888,9 @@ int cpu_signal_handler(int host_signum, void *pinfo, uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr) { + MemOpIdx oi = make_memop_idx(MO_UB, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldub_p(g2h(env_cpu(env), ptr)); @@ -904,8 +905,9 @@ int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr) uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr) { + MemOpIdx oi = make_memop_idx(MO_BEUW, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - uint16_t meminfo = trace_mem_get_info(MO_BEUW, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = lduw_be_p(g2h(env_cpu(env), ptr)); @@ -920,8 +922,9 @@ int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr) uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr) { + MemOpIdx oi = make_memop_idx(MO_BEUL, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - uint16_t meminfo = trace_mem_get_info(MO_BEUL, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldl_be_p(g2h(env_cpu(env), ptr)); @@ -931,8 +934,9 @@ uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr) uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr) { + MemOpIdx oi = make_memop_idx(MO_BEQ, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, false); uint64_t ret; - uint16_t meminfo = trace_mem_get_info(MO_BEQ, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldq_be_p(g2h(env_cpu(env), ptr)); @@ -942,8 +946,9 @@ uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr) uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr) { + MemOpIdx oi = make_memop_idx(MO_LEUW, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - uint16_t meminfo = trace_mem_get_info(MO_LEUW, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = lduw_le_p(g2h(env_cpu(env), ptr)); @@ -958,8 +963,9 @@ int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr) uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr) { + MemOpIdx oi = make_memop_idx(MO_LEUL, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - uint16_t meminfo = trace_mem_get_info(MO_LEUL, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldl_le_p(g2h(env_cpu(env), ptr)); @@ -969,8 +975,9 @@ uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr) uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr) { + MemOpIdx oi = make_memop_idx(MO_LEQ, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, false); uint64_t ret; - uint16_t meminfo = trace_mem_get_info(MO_LEQ, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldq_le_p(g2h(env_cpu(env), ptr)); @@ -1065,7 +1072,8 @@ uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { - uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, true); + MemOpIdx oi = make_memop_idx(MO_UB, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stb_p(g2h(env_cpu(env), ptr), val); @@ -1074,7 +1082,8 @@ void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { - uint16_t meminfo = trace_mem_get_info(MO_BEUW, MMU_USER_IDX, true); + MemOpIdx oi = make_memop_idx(MO_BEUW, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stw_be_p(g2h(env_cpu(env), ptr), val); @@ -1083,7 +1092,8 @@ void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { - uint16_t meminfo = trace_mem_get_info(MO_BEUL, MMU_USER_IDX, true); + MemOpIdx oi = make_memop_idx(MO_BEUL, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stl_be_p(g2h(env_cpu(env), ptr), val); @@ -1092,7 +1102,8 @@ void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val) { - uint16_t meminfo = trace_mem_get_info(MO_BEQ, MMU_USER_IDX, true); + MemOpIdx oi = make_memop_idx(MO_BEQ, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stq_be_p(g2h(env_cpu(env), ptr), val); @@ -1101,7 +1112,8 @@ void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val) void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { - uint16_t meminfo = trace_mem_get_info(MO_LEUW, MMU_USER_IDX, true); + MemOpIdx oi = make_memop_idx(MO_LEUW, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stw_le_p(g2h(env_cpu(env), ptr), val); @@ -1110,7 +1122,8 @@ void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { - uint16_t meminfo = trace_mem_get_info(MO_LEUL, MMU_USER_IDX, true); + MemOpIdx oi = make_memop_idx(MO_LEUL, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stl_le_p(g2h(env_cpu(env), ptr), val); @@ -1119,7 +1132,8 @@ void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val) { - uint16_t meminfo = trace_mem_get_info(MO_LEQ, MMU_USER_IDX, true); + MemOpIdx oi = make_memop_idx(MO_LEQ, MMU_USER_IDX); + uint16_t meminfo = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stq_le_p(g2h(env_cpu(env), ptr), val); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index e1490c372e..37b440af7f 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2866,7 +2866,7 @@ static inline void plugin_gen_mem_callbacks(TCGv vaddr, uint16_t info) void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) { MemOp orig_memop; - uint16_t info = trace_mem_get_info(memop, idx, 0); + uint16_t info = trace_mem_get_info(make_memop_idx(memop, idx), 0); tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 0, 0); @@ -2904,7 +2904,7 @@ void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) { TCGv_i32 swap = NULL; - uint16_t info = trace_mem_get_info(memop, idx, 1); + uint16_t info = trace_mem_get_info(make_memop_idx(memop, idx), 1); tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 0, 1); @@ -2956,7 +2956,7 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 1, 0); - info = trace_mem_get_info(memop, idx, 0); + info = trace_mem_get_info(make_memop_idx(memop, idx), 0); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); orig_memop = memop; @@ -3004,7 +3004,7 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 1, 1); - info = trace_mem_get_info(memop, idx, 1); + info = trace_mem_get_info(make_memop_idx(memop, idx), 1); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { diff --git a/trace/mem.h b/trace/mem.h index 2f27e7bdf0..699566c661 100644 --- a/trace/mem.h +++ b/trace/mem.h @@ -10,7 +10,7 @@ #ifndef TRACE__MEM_H #define TRACE__MEM_H -#include "tcg/tcg.h" +#include "exec/memopidx.h" #define TRACE_MEM_SZ_SHIFT_MASK 0xf /* size shift mask */ #define TRACE_MEM_SE (1ULL << 4) /* sign extended (y/n) */ @@ -19,45 +19,33 @@ #define TRACE_MEM_MMU_SHIFT 8 /* mmu idx */ /** - * trace_mem_build_info: + * trace_mem_get_info: * * Return a value for the 'info' argument in guest memory access traces. */ -static inline uint16_t trace_mem_build_info(int size_shift, bool sign_extend, - MemOp endianness, bool store, - unsigned int mmu_idx) +static inline uint16_t trace_mem_get_info(MemOpIdx oi, bool store) { + MemOp op = get_memop(oi); + uint32_t size_shift = op & MO_SIZE; + bool sign_extend = op & MO_SIGN; + bool big_endian = (op & MO_BSWAP) == MO_BE; uint16_t res; res = size_shift & TRACE_MEM_SZ_SHIFT_MASK; if (sign_extend) { res |= TRACE_MEM_SE; } - if (endianness == MO_BE) { + if (big_endian) { res |= TRACE_MEM_BE; } if (store) { res |= TRACE_MEM_ST; } #ifdef CONFIG_SOFTMMU - res |= mmu_idx << TRACE_MEM_MMU_SHIFT; + res |= get_mmuidx(oi) << TRACE_MEM_MMU_SHIFT; #endif + return res; } - -/** - * trace_mem_get_info: - * - * Return a value for the 'info' argument in guest memory access traces. - */ -static inline uint16_t trace_mem_get_info(MemOp op, - unsigned int mmu_idx, - bool store) -{ - return trace_mem_build_info(op & MO_SIZE, !!(op & MO_SIGN), - op & MO_BSWAP, store, - mmu_idx); -} - #endif /* TRACE__MEM_H */ From c3e83e376cf028fade97072d86f33e4a92ddf9a2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 26 Jul 2021 08:19:40 -1000 Subject: [PATCH 0290/1334] accel/tcg: Pass MemOpIdx to atomic_trace_*_post MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will shortly use the MemOpIdx directly, but in the meantime re-compute the trace meminfo. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/atomic_common.c.inc | 30 +++++++++++----------- accel/tcg/atomic_template.h | 48 +++++++++++++++++------------------ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 6019a957b9..db81eb5e66 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -13,55 +13,55 @@ * See the COPYING file in the top-level directory. */ -static uint16_t atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, - MemOpIdx oi) +static void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, + MemOpIdx oi) { CPUState *cpu = env_cpu(env); uint16_t info = trace_mem_get_info(oi, false); trace_guest_mem_before_exec(cpu, addr, info); trace_guest_mem_before_exec(cpu, addr, info | TRACE_MEM_ST); - - return info; } static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, - uint16_t info) + MemOpIdx oi) { + uint16_t info = trace_mem_get_info(oi, false); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info); qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info | TRACE_MEM_ST); } #if HAVE_ATOMIC128 -static uint16_t atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, - MemOpIdx oi) +static void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, + MemOpIdx oi) { uint16_t info = trace_mem_get_info(oi, false); trace_guest_mem_before_exec(env_cpu(env), addr, info); - - return info; } static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, - uint16_t info) + MemOpIdx oi) { + uint16_t info = trace_mem_get_info(oi, false); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info); } -static uint16_t atomic_trace_st_pre(CPUArchState *env, target_ulong addr, - MemOpIdx oi) +static void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, + MemOpIdx oi) { uint16_t info = trace_mem_get_info(oi, true); trace_guest_mem_before_exec(env_cpu(env), addr, info); - - return info; } static void atomic_trace_st_post(CPUArchState *env, target_ulong addr, - uint16_t info) + MemOpIdx oi) { + uint16_t info = trace_mem_get_info(oi, false); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info); } #endif diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 4230ff2957..c08d859a8a 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -77,15 +77,15 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr); DATA_TYPE ret; - uint16_t info = atomic_trace_rmw_pre(env, addr, oi); + atomic_trace_rmw_pre(env, addr, oi); #if DATA_SIZE == 16 ret = atomic16_cmpxchg(haddr, cmpv, newv); #else ret = qatomic_cmpxchg__nocheck(haddr, cmpv, newv); #endif ATOMIC_MMU_CLEANUP; - atomic_trace_rmw_post(env, addr, info); + atomic_trace_rmw_post(env, addr, oi); return ret; } @@ -97,11 +97,11 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ, retaddr); DATA_TYPE val; - uint16_t info = atomic_trace_ld_pre(env, addr, oi); + atomic_trace_ld_pre(env, addr, oi); val = atomic16_read(haddr); ATOMIC_MMU_CLEANUP; - atomic_trace_ld_post(env, addr, info); + atomic_trace_ld_post(env, addr, oi); return val; } @@ -110,11 +110,11 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, retaddr); - uint16_t info = atomic_trace_st_pre(env, addr, oi); + atomic_trace_st_pre(env, addr, oi); atomic16_set(haddr, val); ATOMIC_MMU_CLEANUP; - atomic_trace_st_post(env, addr, info); + atomic_trace_st_post(env, addr, oi); } #endif #else @@ -124,11 +124,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr); DATA_TYPE ret; - uint16_t info = atomic_trace_rmw_pre(env, addr, oi); + atomic_trace_rmw_pre(env, addr, oi); ret = qatomic_xchg__nocheck(haddr, val); ATOMIC_MMU_CLEANUP; - atomic_trace_rmw_post(env, addr, info); + atomic_trace_rmw_post(env, addr, oi); return ret; } @@ -139,10 +139,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ DATA_TYPE ret; \ - uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \ + atomic_trace_rmw_pre(env, addr, oi); \ ret = qatomic_##X(haddr, val); \ ATOMIC_MMU_CLEANUP; \ - atomic_trace_rmw_post(env, addr, info); \ + atomic_trace_rmw_post(env, addr, oi); \ return ret; \ } @@ -172,7 +172,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ XDATA_TYPE cmp, old, new, val = xval; \ - uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \ + atomic_trace_rmw_pre(env, addr, oi); \ smp_mb(); \ cmp = qatomic_read__nocheck(haddr); \ do { \ @@ -180,7 +180,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ cmp = qatomic_cmpxchg__nocheck(haddr, old, new); \ } while (cmp != old); \ ATOMIC_MMU_CLEANUP; \ - atomic_trace_rmw_post(env, addr, info); \ + atomic_trace_rmw_post(env, addr, oi); \ return RET; \ } @@ -216,15 +216,15 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr); DATA_TYPE ret; - uint16_t info = atomic_trace_rmw_pre(env, addr, oi); + atomic_trace_rmw_pre(env, addr, oi); #if DATA_SIZE == 16 ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); #else ret = qatomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); #endif ATOMIC_MMU_CLEANUP; - atomic_trace_rmw_post(env, addr, info); + atomic_trace_rmw_post(env, addr, oi); return BSWAP(ret); } @@ -236,11 +236,11 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ, retaddr); DATA_TYPE val; - uint16_t info = atomic_trace_ld_pre(env, addr, oi); + atomic_trace_ld_pre(env, addr, oi); val = atomic16_read(haddr); ATOMIC_MMU_CLEANUP; - atomic_trace_ld_post(env, addr, info); + atomic_trace_ld_post(env, addr, oi); return BSWAP(val); } @@ -249,12 +249,12 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, { DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, retaddr); - uint16_t info = atomic_trace_st_pre(env, addr, oi); + atomic_trace_st_pre(env, addr, oi); val = BSWAP(val); atomic16_set(haddr, val); ATOMIC_MMU_CLEANUP; - atomic_trace_st_post(env, addr, info); + atomic_trace_st_post(env, addr, oi); } #endif #else @@ -264,11 +264,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr); ABI_TYPE ret; - uint16_t info = atomic_trace_rmw_pre(env, addr, oi); + atomic_trace_rmw_pre(env, addr, oi); ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); ATOMIC_MMU_CLEANUP; - atomic_trace_rmw_post(env, addr, info); + atomic_trace_rmw_post(env, addr, oi); return BSWAP(ret); } @@ -279,10 +279,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ DATA_TYPE ret; \ - uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \ + atomic_trace_rmw_pre(env, addr, oi); \ ret = qatomic_##X(haddr, BSWAP(val)); \ ATOMIC_MMU_CLEANUP; \ - atomic_trace_rmw_post(env, addr, info); \ + atomic_trace_rmw_post(env, addr, oi); \ return BSWAP(ret); \ } @@ -309,7 +309,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ XDATA_TYPE ldo, ldn, old, new, val = xval; \ - uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \ + atomic_trace_rmw_pre(env, addr, oi); \ smp_mb(); \ ldn = qatomic_read__nocheck(haddr); \ do { \ @@ -317,7 +317,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ldn = qatomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \ } while (ldo != ldn); \ ATOMIC_MMU_CLEANUP; \ - atomic_trace_rmw_post(env, addr, info); \ + atomic_trace_rmw_post(env, addr, oi); \ return RET; \ } From 37aff08726b533c5df6a5a8685cca8a0de5e6619 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 26 Jul 2021 11:48:30 -1000 Subject: [PATCH 0291/1334] plugins: Reorg arguments to qemu_plugin_vcpu_mem_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the MemOpIdx directly, rather than the rearrangement of the same bits currently done by the trace infrastructure. Pass in enum qemu_plugin_mem_rw so that we are able to treat read-modify-write operations as a single operation. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/atomic_common.c.inc | 13 +++---------- accel/tcg/cputlb.c | 4 ++-- accel/tcg/plugin-gen.c | 5 ++--- accel/tcg/user-exec.c | 28 ++++++++++++++-------------- include/qemu/plugin.h | 26 ++++++++++++++++++++++++-- plugins/api.c | 19 +++++++++++-------- plugins/core.c | 10 +++++----- tcg/tcg-op.c | 30 +++++++++++++++++++++--------- 8 files changed, 82 insertions(+), 53 deletions(-) diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index db81eb5e66..f3ab96e888 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -26,10 +26,7 @@ static void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, MemOpIdx oi) { - uint16_t info = trace_mem_get_info(oi, false); - - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info | TRACE_MEM_ST); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_RW); } #if HAVE_ATOMIC128 @@ -44,9 +41,7 @@ static void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, MemOpIdx oi) { - uint16_t info = trace_mem_get_info(oi, false); - - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); } static void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, @@ -60,9 +55,7 @@ static void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, static void atomic_trace_st_post(CPUArchState *env, target_ulong addr, MemOpIdx oi) { - uint16_t info = trace_mem_get_info(oi, false); - - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } #endif diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 0aa6157ec4..ee07457880 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2120,7 +2120,7 @@ static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, ret = full_load(env, addr, oi, retaddr); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); return ret; } @@ -2556,7 +2556,7 @@ cpu_store_helper(CPUArchState *env, target_ulong addr, uint64_t val, store_helper(env, addr, val, oi, retaddr, op); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } void cpu_stb_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 88e25c6df9..f5fd5f279c 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -45,7 +45,6 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" #include "tcg/tcg-op.h" -#include "trace/mem.h" #include "exec/exec-all.h" #include "exec/plugin-gen.h" #include "exec/translator.h" @@ -211,9 +210,9 @@ static void gen_mem_wrapped(enum plugin_gen_cb type, const union mem_gen_fn *f, TCGv addr, uint32_t info, bool is_mem) { - int wr = !!(info & TRACE_MEM_ST); + enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); - gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, wr); + gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, rw); if (is_mem) { f->mem_fn(addr, info); } else { diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 3ba7acf7f4..13e0b9e430 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -894,7 +894,7 @@ uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldub_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; } @@ -911,7 +911,7 @@ uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = lduw_be_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; } @@ -928,7 +928,7 @@ uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldl_be_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; } @@ -940,7 +940,7 @@ uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldq_be_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; } @@ -952,7 +952,7 @@ uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = lduw_le_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; } @@ -969,7 +969,7 @@ uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldl_le_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; } @@ -981,7 +981,7 @@ uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); ret = ldq_le_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; } @@ -1077,7 +1077,7 @@ void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stb_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) @@ -1087,7 +1087,7 @@ void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stw_be_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) @@ -1097,7 +1097,7 @@ void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stl_be_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val) @@ -1107,7 +1107,7 @@ void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stq_be_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) @@ -1117,7 +1117,7 @@ void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stw_le_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) @@ -1127,7 +1127,7 @@ void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stl_le_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val) @@ -1137,7 +1137,7 @@ void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val) trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); stq_le_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr, diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 9a8438f683..b3172b147f 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -12,6 +12,7 @@ #include "qemu/error-report.h" #include "qemu/queue.h" #include "qemu/option.h" +#include "exec/memopidx.h" /* * Events that plugins can subscribe to. @@ -36,6 +37,25 @@ enum qemu_plugin_event { struct qemu_plugin_desc; typedef QTAILQ_HEAD(, qemu_plugin_desc) QemuPluginList; +/* + * Construct a qemu_plugin_meminfo_t. + */ +static inline qemu_plugin_meminfo_t +make_plugin_meminfo(MemOpIdx oi, enum qemu_plugin_mem_rw rw) +{ + return oi | (rw << 16); +} + +/* + * Extract the memory operation direction from a qemu_plugin_meminfo_t. + * Other portions may be extracted via get_memop and get_mmuidx. + */ +static inline enum qemu_plugin_mem_rw +get_plugin_meminfo_rw(qemu_plugin_meminfo_t i) +{ + return i >> 16; +} + #ifdef CONFIG_PLUGIN extern QemuOptsList qemu_plugin_opts; @@ -180,7 +200,8 @@ qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a6, uint64_t a7, uint64_t a8); void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret); -void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, uint32_t meminfo); +void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, + MemOpIdx oi, enum qemu_plugin_mem_rw rw); void qemu_plugin_flush_cb(void); @@ -244,7 +265,8 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret) { } static inline void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, - uint32_t meminfo) + MemOpIdx oi, + enum qemu_plugin_mem_rw rw) { } static inline void qemu_plugin_flush_cb(void) diff --git a/plugins/api.c b/plugins/api.c index acff9ce8ac..b143b09ce9 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -45,7 +45,6 @@ #include "qemu/plugin-memory.h" #include "hw/boards.h" #endif -#include "trace/mem.h" /* Uninstall and Reset handlers */ @@ -246,22 +245,25 @@ const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) { - return info & TRACE_MEM_SZ_SHIFT_MASK; + MemOp op = get_memop(info); + return op & MO_SIZE; } bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) { - return !!(info & TRACE_MEM_SE); + MemOp op = get_memop(info); + return op & MO_SIGN; } bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) { - return !!(info & TRACE_MEM_BE); + MemOp op = get_memop(info); + return (op & MO_BSWAP) == MO_BE; } bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) { - return !!(info & TRACE_MEM_ST); + return get_plugin_meminfo_rw(info) & QEMU_PLUGIN_MEM_W; } /* @@ -277,11 +279,12 @@ struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, { #ifdef CONFIG_SOFTMMU CPUState *cpu = current_cpu; - unsigned int mmu_idx = info >> TRACE_MEM_MMU_SHIFT; - hwaddr_info.is_store = info & TRACE_MEM_ST; + unsigned int mmu_idx = get_mmuidx(info); + enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); + hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, - info & TRACE_MEM_ST, &hwaddr_info)) { + hwaddr_info.is_store, &hwaddr_info)) { error_report("invalid use of qemu_plugin_get_hwaddr"); return NULL; } diff --git a/plugins/core.c b/plugins/core.c index 6b2490f973..792262da08 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -27,7 +27,6 @@ #include "exec/helper-proto.h" #include "tcg/tcg.h" #include "tcg/tcg-op.h" -#include "trace/mem.h" /* mem_info macros */ #include "plugin.h" #include "qemu/compiler.h" @@ -446,7 +445,8 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb) } } -void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, uint32_t info) +void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, + MemOpIdx oi, enum qemu_plugin_mem_rw rw) { GArray *arr = cpu->plugin_mem_cbs; size_t i; @@ -457,14 +457,14 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, uint32_t info) for (i = 0; i < arr->len; i++) { struct qemu_plugin_dyn_cb *cb = &g_array_index(arr, struct qemu_plugin_dyn_cb, i); - int w = !!(info & TRACE_MEM_ST) + 1; - if (!(w & cb->rw)) { + if (!(rw & cb->rw)) { break; } switch (cb->type) { case PLUGIN_CB_REGULAR: - cb->f.vcpu_mem(cpu->cpu_index, info, vaddr, cb->userp); + cb->f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), + vaddr, cb->userp); break; case PLUGIN_CB_INLINE: exec_inline_op(cb); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 37b440af7f..af7bb851b5 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2853,10 +2853,12 @@ static inline TCGv plugin_prep_mem_callbacks(TCGv vaddr) return vaddr; } -static inline void plugin_gen_mem_callbacks(TCGv vaddr, uint16_t info) +static void plugin_gen_mem_callbacks(TCGv vaddr, MemOpIdx oi, + enum qemu_plugin_mem_rw rw) { #ifdef CONFIG_PLUGIN if (tcg_ctx->plugin_insn != NULL) { + qemu_plugin_meminfo_t info = make_plugin_meminfo(oi, rw); plugin_gen_empty_mem_callback(vaddr, info); tcg_temp_free(vaddr); } @@ -2866,10 +2868,13 @@ static inline void plugin_gen_mem_callbacks(TCGv vaddr, uint16_t info) void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) { MemOp orig_memop; - uint16_t info = trace_mem_get_info(make_memop_idx(memop, idx), 0); + MemOpIdx oi; + uint16_t info; tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 0, 0); + oi = make_memop_idx(memop, idx); + info = trace_mem_get_info(oi, 0); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); orig_memop = memop; @@ -2883,7 +2888,7 @@ void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) addr = plugin_prep_mem_callbacks(addr); gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx); - plugin_gen_mem_callbacks(addr, info); + plugin_gen_mem_callbacks(addr, oi, QEMU_PLUGIN_MEM_R); if ((orig_memop ^ memop) & MO_BSWAP) { switch (orig_memop & MO_SIZE) { @@ -2904,10 +2909,13 @@ void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) { TCGv_i32 swap = NULL; - uint16_t info = trace_mem_get_info(make_memop_idx(memop, idx), 1); + MemOpIdx oi; + uint16_t info; tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 0, 1); + oi = make_memop_idx(memop, idx); + info = trace_mem_get_info(oi, 1); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { @@ -2932,7 +2940,7 @@ void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) } else { gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx); } - plugin_gen_mem_callbacks(addr, info); + plugin_gen_mem_callbacks(addr, oi, QEMU_PLUGIN_MEM_W); if (swap) { tcg_temp_free_i32(swap); @@ -2942,6 +2950,7 @@ void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) { MemOp orig_memop; + MemOpIdx oi; uint16_t info; if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { @@ -2956,7 +2965,8 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 1, 0); - info = trace_mem_get_info(make_memop_idx(memop, idx), 0); + oi = make_memop_idx(memop, idx); + info = trace_mem_get_info(oi, 0); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); orig_memop = memop; @@ -2970,7 +2980,7 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) addr = plugin_prep_mem_callbacks(addr); gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx); - plugin_gen_mem_callbacks(addr, info); + plugin_gen_mem_callbacks(addr, oi, QEMU_PLUGIN_MEM_R); if ((orig_memop ^ memop) & MO_BSWAP) { int flags = (orig_memop & MO_SIGN @@ -2995,6 +3005,7 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) { TCGv_i64 swap = NULL; + MemOpIdx oi; uint16_t info; if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { @@ -3004,7 +3015,8 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 1, 1); - info = trace_mem_get_info(make_memop_idx(memop, idx), 1); + oi = make_memop_idx(memop, idx); + info = trace_mem_get_info(oi, 1); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { @@ -3028,7 +3040,7 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) addr = plugin_prep_mem_callbacks(addr); gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx); - plugin_gen_mem_callbacks(addr, info); + plugin_gen_mem_callbacks(addr, oi, QEMU_PLUGIN_MEM_W); if (swap) { tcg_temp_free_i64(swap); From 0583f775d2740f64739febf6496d9207552399f6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 26 Jul 2021 13:21:38 -1000 Subject: [PATCH 0292/1334] trace: Split guest_mem_before MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no point in encoding load/store within a bit of the memory trace info operand. Represent atomic operations as a single read-modify-write tracepoint. Use MemOpIdx instead of inventing a form specifically for traces. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/atomic_common.c.inc | 12 +++------ accel/tcg/atomic_template.h | 1 - accel/tcg/cputlb.c | 7 ++--- accel/tcg/user-exec.c | 44 +++++++++++------------------- tcg/tcg-op.c | 17 +++--------- trace-events | 18 +++---------- trace/mem.h | 51 ----------------------------------- 7 files changed, 28 insertions(+), 122 deletions(-) delete mode 100644 trace/mem.h diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index f3ab96e888..1df1f243e9 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -17,10 +17,8 @@ static void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, MemOpIdx oi) { CPUState *cpu = env_cpu(env); - uint16_t info = trace_mem_get_info(oi, false); - trace_guest_mem_before_exec(cpu, addr, info); - trace_guest_mem_before_exec(cpu, addr, info | TRACE_MEM_ST); + trace_guest_rmw_before_exec(cpu, addr, oi); } static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, @@ -33,9 +31,7 @@ static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, static void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, MemOpIdx oi) { - uint16_t info = trace_mem_get_info(oi, false); - - trace_guest_mem_before_exec(env_cpu(env), addr, info); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); } static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, @@ -47,9 +43,7 @@ static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, static void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, MemOpIdx oi) { - uint16_t info = trace_mem_get_info(oi, true); - - trace_guest_mem_before_exec(env_cpu(env), addr, info); + trace_guest_st_before_exec(env_cpu(env), addr, oi); } static void atomic_trace_st_post(CPUArchState *env, target_ulong addr, diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index c08d859a8a..2d917b6b1f 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -19,7 +19,6 @@ */ #include "qemu/plugin.h" -#include "trace/mem.h" #if DATA_SIZE == 16 # define SUFFIX o diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index ee07457880..46140ccff3 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -34,7 +34,6 @@ #include "qemu/atomic128.h" #include "exec/translate-all.h" #include "trace/trace-root.h" -#include "trace/mem.h" #include "tb-hash.h" #include "internal.h" #ifdef CONFIG_PLUGIN @@ -2113,10 +2112,9 @@ static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, MemOp op, FullLoadHelper *full_load) { MemOpIdx oi = make_memop_idx(op, mmu_idx); - uint16_t meminfo = trace_mem_get_info(oi, false); uint64_t ret; - trace_guest_mem_before_exec(env_cpu(env), addr, meminfo); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); ret = full_load(env, addr, oi, retaddr); @@ -2550,9 +2548,8 @@ cpu_store_helper(CPUArchState *env, target_ulong addr, uint64_t val, int mmu_idx, uintptr_t retaddr, MemOp op) { MemOpIdx oi = make_memop_idx(op, mmu_idx); - uint16_t meminfo = trace_mem_get_info(oi, true); - trace_guest_mem_before_exec(env_cpu(env), addr, meminfo); + trace_guest_st_before_exec(env_cpu(env), addr, oi); store_helper(env, addr, val, oi, retaddr, op); diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 13e0b9e430..65d3c9b286 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -27,7 +27,7 @@ #include "exec/helper-proto.h" #include "qemu/atomic128.h" #include "trace/trace-root.h" -#include "trace/mem.h" +#include "internal.h" #undef EAX #undef ECX @@ -889,10 +889,9 @@ int cpu_signal_handler(int host_signum, void *pinfo, uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr) { MemOpIdx oi = make_memop_idx(MO_UB, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_ld_before_exec(env_cpu(env), ptr, oi); ret = ldub_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; @@ -906,10 +905,9 @@ int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr) uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr) { MemOpIdx oi = make_memop_idx(MO_BEUW, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_ld_before_exec(env_cpu(env), ptr, oi); ret = lduw_be_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; @@ -923,10 +921,9 @@ int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr) uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr) { MemOpIdx oi = make_memop_idx(MO_BEUL, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_ld_before_exec(env_cpu(env), ptr, oi); ret = ldl_be_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; @@ -935,10 +932,9 @@ uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr) uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr) { MemOpIdx oi = make_memop_idx(MO_BEQ, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, false); uint64_t ret; - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_ld_before_exec(env_cpu(env), ptr, oi); ret = ldq_be_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; @@ -947,10 +943,9 @@ uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr) uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr) { MemOpIdx oi = make_memop_idx(MO_LEUW, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_ld_before_exec(env_cpu(env), ptr, oi); ret = lduw_le_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; @@ -964,10 +959,9 @@ int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr) uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr) { MemOpIdx oi = make_memop_idx(MO_LEUL, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, false); uint32_t ret; - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_ld_before_exec(env_cpu(env), ptr, oi); ret = ldl_le_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; @@ -976,10 +970,9 @@ uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr) uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr) { MemOpIdx oi = make_memop_idx(MO_LEQ, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, false); uint64_t ret; - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_ld_before_exec(env_cpu(env), ptr, oi); ret = ldq_le_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); return ret; @@ -1073,9 +1066,8 @@ uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { MemOpIdx oi = make_memop_idx(MO_UB, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, true); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_st_before_exec(env_cpu(env), ptr, oi); stb_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } @@ -1083,9 +1075,8 @@ void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { MemOpIdx oi = make_memop_idx(MO_BEUW, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, true); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_st_before_exec(env_cpu(env), ptr, oi); stw_be_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } @@ -1093,9 +1084,8 @@ void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { MemOpIdx oi = make_memop_idx(MO_BEUL, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, true); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_st_before_exec(env_cpu(env), ptr, oi); stl_be_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } @@ -1103,9 +1093,8 @@ void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val) { MemOpIdx oi = make_memop_idx(MO_BEQ, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, true); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_st_before_exec(env_cpu(env), ptr, oi); stq_be_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } @@ -1113,9 +1102,8 @@ void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val) void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { MemOpIdx oi = make_memop_idx(MO_LEUW, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, true); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_st_before_exec(env_cpu(env), ptr, oi); stw_le_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } @@ -1123,9 +1111,8 @@ void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) { MemOpIdx oi = make_memop_idx(MO_LEUL, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, true); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_st_before_exec(env_cpu(env), ptr, oi); stl_le_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } @@ -1133,9 +1120,8 @@ void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val) { MemOpIdx oi = make_memop_idx(MO_LEQ, MMU_USER_IDX); - uint16_t meminfo = trace_mem_get_info(oi, true); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + trace_guest_st_before_exec(env_cpu(env), ptr, oi); stq_le_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); } diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index af7bb851b5..b1cfd36f29 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -28,7 +28,6 @@ #include "tcg/tcg-op.h" #include "tcg/tcg-mo.h" #include "trace-tcg.h" -#include "trace/mem.h" #include "exec/plugin-gen.h" /* Reduce the number of ifdefs below. This assumes that all uses of @@ -2869,13 +2868,11 @@ void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) { MemOp orig_memop; MemOpIdx oi; - uint16_t info; tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 0, 0); oi = make_memop_idx(memop, idx); - info = trace_mem_get_info(oi, 0); - trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); + trace_guest_ld_before_tcg(tcg_ctx->cpu, cpu_env, addr, oi); orig_memop = memop; if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { @@ -2910,13 +2907,11 @@ void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) { TCGv_i32 swap = NULL; MemOpIdx oi; - uint16_t info; tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 0, 1); oi = make_memop_idx(memop, idx); - info = trace_mem_get_info(oi, 1); - trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); + trace_guest_st_before_tcg(tcg_ctx->cpu, cpu_env, addr, oi); if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { swap = tcg_temp_new_i32(); @@ -2951,7 +2946,6 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) { MemOp orig_memop; MemOpIdx oi; - uint16_t info; if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop); @@ -2966,8 +2960,7 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 1, 0); oi = make_memop_idx(memop, idx); - info = trace_mem_get_info(oi, 0); - trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); + trace_guest_ld_before_tcg(tcg_ctx->cpu, cpu_env, addr, oi); orig_memop = memop; if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { @@ -3006,7 +2999,6 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) { TCGv_i64 swap = NULL; MemOpIdx oi; - uint16_t info; if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop); @@ -3016,8 +3008,7 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 1, 1); oi = make_memop_idx(memop, idx); - info = trace_mem_get_info(oi, 1); - trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info); + trace_guest_st_before_tcg(tcg_ctx->cpu, cpu_env, addr, oi); if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { swap = tcg_temp_new_i64(); diff --git a/trace-events b/trace-events index c4cca29939..a637a61eba 100644 --- a/trace-events +++ b/trace-events @@ -120,26 +120,16 @@ vcpu guest_cpu_reset(void) # tcg/tcg-op.c # @vaddr: Access' virtual address. -# @info : Access' information (see below). +# @memopidx: Access' information (see below). # # Start virtual memory access (before any potential access violation). -# # Does not include memory accesses performed by devices. # -# Access information can be parsed as: -# -# struct mem_info { -# uint8_t size_shift : 4; /* interpreted as "1 << size_shift" bytes */ -# bool sign_extend: 1; /* sign-extended */ -# uint8_t endianness : 1; /* 0: little, 1: big */ -# bool store : 1; /* whether it is a store operation */ -# pad : 1; -# uint8_t mmuidx : 4; /* mmuidx (softmmu only) */ -# }; -# # Mode: user, softmmu # Targets: TCG(all) -vcpu tcg guest_mem_before(TCGv vaddr, uint16_t info) "info=%d", "vaddr=0x%016"PRIx64" info=%d" +vcpu tcg guest_ld_before(TCGv vaddr, uint32_t memopidx) "info=%d", "vaddr=0x%016"PRIx64" memopidx=0x%x" +vcpu tcg guest_st_before(TCGv vaddr, uint32_t memopidx) "info=%d", "vaddr=0x%016"PRIx64" memopidx=0x%x" +vcpu tcg guest_rmw_before(TCGv vaddr, uint32_t memopidx) "info=%d", "vaddr=0x%016"PRIx64" memopidx=0x%x" # include/user/syscall-trace.h diff --git a/trace/mem.h b/trace/mem.h deleted file mode 100644 index 699566c661..0000000000 --- a/trace/mem.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Helper functions for guest memory tracing - * - * Copyright (C) 2016 Lluís Vilanova - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef TRACE__MEM_H -#define TRACE__MEM_H - -#include "exec/memopidx.h" - -#define TRACE_MEM_SZ_SHIFT_MASK 0xf /* size shift mask */ -#define TRACE_MEM_SE (1ULL << 4) /* sign extended (y/n) */ -#define TRACE_MEM_BE (1ULL << 5) /* big endian (y/n) */ -#define TRACE_MEM_ST (1ULL << 6) /* store (y/n) */ -#define TRACE_MEM_MMU_SHIFT 8 /* mmu idx */ - -/** - * trace_mem_get_info: - * - * Return a value for the 'info' argument in guest memory access traces. - */ -static inline uint16_t trace_mem_get_info(MemOpIdx oi, bool store) -{ - MemOp op = get_memop(oi); - uint32_t size_shift = op & MO_SIZE; - bool sign_extend = op & MO_SIGN; - bool big_endian = (op & MO_BSWAP) == MO_BE; - uint16_t res; - - res = size_shift & TRACE_MEM_SZ_SHIFT_MASK; - if (sign_extend) { - res |= TRACE_MEM_SE; - } - if (big_endian) { - res |= TRACE_MEM_BE; - } - if (store) { - res |= TRACE_MEM_ST; - } -#ifdef CONFIG_SOFTMMU - res |= get_mmuidx(oi) << TRACE_MEM_MMU_SHIFT; -#endif - - return res; -} - -#endif /* TRACE__MEM_H */ From dc29f4746fc8c641fb1182495c2662381cc16e23 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 30 Jul 2021 15:20:45 -1000 Subject: [PATCH 0293/1334] hw/core/cpu: Re-sort the non-pointers to the end of CPUClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Despite the comment, the members were not kept at the end. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/hw/core/cpu.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index bc864564ce..b7d5bc1200 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -131,7 +131,6 @@ struct CPUClass { ObjectClass *(*class_by_name)(const char *cpu_model); void (*parse_features)(const char *typename, char *str, Error **errp); - int reset_dump_flags; bool (*has_work)(CPUState *cpu); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, uint8_t *buf, int len, bool is_write); @@ -149,9 +148,6 @@ struct CPUClass { void (*disas_set_info)(CPUState *cpu, disassemble_info *info); const char *deprecation_note; - /* Keep non-pointer data at the end to minimize holes. */ - int gdb_num_core_regs; - bool gdb_stop_before_watchpoint; struct AccelCPUClass *accel_cpu; /* when system emulation is not available, this pointer is NULL */ @@ -165,6 +161,13 @@ struct CPUClass { * class data that depends on the accelerator, see accel/accel-common.c. */ void (*init_accel_cpu)(struct AccelCPUClass *accel_cpu, CPUClass *cc); + + /* + * Keep non-pointer data at the end to minimize holes. + */ + int reset_dump_flags; + int gdb_num_core_regs; + bool gdb_stop_before_watchpoint; }; /* From 2552d60ebd6c519027d74bd452db7580b90e5cbd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 19:25:58 -0700 Subject: [PATCH 0294/1334] tcg: Expand usadd/ussub with umin/umax For usadd, we only have to consider overflow. Since ~B + B == -1, the maximum value for A that saturates is ~B. For ussub, we only have to consider underflow. The minimum value that saturates to 0 from A - B is B. Signed-off-by: Richard Henderson --- tcg/tcg-op-vec.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 15e026ae49..faf30f9cdd 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -119,6 +119,18 @@ bool tcg_can_emit_vecop_list(const TCGOpcode *list, continue; } break; + case INDEX_op_usadd_vec: + if (tcg_can_emit_vec_op(INDEX_op_umin_vec, type, vece) || + tcg_can_emit_vec_op(INDEX_op_cmp_vec, type, vece)) { + continue; + } + break; + case INDEX_op_ussub_vec: + if (tcg_can_emit_vec_op(INDEX_op_umax_vec, type, vece) || + tcg_can_emit_vec_op(INDEX_op_cmp_vec, type, vece)) { + continue; + } + break; case INDEX_op_cmpsel_vec: case INDEX_op_smin_vec: case INDEX_op_smax_vec: @@ -603,7 +615,18 @@ void tcg_gen_ssadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) void tcg_gen_usadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) { - do_op3_nofail(vece, r, a, b, INDEX_op_usadd_vec); + if (!do_op3(vece, r, a, b, INDEX_op_usadd_vec)) { + const TCGOpcode *hold_list = tcg_swap_vecop_list(NULL); + TCGv_vec t = tcg_temp_new_vec_matching(r); + + /* usadd(a, b) = min(a, ~b) + b */ + tcg_gen_not_vec(vece, t, b); + tcg_gen_umin_vec(vece, t, t, a); + tcg_gen_add_vec(vece, r, t, b); + + tcg_temp_free_vec(t); + tcg_swap_vecop_list(hold_list); + } } void tcg_gen_sssub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) @@ -613,7 +636,17 @@ void tcg_gen_sssub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) void tcg_gen_ussub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) { - do_op3_nofail(vece, r, a, b, INDEX_op_ussub_vec); + if (!do_op3(vece, r, a, b, INDEX_op_ussub_vec)) { + const TCGOpcode *hold_list = tcg_swap_vecop_list(NULL); + TCGv_vec t = tcg_temp_new_vec_matching(r); + + /* ussub(a, b) = max(a, b) - b */ + tcg_gen_umax_vec(vece, t, a, b); + tcg_gen_sub_vec(vece, r, t, b); + + tcg_temp_free_vec(t); + tcg_swap_vecop_list(hold_list); + } } static void do_minmax(unsigned vece, TCGv_vec r, TCGv_vec a, From 3704993f545efbe7b5a9ede83525f0d9c07cb31f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 15:15:31 -0700 Subject: [PATCH 0295/1334] tcg/s390x: Rename from tcg/s390 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This emphasizes that we don't support s390, only 64-bit s390x hosts. Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- meson.build | 2 -- tcg/{s390 => s390x}/tcg-target-con-set.h | 0 tcg/{s390 => s390x}/tcg-target-con-str.h | 0 tcg/{s390 => s390x}/tcg-target.c.inc | 0 tcg/{s390 => s390x}/tcg-target.h | 0 5 files changed, 2 deletions(-) rename tcg/{s390 => s390x}/tcg-target-con-set.h (100%) rename tcg/{s390 => s390x}/tcg-target-con-str.h (100%) rename tcg/{s390 => s390x}/tcg-target.c.inc (100%) rename tcg/{s390 => s390x}/tcg-target.h (100%) diff --git a/meson.build b/meson.build index 7b596fdcd9..99a0a3e689 100644 --- a/meson.build +++ b/meson.build @@ -268,8 +268,6 @@ if not get_option('tcg').disabled() tcg_arch = 'tci' elif config_host['ARCH'] == 'sparc64' tcg_arch = 'sparc' - elif config_host['ARCH'] == 's390x' - tcg_arch = 's390' elif config_host['ARCH'] in ['x86_64', 'x32'] tcg_arch = 'i386' elif config_host['ARCH'] == 'ppc64' diff --git a/tcg/s390/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h similarity index 100% rename from tcg/s390/tcg-target-con-set.h rename to tcg/s390x/tcg-target-con-set.h diff --git a/tcg/s390/tcg-target-con-str.h b/tcg/s390x/tcg-target-con-str.h similarity index 100% rename from tcg/s390/tcg-target-con-str.h rename to tcg/s390x/tcg-target-con-str.h diff --git a/tcg/s390/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc similarity index 100% rename from tcg/s390/tcg-target.c.inc rename to tcg/s390x/tcg-target.c.inc diff --git a/tcg/s390/tcg-target.h b/tcg/s390x/tcg-target.h similarity index 100% rename from tcg/s390/tcg-target.h rename to tcg/s390x/tcg-target.h From 748b7f3ef78654f0ed11c85ed0b34a02f97ea856 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 27 Apr 2019 16:51:09 -0700 Subject: [PATCH 0296/1334] tcg/s390x: Change FACILITY representation We will shortly need to be able to check facilities beyond the first 64. Instead of explicitly masking against s390_facilities, create a HAVE_FACILITY macro that indexes an array. Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- v2: Change name to HAVE_FACILITY (david) --- tcg/s390x/tcg-target.c.inc | 74 +++++++++++++++++++------------------- tcg/s390x/tcg-target.h | 29 ++++++++------- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index fd0b3316d2..a224244b52 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -66,7 +66,7 @@ We don't need this when we have pc-relative loads with the general instructions extension facility. */ #define TCG_REG_TB TCG_REG_R12 -#define USE_REG_TB (!(s390_facilities & FACILITY_GEN_INST_EXT)) +#define USE_REG_TB (!HAVE_FACILITY(GEN_INST_EXT)) #ifndef CONFIG_SOFTMMU #define TCG_GUEST_BASE_REG TCG_REG_R13 @@ -377,7 +377,7 @@ static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { #endif static const tcg_insn_unit *tb_ret_addr; -uint64_t s390_facilities; +uint64_t s390_facilities[1]; static bool patch_reloc(tcg_insn_unit *src_rw, int type, intptr_t value, intptr_t addend) @@ -577,7 +577,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, } /* Try all 48-bit insns that can load it in one go. */ - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { if (sval == (int32_t)sval) { tcg_out_insn(s, RIL, LGFI, ret, sval); return; @@ -620,7 +620,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, } /* Otherwise, stuff it in the constant pool. */ - if (s390_facilities & FACILITY_GEN_INST_EXT) { + if (HAVE_FACILITY(GEN_INST_EXT)) { tcg_out_insn(s, RIL, LGRL, ret, 0); new_pool_label(s, sval, R_390_PC32DBL, s->code_ptr - 2, 2); } else if (USE_REG_TB && !in_prologue) { @@ -706,7 +706,7 @@ static void tcg_out_ld_abs(TCGContext *s, TCGType type, { intptr_t addr = (intptr_t)abs; - if ((s390_facilities & FACILITY_GEN_INST_EXT) && !(addr & 1)) { + if (HAVE_FACILITY(GEN_INST_EXT) && !(addr & 1)) { ptrdiff_t disp = tcg_pcrel_diff(s, abs) >> 1; if (disp == (int32_t)disp) { if (type == TCG_TYPE_I32) { @@ -740,7 +740,7 @@ static inline void tcg_out_risbg(TCGContext *s, TCGReg dest, TCGReg src, static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { tcg_out_insn(s, RRE, LGBR, dest, src); return; } @@ -760,7 +760,7 @@ static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { tcg_out_insn(s, RRE, LLGCR, dest, src); return; } @@ -780,7 +780,7 @@ static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { tcg_out_insn(s, RRE, LGHR, dest, src); return; } @@ -800,7 +800,7 @@ static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) static void tgen_ext16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { tcg_out_insn(s, RRE, LLGHR, dest, src); return; } @@ -888,7 +888,7 @@ static void tgen_andi(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) tgen_ext32u(s, dest, dest); return; } - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { if ((val & valid) == 0xff) { tgen_ext8u(s, TCG_TYPE_I64, dest, dest); return; @@ -909,7 +909,7 @@ static void tgen_andi(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) } /* Try all 48-bit insns that can perform it in one go. */ - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { for (i = 0; i < 2; i++) { tcg_target_ulong mask = ~(0xffffffffull << i*32); if (((val | ~valid) & mask) == mask) { @@ -918,7 +918,7 @@ static void tgen_andi(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) } } } - if ((s390_facilities & FACILITY_GEN_INST_EXT) && risbg_mask(val)) { + if (HAVE_FACILITY(GEN_INST_EXT) && risbg_mask(val)) { tgen_andi_risbg(s, dest, dest, val); return; } @@ -967,7 +967,7 @@ static void tgen_ori(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) } /* Try all 48-bit insns that can perform it in one go. */ - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { for (i = 0; i < 2; i++) { tcg_target_ulong mask = (0xffffffffull << i*32); if ((val & mask) != 0 && (val & ~mask) == 0) { @@ -992,7 +992,7 @@ static void tgen_ori(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) /* Perform the OR via sequential modifications to the high and low parts. Do this via recursion to handle 16-bit vs 32-bit masks in each half. */ - tcg_debug_assert(s390_facilities & FACILITY_EXT_IMM); + tcg_debug_assert(HAVE_FACILITY(EXT_IMM)); tgen_ori(s, type, dest, val & 0x00000000ffffffffull); tgen_ori(s, type, dest, val & 0xffffffff00000000ull); } @@ -1001,7 +1001,7 @@ static void tgen_ori(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) static void tgen_xori(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) { /* Try all 48-bit insns that can perform it in one go. */ - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { if ((val & 0xffffffff00000000ull) == 0) { tcg_out_insn(s, RIL, XILF, dest, val); return; @@ -1025,7 +1025,7 @@ static void tgen_xori(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) tcg_tbrel_diff(s, NULL)); } else { /* Perform the xor by parts. */ - tcg_debug_assert(s390_facilities & FACILITY_EXT_IMM); + tcg_debug_assert(HAVE_FACILITY(EXT_IMM)); if (val & 0xffffffff) { tcg_out_insn(s, RIL, XILF, dest, val); } @@ -1059,7 +1059,7 @@ static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, goto exit; } - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { if (type == TCG_TYPE_I32) { op = (is_unsigned ? RIL_CLFI : RIL_CFI); tcg_out_insn_RIL(s, op, r1, c2); @@ -1122,7 +1122,7 @@ static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, bool have_loc; /* With LOC2, we can always emit the minimum 3 insns. */ - if (s390_facilities & FACILITY_LOAD_ON_COND2) { + if (HAVE_FACILITY(LOAD_ON_COND2)) { /* Emit: d = 0, d = (cc ? 1 : d). */ cc = tgen_cmp(s, type, cond, c1, c2, c2const, false); tcg_out_movi(s, TCG_TYPE_I64, dest, 0); @@ -1130,7 +1130,7 @@ static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, return; } - have_loc = (s390_facilities & FACILITY_LOAD_ON_COND) != 0; + have_loc = HAVE_FACILITY(LOAD_ON_COND); /* For HAVE_LOC, only the paths through GTU/GT/LEU/LE are smaller. */ restart: @@ -1216,7 +1216,7 @@ static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, TCGArg v3, int v3const) { int cc; - if (s390_facilities & FACILITY_LOAD_ON_COND) { + if (HAVE_FACILITY(LOAD_ON_COND)) { cc = tgen_cmp(s, type, c, c1, c2, c2const, false); if (v3const) { tcg_out_insn(s, RIE, LOCGHI, dest, v3, cc); @@ -1249,7 +1249,7 @@ static void tgen_clz(TCGContext *s, TCGReg dest, TCGReg a1, } else { tcg_out_mov(s, TCG_TYPE_I64, dest, a2); } - if (s390_facilities & FACILITY_LOAD_ON_COND) { + if (HAVE_FACILITY(LOAD_ON_COND)) { /* Emit: if (one bit found) dest = r0. */ tcg_out_insn(s, RRF, LOCGR, dest, TCG_REG_R0, 2); } else { @@ -1325,7 +1325,7 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, { int cc; - if (s390_facilities & FACILITY_GEN_INST_EXT) { + if (HAVE_FACILITY(GEN_INST_EXT)) { bool is_unsigned = is_unsigned_cond(c); bool in_range; S390Opcode opc; @@ -1519,7 +1519,7 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, MemOp opc, cross pages using the address of the last byte of the access. */ a_off = (a_bits >= s_bits ? 0 : s_mask - a_mask); tlb_mask = (uint64_t)TARGET_PAGE_MASK | a_mask; - if ((s390_facilities & FACILITY_GEN_INST_EXT) && a_off == 0) { + if (HAVE_FACILITY(GEN_INST_EXT) && a_off == 0) { tgen_andi_risbg(s, TCG_REG_R3, addr_reg, tlb_mask); } else { tcg_out_insn(s, RX, LA, TCG_REG_R3, addr_reg, TCG_REG_NONE, a_off); @@ -1810,7 +1810,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, RI, AHI, a0, a2); break; } - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { tcg_out_insn(s, RIL, AFI, a0, a2); break; } @@ -2056,7 +2056,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, RI, AGHI, a0, a2); break; } - if (s390_facilities & FACILITY_EXT_IMM) { + if (HAVE_FACILITY(EXT_IMM)) { if (a2 == (int32_t)a2) { tcg_out_insn(s, RIL, AGFI, a0, a2); break; @@ -2281,8 +2281,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, /* The host memory model is quite strong, we simply need to serialize the instruction stream. */ if (args[0] & TCG_MO_ST_LD) { - tcg_out_insn(s, RR, BCR, - s390_facilities & FACILITY_FAST_BCR_SER ? 14 : 15, 0); + tcg_out_insn(s, RR, BCR, HAVE_FACILITY(FAST_BCR_SER) ? 14 : 15, 0); } break; @@ -2345,7 +2344,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_or_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: - return (s390_facilities & FACILITY_DISTINCT_OPS + return (HAVE_FACILITY(DISTINCT_OPS) ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ri)); @@ -2353,19 +2352,19 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) /* If we have the general-instruction-extensions, then we have MULTIPLY SINGLE IMMEDIATE with a signed 32-bit, otherwise we have only MULTIPLY HALFWORD IMMEDIATE, with a signed 16-bit. */ - return (s390_facilities & FACILITY_GEN_INST_EXT + return (HAVE_FACILITY(GEN_INST_EXT) ? C_O1_I2(r, 0, ri) : C_O1_I2(r, 0, rI)); case INDEX_op_mul_i64: - return (s390_facilities & FACILITY_GEN_INST_EXT + return (HAVE_FACILITY(GEN_INST_EXT) ? C_O1_I2(r, 0, rJ) : C_O1_I2(r, 0, rI)); case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: - return (s390_facilities & FACILITY_DISTINCT_OPS + return (HAVE_FACILITY(DISTINCT_OPS) ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ri)); @@ -2409,7 +2408,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: - return (s390_facilities & FACILITY_LOAD_ON_COND2 + return (HAVE_FACILITY(LOAD_ON_COND2) ? C_O1_I4(r, r, ri, rI, 0) : C_O1_I4(r, r, ri, r, 0)); @@ -2424,13 +2423,13 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_add2_i32: case INDEX_op_sub2_i32: - return (s390_facilities & FACILITY_EXT_IMM + return (HAVE_FACILITY(EXT_IMM) ? C_O2_I4(r, r, 0, 1, ri, r) : C_O2_I4(r, r, 0, 1, r, r)); case INDEX_op_add2_i64: case INDEX_op_sub2_i64: - return (s390_facilities & FACILITY_EXT_IMM + return (HAVE_FACILITY(EXT_IMM) ? C_O2_I4(r, r, 0, 1, rA, r) : C_O2_I4(r, r, 0, 1, r, r)); @@ -2446,13 +2445,12 @@ static void query_s390_facilities(void) /* Is STORE FACILITY LIST EXTENDED available? Honestly, I believe this is present on all 64-bit systems, but let's check for it anyway. */ if (hwcap & HWCAP_S390_STFLE) { - register int r0 __asm__("0"); - register void *r1 __asm__("1"); + register int r0 __asm__("0") = ARRAY_SIZE(s390_facilities) - 1; + register void *r1 __asm__("1") = s390_facilities; /* stfle 0(%r1) */ - r1 = &s390_facilities; asm volatile(".word 0xb2b0,0x1000" - : "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc"); + : "=r"(r0) : "r"(r0), "r"(r1) : "memory", "cc"); } } diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 2e4ede2ea2..18d0d330e6 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -55,16 +55,19 @@ typedef enum TCGReg { /* A list of relevant facilities used by this translator. Some of these are required for proper operation, and these are checked at startup. */ -#define FACILITY_ZARCH_ACTIVE (1ULL << (63 - 2)) -#define FACILITY_LONG_DISP (1ULL << (63 - 18)) -#define FACILITY_EXT_IMM (1ULL << (63 - 21)) -#define FACILITY_GEN_INST_EXT (1ULL << (63 - 34)) -#define FACILITY_LOAD_ON_COND (1ULL << (63 - 45)) +#define FACILITY_ZARCH_ACTIVE 2 +#define FACILITY_LONG_DISP 18 +#define FACILITY_EXT_IMM 21 +#define FACILITY_GEN_INST_EXT 34 +#define FACILITY_LOAD_ON_COND 45 #define FACILITY_FAST_BCR_SER FACILITY_LOAD_ON_COND #define FACILITY_DISTINCT_OPS FACILITY_LOAD_ON_COND -#define FACILITY_LOAD_ON_COND2 (1ULL << (63 - 53)) +#define FACILITY_LOAD_ON_COND2 53 -extern uint64_t s390_facilities; +extern uint64_t s390_facilities[1]; + +#define HAVE_FACILITY(X) \ + ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) /* optional instructions */ #define TCG_TARGET_HAS_div2_i32 1 @@ -85,8 +88,8 @@ extern uint64_t s390_facilities; #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_deposit_i32 (s390_facilities & FACILITY_GEN_INST_EXT) -#define TCG_TARGET_HAS_extract_i32 (s390_facilities & FACILITY_GEN_INST_EXT) +#define TCG_TARGET_HAS_deposit_i32 HAVE_FACILITY(GEN_INST_EXT) +#define TCG_TARGET_HAS_extract_i32 HAVE_FACILITY(GEN_INST_EXT) #define TCG_TARGET_HAS_sextract_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_movcond_i32 1 @@ -98,7 +101,7 @@ extern uint64_t s390_facilities; #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_extrl_i64_i32 0 #define TCG_TARGET_HAS_extrh_i64_i32 0 -#define TCG_TARGET_HAS_direct_jump (s390_facilities & FACILITY_GEN_INST_EXT) +#define TCG_TARGET_HAS_direct_jump HAVE_FACILITY(GEN_INST_EXT) #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_div2_i64 1 @@ -119,11 +122,11 @@ extern uint64_t s390_facilities; #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 (s390_facilities & FACILITY_EXT_IMM) +#define TCG_TARGET_HAS_clz_i64 HAVE_FACILITY(EXT_IMM) #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 (s390_facilities & FACILITY_GEN_INST_EXT) -#define TCG_TARGET_HAS_extract_i64 (s390_facilities & FACILITY_GEN_INST_EXT) +#define TCG_TARGET_HAS_deposit_i64 HAVE_FACILITY(GEN_INST_EXT) +#define TCG_TARGET_HAS_extract_i64 HAVE_FACILITY(GEN_INST_EXT) #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_movcond_i64 1 From eee6251b48f4d65e10feaff3f015241585d89f49 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 27 Apr 2019 17:09:38 -0700 Subject: [PATCH 0297/1334] tcg/s390x: Merge TCG_AREG0 and TCG_REG_CALL_STACK into TCGReg They are rightly values in the same enumeration. Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.h | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 18d0d330e6..0174357f1b 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -32,22 +32,13 @@ #define MAX_CODE_GEN_BUFFER_SIZE (3 * GiB) typedef enum TCGReg { - TCG_REG_R0 = 0, - TCG_REG_R1, - TCG_REG_R2, - TCG_REG_R3, - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, - TCG_REG_R11, - TCG_REG_R12, - TCG_REG_R13, - TCG_REG_R14, - TCG_REG_R15 + TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3, + TCG_REG_R4, TCG_REG_R5, TCG_REG_R6, TCG_REG_R7, + TCG_REG_R8, TCG_REG_R9, TCG_REG_R10, TCG_REG_R11, + TCG_REG_R12, TCG_REG_R13, TCG_REG_R14, TCG_REG_R15, + + TCG_AREG0 = TCG_REG_R10, + TCG_REG_CALL_STACK = TCG_REG_R15 } TCGReg; #define TCG_TARGET_NB_REGS 16 @@ -138,7 +129,6 @@ extern uint64_t s390_facilities[1]; #define TCG_TARGET_HAS_mulsh_i64 0 /* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_R15 #define TCG_TARGET_STACK_ALIGN 8 #define TCG_TARGET_CALL_STACK_OFFSET 160 @@ -147,10 +137,6 @@ extern uint64_t s390_facilities[1]; #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) -enum { - TCG_AREG0 = TCG_REG_R10, -}; - static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, uintptr_t jmp_rw, uintptr_t addr) { From 34ef7676090e35ffb7f7d8b8d92a843a6ee94931 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 16:33:14 -0700 Subject: [PATCH 0298/1334] tcg/s390x: Add host vector framework Add registers and function stubs. The functionality is disabled via squashing s390_facilities[2] to 0. We must still include results for the mandatory opcodes in tcg_target_op_def, as all opcodes are checked during tcg init. Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target-con-set.h | 4 + tcg/s390x/tcg-target-con-str.h | 1 + tcg/s390x/tcg-target.c.inc | 137 ++++++++++++++++++++++++++++++++- tcg/s390x/tcg-target.h | 35 ++++++++- tcg/s390x/tcg-target.opc.h | 12 +++ 5 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 tcg/s390x/tcg-target.opc.h diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 31985e4903..ce9432cfe3 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -13,13 +13,17 @@ C_O0_I1(r) C_O0_I2(L, L) C_O0_I2(r, r) C_O0_I2(r, ri) +C_O0_I2(v, r) C_O1_I1(r, L) C_O1_I1(r, r) +C_O1_I1(v, r) +C_O1_I1(v, vr) C_O1_I2(r, 0, ri) C_O1_I2(r, 0, rI) C_O1_I2(r, 0, rJ) C_O1_I2(r, r, ri) C_O1_I2(r, rZ, r) +C_O1_I2(v, v, v) C_O1_I4(r, r, ri, r, 0) C_O1_I4(r, r, ri, rI, 0) C_O2_I2(b, a, 0, r) diff --git a/tcg/s390x/tcg-target-con-str.h b/tcg/s390x/tcg-target-con-str.h index 892d8f8c06..8bb0358ae5 100644 --- a/tcg/s390x/tcg-target-con-str.h +++ b/tcg/s390x/tcg-target-con-str.h @@ -10,6 +10,7 @@ */ REGS('r', ALL_GENERAL_REGS) REGS('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) +REGS('v', ALL_VECTOR_REGS) /* * A (single) even/odd pair for division. * TODO: Add something to the register allocator to allow diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index a224244b52..8bee6dd26e 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -43,6 +43,8 @@ #define TCG_CT_CONST_ZERO 0x800 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 16) +#define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) + /* * For softmmu, we need to avoid conflicts with the first 3 * argument registers to perform the tlb lookup, and to call @@ -268,8 +270,13 @@ typedef enum S390Opcode { #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", - "%r8", "%r9", "%r10" "%r11" "%r12" "%r13" "%r14" "%r15" + "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "%v0", "%v1", "%v2", "%v3", "%v4", "%v5", "%v6", "%v7", + "%v8", "%v9", "%v10", "%v11", "%v12", "%v13", "%v14", "%v15", + "%v16", "%v17", "%v18", "%v19", "%v20", "%v21", "%v22", "%v23", + "%v24", "%v25", "%v26", "%v27", "%v28", "%v29", "%v30", "%v31", }; #endif @@ -295,6 +302,32 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_R4, TCG_REG_R3, TCG_REG_R2, + + /* V8-V15 are call saved, and omitted. */ + TCG_REG_V0, + TCG_REG_V1, + TCG_REG_V2, + TCG_REG_V3, + TCG_REG_V4, + TCG_REG_V5, + TCG_REG_V6, + TCG_REG_V7, + TCG_REG_V16, + TCG_REG_V17, + TCG_REG_V18, + TCG_REG_V19, + TCG_REG_V20, + TCG_REG_V21, + TCG_REG_V22, + TCG_REG_V23, + TCG_REG_V24, + TCG_REG_V25, + TCG_REG_V26, + TCG_REG_V27, + TCG_REG_V28, + TCG_REG_V29, + TCG_REG_V30, + TCG_REG_V31, }; static const int tcg_target_call_iarg_regs[] = { @@ -377,7 +410,7 @@ static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { #endif static const tcg_insn_unit *tb_ret_addr; -uint64_t s390_facilities[1]; +uint64_t s390_facilities[3]; static bool patch_reloc(tcg_insn_unit *src_rw, int type, intptr_t value, intptr_t addend) @@ -2293,6 +2326,42 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, } } +static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, TCGReg src) +{ + g_assert_not_reached(); +} + +static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, TCGReg base, intptr_t offset) +{ + g_assert_not_reached(); +} + +static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, int64_t val) +{ + g_assert_not_reached(); +} + +static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, + unsigned vecl, unsigned vece, + const TCGArg *args, const int *const_args) +{ + g_assert_not_reached(); +} + +int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) +{ + return 0; +} + +void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, + TCGArg a0, ...) +{ + g_assert_not_reached(); +} + static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) { switch (op) { @@ -2433,11 +2502,34 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) ? C_O2_I4(r, r, 0, 1, rA, r) : C_O2_I4(r, r, 0, 1, r, r)); + case INDEX_op_st_vec: + return C_O0_I2(v, r); + case INDEX_op_ld_vec: + case INDEX_op_dupm_vec: + return C_O1_I1(v, r); + case INDEX_op_dup_vec: + return C_O1_I1(v, vr); + case INDEX_op_add_vec: + case INDEX_op_sub_vec: + case INDEX_op_and_vec: + case INDEX_op_or_vec: + case INDEX_op_xor_vec: + case INDEX_op_cmp_vec: + return C_O1_I2(v, v, v); + default: g_assert_not_reached(); } } +/* + * Mainline glibc added HWCAP_S390_VX before it was kernel abi. + * Some distros have fixed this up locally, others have not. + */ +#ifndef HWCAP_S390_VXRS +#define HWCAP_S390_VXRS 2048 +#endif + static void query_s390_facilities(void) { unsigned long hwcap = qemu_getauxval(AT_HWCAP); @@ -2452,6 +2544,16 @@ static void query_s390_facilities(void) asm volatile(".word 0xb2b0,0x1000" : "=r"(r0) : "r"(r0), "r"(r1) : "memory", "cc"); } + + /* + * Use of vector registers requires os support beyond the facility bit. + * If the kernel does not advertise support, disable the facility bits. + * There is nothing else we currently care about in the 3rd word, so + * disable VECTOR with one store. + */ + if (1 || !(hwcap & HWCAP_S390_VXRS)) { + s390_facilities[2] = 0; + } } static void tcg_target_init(TCGContext *s) @@ -2460,6 +2562,10 @@ static void tcg_target_init(TCGContext *s) tcg_target_available_regs[TCG_TYPE_I32] = 0xffff; tcg_target_available_regs[TCG_TYPE_I64] = 0xffff; + if (HAVE_FACILITY(VECTOR)) { + tcg_target_available_regs[TCG_TYPE_V64] = 0xffffffff00000000ull; + tcg_target_available_regs[TCG_TYPE_V128] = 0xffffffff00000000ull; + } tcg_target_call_clobber_regs = 0; tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R0); @@ -2474,6 +2580,31 @@ static void tcg_target_init(TCGContext *s) /* The return register can be considered call-clobbered. */ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V0); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V1); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V2); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V3); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V4); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V5); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V6); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V7); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V16); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V17); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V18); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V19); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V20); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V21); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V22); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V23); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V24); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V25); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V26); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V27); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V28); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V29); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V30); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_V31); + s->reserved_regs = 0; tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); /* XXX many insns can't be used with R0, so we better avoid it for now */ diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 0174357f1b..5a03c5f2f4 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -37,11 +37,20 @@ typedef enum TCGReg { TCG_REG_R8, TCG_REG_R9, TCG_REG_R10, TCG_REG_R11, TCG_REG_R12, TCG_REG_R13, TCG_REG_R14, TCG_REG_R15, + TCG_REG_V0 = 32, TCG_REG_V1, TCG_REG_V2, TCG_REG_V3, + TCG_REG_V4, TCG_REG_V5, TCG_REG_V6, TCG_REG_V7, + TCG_REG_V8, TCG_REG_V9, TCG_REG_V10, TCG_REG_V11, + TCG_REG_V12, TCG_REG_V13, TCG_REG_V14, TCG_REG_V15, + TCG_REG_V16, TCG_REG_V17, TCG_REG_V18, TCG_REG_V19, + TCG_REG_V20, TCG_REG_V21, TCG_REG_V22, TCG_REG_V23, + TCG_REG_V24, TCG_REG_V25, TCG_REG_V26, TCG_REG_V27, + TCG_REG_V28, TCG_REG_V29, TCG_REG_V30, TCG_REG_V31, + TCG_AREG0 = TCG_REG_R10, TCG_REG_CALL_STACK = TCG_REG_R15 } TCGReg; -#define TCG_TARGET_NB_REGS 16 +#define TCG_TARGET_NB_REGS 64 /* A list of relevant facilities used by this translator. Some of these are required for proper operation, and these are checked at startup. */ @@ -54,8 +63,9 @@ typedef enum TCGReg { #define FACILITY_FAST_BCR_SER FACILITY_LOAD_ON_COND #define FACILITY_DISTINCT_OPS FACILITY_LOAD_ON_COND #define FACILITY_LOAD_ON_COND2 53 +#define FACILITY_VECTOR 129 -extern uint64_t s390_facilities[1]; +extern uint64_t s390_facilities[3]; #define HAVE_FACILITY(X) \ ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) @@ -128,6 +138,27 @@ extern uint64_t s390_facilities[1]; #define TCG_TARGET_HAS_muluh_i64 0 #define TCG_TARGET_HAS_mulsh_i64 0 +#define TCG_TARGET_HAS_v64 HAVE_FACILITY(VECTOR) +#define TCG_TARGET_HAS_v128 HAVE_FACILITY(VECTOR) +#define TCG_TARGET_HAS_v256 0 + +#define TCG_TARGET_HAS_andc_vec 0 +#define TCG_TARGET_HAS_orc_vec 0 +#define TCG_TARGET_HAS_not_vec 0 +#define TCG_TARGET_HAS_neg_vec 0 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 0 +#define TCG_TARGET_HAS_shi_vec 0 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 0 +#define TCG_TARGET_HAS_mul_vec 0 +#define TCG_TARGET_HAS_sat_vec 0 +#define TCG_TARGET_HAS_minmax_vec 0 +#define TCG_TARGET_HAS_bitsel_vec 0 +#define TCG_TARGET_HAS_cmpsel_vec 0 + /* used for function call generation */ #define TCG_TARGET_STACK_ALIGN 8 #define TCG_TARGET_CALL_STACK_OFFSET 160 diff --git a/tcg/s390x/tcg-target.opc.h b/tcg/s390x/tcg-target.opc.h new file mode 100644 index 0000000000..67afc82a93 --- /dev/null +++ b/tcg/s390x/tcg-target.opc.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2021 Linaro + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + * + * See the COPYING file in the top-level directory for details. + * + * Target-specific opcodes for host vector expansion. These will be + * emitted by tcg_expand_vec_op. For those familiar with GCC internals, + * consider these to be UNSPEC with names. + */ From 2dabf74252e2b9b9f23b1991d69a8def61442a2c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 17:04:22 -0700 Subject: [PATCH 0299/1334] tcg/s390x: Implement tcg_out_ld/st for vector types Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 132 +++++++++++++++++++++++++++++++++---- 1 file changed, 120 insertions(+), 12 deletions(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 8bee6dd26e..d80f25e48e 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -265,6 +265,12 @@ typedef enum S390Opcode { RX_STC = 0x42, RX_STH = 0x40, + VRX_VL = 0xe706, + VRX_VLLEZ = 0xe704, + VRX_VST = 0xe70e, + VRX_VSTEF = 0xe70b, + VRX_VSTEG = 0xe70a, + NOP = 0x0707, } S390Opcode; @@ -412,6 +418,16 @@ static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { static const tcg_insn_unit *tb_ret_addr; uint64_t s390_facilities[3]; +static inline bool is_general_reg(TCGReg r) +{ + return r <= TCG_REG_R15; +} + +static inline bool is_vector_reg(TCGReg r) +{ + return r >= TCG_REG_V0 && r <= TCG_REG_V31; +} + static bool patch_reloc(tcg_insn_unit *src_rw, int type, intptr_t value, intptr_t addend) { @@ -529,6 +545,31 @@ static void tcg_out_insn_RSY(TCGContext *s, S390Opcode op, TCGReg r1, #define tcg_out_insn_RX tcg_out_insn_RS #define tcg_out_insn_RXY tcg_out_insn_RSY +static int RXB(TCGReg v1, TCGReg v2, TCGReg v3, TCGReg v4) +{ + /* + * Shift bit 4 of each regno to its corresponding bit of RXB. + * RXB itself begins at bit 8 of the instruction so 8 - 4 = 4 + * is the left-shift of the 4th operand. + */ + return ((v1 & 0x10) << (4 + 3)) + | ((v2 & 0x10) << (4 + 2)) + | ((v3 & 0x10) << (4 + 1)) + | ((v4 & 0x10) << (4 + 0)); +} + +static void tcg_out_insn_VRX(TCGContext *s, S390Opcode op, TCGReg v1, + TCGReg b2, TCGReg x2, intptr_t d2, int m3) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_debug_assert(d2 >= 0 && d2 <= 0xfff); + tcg_debug_assert(is_general_reg(x2)); + tcg_debug_assert(is_general_reg(b2)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | x2); + tcg_out16(s, (b2 << 12) | d2); + tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, 0, 0) | (m3 << 12)); +} + /* Emit an opcode with "type-checking" of the format. */ #define tcg_out_insn(S, FMT, OP, ...) \ glue(tcg_out_insn_,FMT)(S, glue(glue(FMT,_),OP), ## __VA_ARGS__) @@ -705,25 +746,92 @@ static void tcg_out_mem(TCGContext *s, S390Opcode opc_rx, S390Opcode opc_rxy, } } +static void tcg_out_vrx_mem(TCGContext *s, S390Opcode opc_vrx, + TCGReg data, TCGReg base, TCGReg index, + tcg_target_long ofs, int m3) +{ + if (ofs < 0 || ofs >= 0x1000) { + if (ofs >= -0x80000 && ofs < 0x80000) { + tcg_out_insn(s, RXY, LAY, TCG_TMP0, base, index, ofs); + base = TCG_TMP0; + index = TCG_REG_NONE; + ofs = 0; + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs); + if (index != TCG_REG_NONE) { + tcg_out_insn(s, RRE, AGR, TCG_TMP0, index); + } + index = TCG_TMP0; + ofs = 0; + } + } + tcg_out_insn_VRX(s, opc_vrx, data, base, index, ofs, m3); +} /* load data without address translation or endianness conversion */ -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data, - TCGReg base, intptr_t ofs) +static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, intptr_t ofs) { - if (type == TCG_TYPE_I32) { - tcg_out_mem(s, RX_L, RXY_LY, data, base, TCG_REG_NONE, ofs); - } else { - tcg_out_mem(s, 0, RXY_LG, data, base, TCG_REG_NONE, ofs); + switch (type) { + case TCG_TYPE_I32: + if (likely(is_general_reg(data))) { + tcg_out_mem(s, RX_L, RXY_LY, data, base, TCG_REG_NONE, ofs); + break; + } + tcg_out_vrx_mem(s, VRX_VLLEZ, data, base, TCG_REG_NONE, ofs, MO_32); + break; + + case TCG_TYPE_I64: + if (likely(is_general_reg(data))) { + tcg_out_mem(s, 0, RXY_LG, data, base, TCG_REG_NONE, ofs); + break; + } + /* fallthru */ + + case TCG_TYPE_V64: + tcg_out_vrx_mem(s, VRX_VLLEZ, data, base, TCG_REG_NONE, ofs, MO_64); + break; + + case TCG_TYPE_V128: + /* Hint quadword aligned. */ + tcg_out_vrx_mem(s, VRX_VL, data, base, TCG_REG_NONE, ofs, 4); + break; + + default: + g_assert_not_reached(); } } -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg data, - TCGReg base, intptr_t ofs) +static void tcg_out_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, intptr_t ofs) { - if (type == TCG_TYPE_I32) { - tcg_out_mem(s, RX_ST, RXY_STY, data, base, TCG_REG_NONE, ofs); - } else { - tcg_out_mem(s, 0, RXY_STG, data, base, TCG_REG_NONE, ofs); + switch (type) { + case TCG_TYPE_I32: + if (likely(is_general_reg(data))) { + tcg_out_mem(s, RX_ST, RXY_STY, data, base, TCG_REG_NONE, ofs); + } else { + tcg_out_vrx_mem(s, VRX_VSTEF, data, base, TCG_REG_NONE, ofs, 1); + } + break; + + case TCG_TYPE_I64: + if (likely(is_general_reg(data))) { + tcg_out_mem(s, 0, RXY_STG, data, base, TCG_REG_NONE, ofs); + break; + } + /* fallthru */ + + case TCG_TYPE_V64: + tcg_out_vrx_mem(s, VRX_VSTEG, data, base, TCG_REG_NONE, ofs, 0); + break; + + case TCG_TYPE_V128: + /* Hint quadword aligned. */ + tcg_out_vrx_mem(s, VRX_VST, data, base, TCG_REG_NONE, ofs, 4); + break; + + default: + g_assert_not_reached(); } } From b33ce7251c9591635ca0f93bf5e75e7dbb844b5c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 17:12:18 -0700 Subject: [PATCH 0300/1334] tcg/s390x: Implement tcg_out_mov for vector types Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 72 +++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index d80f25e48e..586a4b5587 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -265,6 +265,11 @@ typedef enum S390Opcode { RX_STC = 0x42, RX_STH = 0x40, + VRRa_VLR = 0xe756, + + VRSb_VLVG = 0xe722, + VRSc_VLGV = 0xe721, + VRX_VL = 0xe706, VRX_VLLEZ = 0xe704, VRX_VST = 0xe70e, @@ -558,6 +563,39 @@ static int RXB(TCGReg v1, TCGReg v2, TCGReg v3, TCGReg v4) | ((v4 & 0x10) << (4 + 0)); } +static void tcg_out_insn_VRRa(TCGContext *s, S390Opcode op, + TCGReg v1, TCGReg v2, int m3) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_debug_assert(is_vector_reg(v2)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v2 & 0xf)); + tcg_out32(s, (op & 0x00ff) | RXB(v1, v2, 0, 0) | (m3 << 12)); +} + +static void tcg_out_insn_VRSb(TCGContext *s, S390Opcode op, TCGReg v1, + intptr_t d2, TCGReg b2, TCGReg r3, int m4) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_debug_assert(d2 >= 0 && d2 <= 0xfff); + tcg_debug_assert(is_general_reg(b2)); + tcg_debug_assert(is_general_reg(r3)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | r3); + tcg_out16(s, b2 << 12 | d2); + tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, 0, 0) | (m4 << 12)); +} + +static void tcg_out_insn_VRSc(TCGContext *s, S390Opcode op, TCGReg r1, + intptr_t d2, TCGReg b2, TCGReg v3, int m4) +{ + tcg_debug_assert(is_general_reg(r1)); + tcg_debug_assert(d2 >= 0 && d2 <= 0xfff); + tcg_debug_assert(is_general_reg(b2)); + tcg_debug_assert(is_vector_reg(v3)); + tcg_out16(s, (op & 0xff00) | (r1 << 4) | (v3 & 0xf)); + tcg_out16(s, b2 << 12 | d2); + tcg_out16(s, (op & 0x00ff) | RXB(0, 0, v3, 0) | (m4 << 12)); +} + static void tcg_out_insn_VRX(TCGContext *s, S390Opcode op, TCGReg v1, TCGReg b2, TCGReg x2, intptr_t d2, int m3) { @@ -591,12 +629,38 @@ static void tcg_out_sh32(TCGContext* s, S390Opcode op, TCGReg dest, static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) { - if (src != dst) { - if (type == TCG_TYPE_I32) { + if (src == dst) { + return true; + } + switch (type) { + case TCG_TYPE_I32: + if (likely(is_general_reg(dst) && is_general_reg(src))) { tcg_out_insn(s, RR, LR, dst, src); - } else { - tcg_out_insn(s, RRE, LGR, dst, src); + break; } + /* fallthru */ + + case TCG_TYPE_I64: + if (likely(is_general_reg(dst))) { + if (likely(is_general_reg(src))) { + tcg_out_insn(s, RRE, LGR, dst, src); + } else { + tcg_out_insn(s, VRSc, VLGV, dst, 0, 0, src, 3); + } + break; + } else if (is_general_reg(src)) { + tcg_out_insn(s, VRSb, VLVG, dst, 0, 0, src, 3); + break; + } + /* fallthru */ + + case TCG_TYPE_V64: + case TCG_TYPE_V128: + tcg_out_insn(s, VRRa, VLR, dst, src, 0); + break; + + default: + g_assert_not_reached(); } return true; } From 79cada8693d3e32162e41323b4bfbeaa765cad35 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 17:25:52 -0700 Subject: [PATCH 0301/1334] tcg/s390x: Implement tcg_out_dup*_vec Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 122 ++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 3 deletions(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 586a4b5587..f59250872b 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -265,13 +265,20 @@ typedef enum S390Opcode { RX_STC = 0x42, RX_STH = 0x40, + VRIa_VGBM = 0xe744, + VRIa_VREPI = 0xe745, + VRIb_VGM = 0xe746, + VRIc_VREP = 0xe74d, + VRRa_VLR = 0xe756, + VRRf_VLVGP = 0xe762, VRSb_VLVG = 0xe722, VRSc_VLGV = 0xe721, VRX_VL = 0xe706, VRX_VLLEZ = 0xe704, + VRX_VLREP = 0xe705, VRX_VST = 0xe70e, VRX_VSTEF = 0xe70b, VRX_VSTEG = 0xe70a, @@ -563,6 +570,34 @@ static int RXB(TCGReg v1, TCGReg v2, TCGReg v3, TCGReg v4) | ((v4 & 0x10) << (4 + 0)); } +static void tcg_out_insn_VRIa(TCGContext *s, S390Opcode op, + TCGReg v1, uint16_t i2, int m3) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4)); + tcg_out16(s, i2); + tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, 0, 0) | (m3 << 12)); +} + +static void tcg_out_insn_VRIb(TCGContext *s, S390Opcode op, + TCGReg v1, uint8_t i2, uint8_t i3, int m4) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4)); + tcg_out16(s, (i2 << 8) | (i3 & 0xff)); + tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, 0, 0) | (m4 << 12)); +} + +static void tcg_out_insn_VRIc(TCGContext *s, S390Opcode op, + TCGReg v1, uint16_t i2, TCGReg v3, int m4) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_debug_assert(is_vector_reg(v3)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v3 & 0xf)); + tcg_out16(s, i2); + tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, v3, 0) | (m4 << 12)); +} + static void tcg_out_insn_VRRa(TCGContext *s, S390Opcode op, TCGReg v1, TCGReg v2, int m3) { @@ -572,6 +607,17 @@ static void tcg_out_insn_VRRa(TCGContext *s, S390Opcode op, tcg_out32(s, (op & 0x00ff) | RXB(v1, v2, 0, 0) | (m3 << 12)); } +static void tcg_out_insn_VRRf(TCGContext *s, S390Opcode op, + TCGReg v1, TCGReg r2, TCGReg r3) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_debug_assert(is_general_reg(r2)); + tcg_debug_assert(is_general_reg(r3)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | r2); + tcg_out16(s, r3 << 12); + tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, 0, 0)); +} + static void tcg_out_insn_VRSb(TCGContext *s, S390Opcode op, TCGReg v1, intptr_t d2, TCGReg b2, TCGReg r3, int m4) { @@ -2501,19 +2547,89 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg dst, TCGReg src) { - g_assert_not_reached(); + if (is_general_reg(src)) { + /* Replicate general register into two MO_64. */ + tcg_out_insn(s, VRRf, VLVGP, dst, src, src); + if (vece == MO_64) { + return true; + } + } + + /* + * Recall that the "standard" integer, within a vector, is the + * rightmost element of the leftmost doubleword, a-la VLLEZ. + */ + tcg_out_insn(s, VRIc, VREP, dst, (8 >> vece) - 1, src, vece); + return true; } static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg dst, TCGReg base, intptr_t offset) { - g_assert_not_reached(); + tcg_out_vrx_mem(s, VRX_VLREP, dst, base, TCG_REG_NONE, offset, vece); + return true; } static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg dst, int64_t val) { - g_assert_not_reached(); + int i, mask, msb, lsb; + + /* Look for int16_t elements. */ + if (vece <= MO_16 || + (vece == MO_32 ? (int32_t)val : val) == (int16_t)val) { + tcg_out_insn(s, VRIa, VREPI, dst, val, vece); + return; + } + + /* Look for bit masks. */ + if (vece == MO_32) { + if (risbg_mask((int32_t)val)) { + /* Handle wraparound by swapping msb and lsb. */ + if ((val & 0x80000001u) == 0x80000001u) { + msb = 32 - ctz32(~val); + lsb = clz32(~val) - 1; + } else { + msb = clz32(val); + lsb = 31 - ctz32(val); + } + tcg_out_insn(s, VRIb, VGM, dst, lsb, msb, MO_32); + return; + } + } else { + if (risbg_mask(val)) { + /* Handle wraparound by swapping msb and lsb. */ + if ((val & 0x8000000000000001ull) == 0x8000000000000001ull) { + /* Handle wraparound by swapping msb and lsb. */ + msb = 64 - ctz64(~val); + lsb = clz64(~val) - 1; + } else { + msb = clz64(val); + lsb = 63 - ctz64(val); + } + tcg_out_insn(s, VRIb, VGM, dst, lsb, msb, MO_64); + return; + } + } + + /* Look for all bytes 0x00 or 0xff. */ + for (i = mask = 0; i < 8; i++) { + uint8_t byte = val >> (i * 8); + if (byte == 0xff) { + mask |= 1 << i; + } else if (byte != 0) { + break; + } + } + if (i == 8) { + tcg_out_insn(s, VRIa, VGBM, dst, mask * 0x0101, 0); + return; + } + + /* Otherwise, stuff it in the constant pool. */ + tcg_out_insn(s, RIL, LARL, TCG_TMP0, 0); + new_pool_label(s, val, R_390_PC32DBL, s->code_ptr - 2, 2); + tcg_out_insn(s, VRX, VLREP, dst, TCG_TMP0, TCG_REG_NONE, 0, MO_64); } static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, From a429ee2978a1bb81cfb737e382a47c119a2c0886 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 18:08:00 -0700 Subject: [PATCH 0302/1334] tcg/s390x: Implement minimal vector operations Implementing add, sub, and, or, xor as the minimal set. This allows us to actually enable vectors in query_s390_facilities. Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 154 ++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 4 deletions(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index f59250872b..063f720199 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -271,6 +271,14 @@ typedef enum S390Opcode { VRIc_VREP = 0xe74d, VRRa_VLR = 0xe756, + VRRc_VA = 0xe7f3, + VRRc_VCEQ = 0xe7f8, /* we leave the m5 cs field 0 */ + VRRc_VCH = 0xe7fb, /* " */ + VRRc_VCHL = 0xe7f9, /* " */ + VRRc_VN = 0xe768, + VRRc_VO = 0xe76a, + VRRc_VS = 0xe7f7, + VRRc_VX = 0xe76d, VRRf_VLVGP = 0xe762, VRSb_VLVG = 0xe722, @@ -607,6 +615,17 @@ static void tcg_out_insn_VRRa(TCGContext *s, S390Opcode op, tcg_out32(s, (op & 0x00ff) | RXB(v1, v2, 0, 0) | (m3 << 12)); } +static void tcg_out_insn_VRRc(TCGContext *s, S390Opcode op, + TCGReg v1, TCGReg v2, TCGReg v3, int m4) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_debug_assert(is_vector_reg(v2)); + tcg_debug_assert(is_vector_reg(v3)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v2 & 0xf)); + tcg_out16(s, v3 << 12); + tcg_out16(s, (op & 0x00ff) | RXB(v1, v2, v3, 0) | (m4 << 12)); +} + static void tcg_out_insn_VRRf(TCGContext *s, S390Opcode op, TCGReg v1, TCGReg r2, TCGReg r3) { @@ -2636,18 +2655,145 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned vece, const TCGArg *args, const int *const_args) { - g_assert_not_reached(); + TCGType type = vecl + TCG_TYPE_V64; + TCGArg a0 = args[0], a1 = args[1], a2 = args[2]; + + switch (opc) { + case INDEX_op_ld_vec: + tcg_out_ld(s, type, a0, a1, a2); + break; + case INDEX_op_st_vec: + tcg_out_st(s, type, a0, a1, a2); + break; + case INDEX_op_dupm_vec: + tcg_out_dupm_vec(s, type, vece, a0, a1, a2); + break; + + case INDEX_op_add_vec: + tcg_out_insn(s, VRRc, VA, a0, a1, a2, vece); + break; + case INDEX_op_sub_vec: + tcg_out_insn(s, VRRc, VS, a0, a1, a2, vece); + break; + case INDEX_op_and_vec: + tcg_out_insn(s, VRRc, VN, a0, a1, a2, 0); + break; + case INDEX_op_or_vec: + tcg_out_insn(s, VRRc, VO, a0, a1, a2, 0); + break; + case INDEX_op_xor_vec: + tcg_out_insn(s, VRRc, VX, a0, a1, a2, 0); + break; + + case INDEX_op_cmp_vec: + switch ((TCGCond)args[3]) { + case TCG_COND_EQ: + tcg_out_insn(s, VRRc, VCEQ, a0, a1, a2, vece); + break; + case TCG_COND_GT: + tcg_out_insn(s, VRRc, VCH, a0, a1, a2, vece); + break; + case TCG_COND_GTU: + tcg_out_insn(s, VRRc, VCHL, a0, a1, a2, vece); + break; + default: + g_assert_not_reached(); + } + break; + + case INDEX_op_mov_vec: /* Always emitted via tcg_out_mov. */ + case INDEX_op_dup_vec: /* Always emitted via tcg_out_dup_vec. */ + default: + g_assert_not_reached(); + } } int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) { - return 0; + switch (opc) { + case INDEX_op_add_vec: + case INDEX_op_and_vec: + case INDEX_op_or_vec: + case INDEX_op_sub_vec: + case INDEX_op_xor_vec: + return 1; + case INDEX_op_cmp_vec: + return -1; + default: + return 0; + } +} + +static bool expand_vec_cmp_noinv(TCGType type, unsigned vece, TCGv_vec v0, + TCGv_vec v1, TCGv_vec v2, TCGCond cond) +{ + bool need_swap = false, need_inv = false; + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_GT: + case TCG_COND_GTU: + break; + case TCG_COND_NE: + case TCG_COND_LE: + case TCG_COND_LEU: + need_inv = true; + break; + case TCG_COND_LT: + case TCG_COND_LTU: + need_swap = true; + break; + case TCG_COND_GE: + case TCG_COND_GEU: + need_swap = need_inv = true; + break; + default: + g_assert_not_reached(); + } + + if (need_inv) { + cond = tcg_invert_cond(cond); + } + if (need_swap) { + TCGv_vec t1; + t1 = v1, v1 = v2, v2 = t1; + cond = tcg_swap_cond(cond); + } + + vec_gen_4(INDEX_op_cmp_vec, type, vece, tcgv_vec_arg(v0), + tcgv_vec_arg(v1), tcgv_vec_arg(v2), cond); + + return need_inv; +} + +static void expand_vec_cmp(TCGType type, unsigned vece, TCGv_vec v0, + TCGv_vec v1, TCGv_vec v2, TCGCond cond) +{ + if (expand_vec_cmp_noinv(type, vece, v0, v1, v2, cond)) { + tcg_gen_not_vec(vece, v0, v0); + } } void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, TCGArg a0, ...) { - g_assert_not_reached(); + va_list va; + TCGv_vec v0, v1, v2; + + va_start(va, a0); + v0 = temp_tcgv_vec(arg_temp(a0)); + v1 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); + v2 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); + + switch (opc) { + case INDEX_op_cmp_vec: + expand_vec_cmp(type, vece, v0, v1, v2, va_arg(va, TCGArg)); + break; + + default: + g_assert_not_reached(); + } + va_end(va); } static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) @@ -2839,7 +2985,7 @@ static void query_s390_facilities(void) * There is nothing else we currently care about in the 3rd word, so * disable VECTOR with one store. */ - if (1 || !(hwcap & HWCAP_S390_VXRS)) { + if (!(hwcap & HWCAP_S390_VXRS)) { s390_facilities[2] = 0; } } From ae77bbe5747dc655bed213006798f9b07e2f79bf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 18:23:16 -0700 Subject: [PATCH 0303/1334] tcg/s390x: Implement andc, orc, abs, neg, not vector operations These logical and arithmetic operations are optional but trivial. Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target-con-set.h | 1 + tcg/s390x/tcg-target.c.inc | 32 ++++++++++++++++++++++++++++++++ tcg/s390x/tcg-target.h | 11 ++++++----- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index ce9432cfe3..cb953896d5 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -17,6 +17,7 @@ C_O0_I2(v, r) C_O1_I1(r, L) C_O1_I1(r, r) C_O1_I1(v, r) +C_O1_I1(v, v) C_O1_I1(v, vr) C_O1_I2(r, 0, ri) C_O1_I2(r, 0, rI) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 063f720199..cbad88271a 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -270,13 +270,18 @@ typedef enum S390Opcode { VRIb_VGM = 0xe746, VRIc_VREP = 0xe74d, + VRRa_VLC = 0xe7de, + VRRa_VLP = 0xe7df, VRRa_VLR = 0xe756, VRRc_VA = 0xe7f3, VRRc_VCEQ = 0xe7f8, /* we leave the m5 cs field 0 */ VRRc_VCH = 0xe7fb, /* " */ VRRc_VCHL = 0xe7f9, /* " */ VRRc_VN = 0xe768, + VRRc_VNC = 0xe769, + VRRc_VNO = 0xe76b, VRRc_VO = 0xe76a, + VRRc_VOC = 0xe76f, VRRc_VS = 0xe7f7, VRRc_VX = 0xe76d, VRRf_VLVGP = 0xe762, @@ -2669,6 +2674,16 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_out_dupm_vec(s, type, vece, a0, a1, a2); break; + case INDEX_op_abs_vec: + tcg_out_insn(s, VRRa, VLP, a0, a1, vece); + break; + case INDEX_op_neg_vec: + tcg_out_insn(s, VRRa, VLC, a0, a1, vece); + break; + case INDEX_op_not_vec: + tcg_out_insn(s, VRRc, VNO, a0, a1, a1, 0); + break; + case INDEX_op_add_vec: tcg_out_insn(s, VRRc, VA, a0, a1, a2, vece); break; @@ -2678,9 +2693,15 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_and_vec: tcg_out_insn(s, VRRc, VN, a0, a1, a2, 0); break; + case INDEX_op_andc_vec: + tcg_out_insn(s, VRRc, VNC, a0, a1, a2, 0); + break; case INDEX_op_or_vec: tcg_out_insn(s, VRRc, VO, a0, a1, a2, 0); break; + case INDEX_op_orc_vec: + tcg_out_insn(s, VRRc, VOC, a0, a1, a2, 0); + break; case INDEX_op_xor_vec: tcg_out_insn(s, VRRc, VX, a0, a1, a2, 0); break; @@ -2711,9 +2732,14 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) { switch (opc) { + case INDEX_op_abs_vec: case INDEX_op_add_vec: case INDEX_op_and_vec: + case INDEX_op_andc_vec: + case INDEX_op_neg_vec: + case INDEX_op_not_vec: case INDEX_op_or_vec: + case INDEX_op_orc_vec: case INDEX_op_sub_vec: case INDEX_op_xor_vec: return 1; @@ -2943,10 +2969,16 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I1(v, r); case INDEX_op_dup_vec: return C_O1_I1(v, vr); + case INDEX_op_abs_vec: + case INDEX_op_neg_vec: + case INDEX_op_not_vec: + return C_O1_I1(v, v); case INDEX_op_add_vec: case INDEX_op_sub_vec: case INDEX_op_and_vec: + case INDEX_op_andc_vec: case INDEX_op_or_vec: + case INDEX_op_orc_vec: case INDEX_op_xor_vec: case INDEX_op_cmp_vec: return C_O1_I2(v, v, v); diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 5a03c5f2f4..a42074e451 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -64,6 +64,7 @@ typedef enum TCGReg { #define FACILITY_DISTINCT_OPS FACILITY_LOAD_ON_COND #define FACILITY_LOAD_ON_COND2 53 #define FACILITY_VECTOR 129 +#define FACILITY_VECTOR_ENH1 135 extern uint64_t s390_facilities[3]; @@ -142,11 +143,11 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_v128 HAVE_FACILITY(VECTOR) #define TCG_TARGET_HAS_v256 0 -#define TCG_TARGET_HAS_andc_vec 0 -#define TCG_TARGET_HAS_orc_vec 0 -#define TCG_TARGET_HAS_not_vec 0 -#define TCG_TARGET_HAS_neg_vec 0 -#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 1 #define TCG_TARGET_HAS_roti_vec 0 #define TCG_TARGET_HAS_rots_vec 0 #define TCG_TARGET_HAS_rotv_vec 0 From 479b61cbfafba997b97aec2e4d323c86240c24b0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 18:33:47 -0700 Subject: [PATCH 0304/1334] tcg/s390x: Implement TCG_TARGET_HAS_mul_vec Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 7 +++++++ tcg/s390x/tcg-target.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index cbad88271a..85178c93d3 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -277,6 +277,7 @@ typedef enum S390Opcode { VRRc_VCEQ = 0xe7f8, /* we leave the m5 cs field 0 */ VRRc_VCH = 0xe7fb, /* " */ VRRc_VCHL = 0xe7f9, /* " */ + VRRc_VML = 0xe7a2, VRRc_VN = 0xe768, VRRc_VNC = 0xe769, VRRc_VNO = 0xe76b, @@ -2696,6 +2697,9 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_andc_vec: tcg_out_insn(s, VRRc, VNC, a0, a1, a2, 0); break; + case INDEX_op_mul_vec: + tcg_out_insn(s, VRRc, VML, a0, a1, a2, vece); + break; case INDEX_op_or_vec: tcg_out_insn(s, VRRc, VO, a0, a1, a2, 0); break; @@ -2745,6 +2749,8 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) return 1; case INDEX_op_cmp_vec: return -1; + case INDEX_op_mul_vec: + return vece < MO_64; default: return 0; } @@ -2981,6 +2987,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_orc_vec: case INDEX_op_xor_vec: case INDEX_op_cmp_vec: + case INDEX_op_mul_vec: return C_O1_I2(v, v, v); default: diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index a42074e451..1c581a2f60 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -154,7 +154,7 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_shi_vec 0 #define TCG_TARGET_HAS_shs_vec 0 #define TCG_TARGET_HAS_shv_vec 0 -#define TCG_TARGET_HAS_mul_vec 0 +#define TCG_TARGET_HAS_mul_vec 1 #define TCG_TARGET_HAS_sat_vec 0 #define TCG_TARGET_HAS_minmax_vec 0 #define TCG_TARGET_HAS_bitsel_vec 0 From 22cb37b41720c8bcd48941b29b7c6ac1f990ab94 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 19:00:38 -0700 Subject: [PATCH 0305/1334] tcg/s390x: Implement vector shift operations Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target-con-set.h | 1 + tcg/s390x/tcg-target.c.inc | 93 +++++++++++++++++++++++++++++++++- tcg/s390x/tcg-target.h | 12 ++--- 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index cb953896d5..49b98f33b9 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -24,6 +24,7 @@ C_O1_I2(r, 0, rI) C_O1_I2(r, 0, rJ) C_O1_I2(r, r, ri) C_O1_I2(r, rZ, r) +C_O1_I2(v, v, r) C_O1_I2(v, v, v) C_O1_I4(r, r, ri, r, 0) C_O1_I4(r, r, ri, rI, 0) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 85178c93d3..98c0cd5091 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -277,6 +277,10 @@ typedef enum S390Opcode { VRRc_VCEQ = 0xe7f8, /* we leave the m5 cs field 0 */ VRRc_VCH = 0xe7fb, /* " */ VRRc_VCHL = 0xe7f9, /* " */ + VRRc_VERLLV = 0xe773, + VRRc_VESLV = 0xe770, + VRRc_VESRAV = 0xe77a, + VRRc_VESRLV = 0xe778, VRRc_VML = 0xe7a2, VRRc_VN = 0xe768, VRRc_VNC = 0xe769, @@ -287,6 +291,10 @@ typedef enum S390Opcode { VRRc_VX = 0xe76d, VRRf_VLVGP = 0xe762, + VRSa_VERLL = 0xe733, + VRSa_VESL = 0xe730, + VRSa_VESRA = 0xe73a, + VRSa_VESRL = 0xe738, VRSb_VLVG = 0xe722, VRSc_VLGV = 0xe721, @@ -643,6 +651,18 @@ static void tcg_out_insn_VRRf(TCGContext *s, S390Opcode op, tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, 0, 0)); } +static void tcg_out_insn_VRSa(TCGContext *s, S390Opcode op, TCGReg v1, + intptr_t d2, TCGReg b2, TCGReg v3, int m4) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_debug_assert(d2 >= 0 && d2 <= 0xfff); + tcg_debug_assert(is_general_reg(b2)); + tcg_debug_assert(is_vector_reg(v3)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v3 & 0xf)); + tcg_out16(s, b2 << 12 | d2); + tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, v3, 0) | (m4 << 12)); +} + static void tcg_out_insn_VRSb(TCGContext *s, S390Opcode op, TCGReg v1, intptr_t d2, TCGReg b2, TCGReg r3, int m4) { @@ -2710,6 +2730,43 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, VRRc, VX, a0, a1, a2, 0); break; + case INDEX_op_shli_vec: + tcg_out_insn(s, VRSa, VESL, a0, a2, TCG_REG_NONE, a1, vece); + break; + case INDEX_op_shri_vec: + tcg_out_insn(s, VRSa, VESRL, a0, a2, TCG_REG_NONE, a1, vece); + break; + case INDEX_op_sari_vec: + tcg_out_insn(s, VRSa, VESRA, a0, a2, TCG_REG_NONE, a1, vece); + break; + case INDEX_op_rotli_vec: + tcg_out_insn(s, VRSa, VERLL, a0, a2, TCG_REG_NONE, a1, vece); + break; + case INDEX_op_shls_vec: + tcg_out_insn(s, VRSa, VESL, a0, 0, a2, a1, vece); + break; + case INDEX_op_shrs_vec: + tcg_out_insn(s, VRSa, VESRL, a0, 0, a2, a1, vece); + break; + case INDEX_op_sars_vec: + tcg_out_insn(s, VRSa, VESRA, a0, 0, a2, a1, vece); + break; + case INDEX_op_rotls_vec: + tcg_out_insn(s, VRSa, VERLL, a0, 0, a2, a1, vece); + break; + case INDEX_op_shlv_vec: + tcg_out_insn(s, VRRc, VESLV, a0, a1, a2, vece); + break; + case INDEX_op_shrv_vec: + tcg_out_insn(s, VRRc, VESRLV, a0, a1, a2, vece); + break; + case INDEX_op_sarv_vec: + tcg_out_insn(s, VRRc, VESRAV, a0, a1, a2, vece); + break; + case INDEX_op_rotlv_vec: + tcg_out_insn(s, VRRc, VERLLV, a0, a1, a2, vece); + break; + case INDEX_op_cmp_vec: switch ((TCGCond)args[3]) { case TCG_COND_EQ: @@ -2744,10 +2801,23 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_not_vec: case INDEX_op_or_vec: case INDEX_op_orc_vec: + case INDEX_op_rotli_vec: + case INDEX_op_rotls_vec: + case INDEX_op_rotlv_vec: + case INDEX_op_sari_vec: + case INDEX_op_sars_vec: + case INDEX_op_sarv_vec: + case INDEX_op_shli_vec: + case INDEX_op_shls_vec: + case INDEX_op_shlv_vec: + case INDEX_op_shri_vec: + case INDEX_op_shrs_vec: + case INDEX_op_shrv_vec: case INDEX_op_sub_vec: case INDEX_op_xor_vec: return 1; case INDEX_op_cmp_vec: + case INDEX_op_rotrv_vec: return -1; case INDEX_op_mul_vec: return vece < MO_64; @@ -2810,7 +2880,7 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, TCGArg a0, ...) { va_list va; - TCGv_vec v0, v1, v2; + TCGv_vec v0, v1, v2, t0; va_start(va, a0); v0 = temp_tcgv_vec(arg_temp(a0)); @@ -2822,6 +2892,13 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, expand_vec_cmp(type, vece, v0, v1, v2, va_arg(va, TCGArg)); break; + case INDEX_op_rotrv_vec: + t0 = tcg_temp_new_vec(type); + tcg_gen_neg_vec(vece, t0, v2); + tcg_gen_rotlv_vec(vece, v0, v1, t0); + tcg_temp_free_vec(t0); + break; + default: g_assert_not_reached(); } @@ -2978,6 +3055,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_abs_vec: case INDEX_op_neg_vec: case INDEX_op_not_vec: + case INDEX_op_rotli_vec: + case INDEX_op_sari_vec: + case INDEX_op_shli_vec: + case INDEX_op_shri_vec: return C_O1_I1(v, v); case INDEX_op_add_vec: case INDEX_op_sub_vec: @@ -2988,7 +3069,17 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_xor_vec: case INDEX_op_cmp_vec: case INDEX_op_mul_vec: + case INDEX_op_rotlv_vec: + case INDEX_op_rotrv_vec: + case INDEX_op_shlv_vec: + case INDEX_op_shrv_vec: + case INDEX_op_sarv_vec: return C_O1_I2(v, v, v); + case INDEX_op_rotls_vec: + case INDEX_op_shls_vec: + case INDEX_op_shrs_vec: + case INDEX_op_sars_vec: + return C_O1_I2(v, v, r); default: g_assert_not_reached(); diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 1c581a2f60..d7d204b782 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -148,12 +148,12 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_not_vec 1 #define TCG_TARGET_HAS_neg_vec 1 #define TCG_TARGET_HAS_abs_vec 1 -#define TCG_TARGET_HAS_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 0 -#define TCG_TARGET_HAS_shi_vec 0 -#define TCG_TARGET_HAS_shs_vec 0 -#define TCG_TARGET_HAS_shv_vec 0 +#define TCG_TARGET_HAS_roti_vec 1 +#define TCG_TARGET_HAS_rots_vec 1 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 1 +#define TCG_TARGET_HAS_shv_vec 1 #define TCG_TARGET_HAS_mul_vec 1 #define TCG_TARGET_HAS_sat_vec 0 #define TCG_TARGET_HAS_minmax_vec 0 From 220db7a6c461d9e33a50134f72a2e0cb3c6c5e62 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 19:07:16 -0700 Subject: [PATCH 0306/1334] tcg/s390x: Implement TCG_TARGET_HAS_minmax_vec Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 25 +++++++++++++++++++++++++ tcg/s390x/tcg-target.h | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 98c0cd5091..3b8fc62cd7 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -282,6 +282,10 @@ typedef enum S390Opcode { VRRc_VESRAV = 0xe77a, VRRc_VESRLV = 0xe778, VRRc_VML = 0xe7a2, + VRRc_VMN = 0xe7fe, + VRRc_VMNL = 0xe7fc, + VRRc_VMX = 0xe7ff, + VRRc_VMXL = 0xe7fd, VRRc_VN = 0xe768, VRRc_VNC = 0xe769, VRRc_VNO = 0xe76b, @@ -2767,6 +2771,19 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, VRRc, VERLLV, a0, a1, a2, vece); break; + case INDEX_op_smin_vec: + tcg_out_insn(s, VRRc, VMN, a0, a1, a2, vece); + break; + case INDEX_op_smax_vec: + tcg_out_insn(s, VRRc, VMX, a0, a1, a2, vece); + break; + case INDEX_op_umin_vec: + tcg_out_insn(s, VRRc, VMNL, a0, a1, a2, vece); + break; + case INDEX_op_umax_vec: + tcg_out_insn(s, VRRc, VMXL, a0, a1, a2, vece); + break; + case INDEX_op_cmp_vec: switch ((TCGCond)args[3]) { case TCG_COND_EQ: @@ -2813,7 +2830,11 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_shri_vec: case INDEX_op_shrs_vec: case INDEX_op_shrv_vec: + case INDEX_op_smax_vec: + case INDEX_op_smin_vec: case INDEX_op_sub_vec: + case INDEX_op_umax_vec: + case INDEX_op_umin_vec: case INDEX_op_xor_vec: return 1; case INDEX_op_cmp_vec: @@ -3074,6 +3095,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_shlv_vec: case INDEX_op_shrv_vec: case INDEX_op_sarv_vec: + case INDEX_op_smax_vec: + case INDEX_op_smin_vec: + case INDEX_op_umax_vec: + case INDEX_op_umin_vec: return C_O1_I2(v, v, v); case INDEX_op_rotls_vec: case INDEX_op_shls_vec: diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index d7d204b782..a79f4f187a 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -156,7 +156,7 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_shv_vec 1 #define TCG_TARGET_HAS_mul_vec 1 #define TCG_TARGET_HAS_sat_vec 0 -#define TCG_TARGET_HAS_minmax_vec 0 +#define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 0 #define TCG_TARGET_HAS_cmpsel_vec 0 From 4223c9c1c6358f65fb2df2708c86f95faf235e30 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 20:17:29 -0700 Subject: [PATCH 0307/1334] tcg/s390x: Implement TCG_TARGET_HAS_sat_vec The unsigned saturations are handled via generic code using min/max. The signed saturations are expanded using double-sized arithmetic and a saturating pack. Since all operations are done via expansion, do not actually set TCG_TARGET_HAS_sat_vec. Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 63 ++++++++++++++++++++++++++++++++++++++ tcg/s390x/tcg-target.opc.h | 3 ++ 2 files changed, 66 insertions(+) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 3b8fc62cd7..965d9ab54c 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -291,7 +291,10 @@ typedef enum S390Opcode { VRRc_VNO = 0xe76b, VRRc_VO = 0xe76a, VRRc_VOC = 0xe76f, + VRRc_VPKS = 0xe797, /* we leave the m5 cs field 0 */ VRRc_VS = 0xe7f7, + VRRa_VUPH = 0xe7d7, + VRRa_VUPL = 0xe7d6, VRRc_VX = 0xe76d, VRRf_VLVGP = 0xe762, @@ -2800,6 +2803,16 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, } break; + case INDEX_op_s390_vuph_vec: + tcg_out_insn(s, VRRa, VUPH, a0, a1, vece); + break; + case INDEX_op_s390_vupl_vec: + tcg_out_insn(s, VRRa, VUPL, a0, a1, vece); + break; + case INDEX_op_s390_vpks_vec: + tcg_out_insn(s, VRRc, VPKS, a0, a1, a2, vece); + break; + case INDEX_op_mov_vec: /* Always emitted via tcg_out_mov. */ case INDEX_op_dup_vec: /* Always emitted via tcg_out_dup_vec. */ default: @@ -2842,6 +2855,9 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) return -1; case INDEX_op_mul_vec: return vece < MO_64; + case INDEX_op_ssadd_vec: + case INDEX_op_sssub_vec: + return vece < MO_64 ? -1 : 0; default: return 0; } @@ -2897,6 +2913,43 @@ static void expand_vec_cmp(TCGType type, unsigned vece, TCGv_vec v0, } } +static void expand_vec_sat(TCGType type, unsigned vece, TCGv_vec v0, + TCGv_vec v1, TCGv_vec v2, TCGOpcode add_sub_opc) +{ + TCGv_vec h1 = tcg_temp_new_vec(type); + TCGv_vec h2 = tcg_temp_new_vec(type); + TCGv_vec l1 = tcg_temp_new_vec(type); + TCGv_vec l2 = tcg_temp_new_vec(type); + + tcg_debug_assert (vece < MO_64); + + /* Unpack with sign-extension. */ + vec_gen_2(INDEX_op_s390_vuph_vec, type, vece, + tcgv_vec_arg(h1), tcgv_vec_arg(v1)); + vec_gen_2(INDEX_op_s390_vuph_vec, type, vece, + tcgv_vec_arg(h2), tcgv_vec_arg(v2)); + + vec_gen_2(INDEX_op_s390_vupl_vec, type, vece, + tcgv_vec_arg(l1), tcgv_vec_arg(v1)); + vec_gen_2(INDEX_op_s390_vupl_vec, type, vece, + tcgv_vec_arg(l2), tcgv_vec_arg(v2)); + + /* Arithmetic on a wider element size. */ + vec_gen_3(add_sub_opc, type, vece + 1, tcgv_vec_arg(h1), + tcgv_vec_arg(h1), tcgv_vec_arg(h2)); + vec_gen_3(add_sub_opc, type, vece + 1, tcgv_vec_arg(l1), + tcgv_vec_arg(l1), tcgv_vec_arg(l2)); + + /* Pack with saturation. */ + vec_gen_3(INDEX_op_s390_vpks_vec, type, vece + 1, + tcgv_vec_arg(v0), tcgv_vec_arg(h1), tcgv_vec_arg(l1)); + + tcg_temp_free_vec(h1); + tcg_temp_free_vec(h2); + tcg_temp_free_vec(l1); + tcg_temp_free_vec(l2); +} + void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, TCGArg a0, ...) { @@ -2920,6 +2973,13 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, tcg_temp_free_vec(t0); break; + case INDEX_op_ssadd_vec: + expand_vec_sat(type, vece, v0, v1, v2, INDEX_op_add_vec); + break; + case INDEX_op_sssub_vec: + expand_vec_sat(type, vece, v0, v1, v2, INDEX_op_sub_vec); + break; + default: g_assert_not_reached(); } @@ -3080,6 +3140,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_sari_vec: case INDEX_op_shli_vec: case INDEX_op_shri_vec: + case INDEX_op_s390_vuph_vec: + case INDEX_op_s390_vupl_vec: return C_O1_I1(v, v); case INDEX_op_add_vec: case INDEX_op_sub_vec: @@ -3099,6 +3161,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_smin_vec: case INDEX_op_umax_vec: case INDEX_op_umin_vec: + case INDEX_op_s390_vpks_vec: return C_O1_I2(v, v, v); case INDEX_op_rotls_vec: case INDEX_op_shls_vec: diff --git a/tcg/s390x/tcg-target.opc.h b/tcg/s390x/tcg-target.opc.h index 67afc82a93..0eb2350fb3 100644 --- a/tcg/s390x/tcg-target.opc.h +++ b/tcg/s390x/tcg-target.opc.h @@ -10,3 +10,6 @@ * emitted by tcg_expand_vec_op. For those familiar with GCC internals, * consider these to be UNSPEC with names. */ +DEF(s390_vuph_vec, 1, 1, 0, IMPLVEC) +DEF(s390_vupl_vec, 1, 1, 0, IMPLVEC) +DEF(s390_vpks_vec, 1, 2, 0, IMPLVEC) From 9bca986df88b8ea46b100e3d21cc9e653c83e0b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 20:36:36 -0700 Subject: [PATCH 0308/1334] tcg/s390x: Implement TCG_TARGET_HAS_bitsel_vec Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target-con-set.h | 1 + tcg/s390x/tcg-target.c.inc | 20 ++++++++++++++++++++ tcg/s390x/tcg-target.h | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 49b98f33b9..426dd92e51 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -26,6 +26,7 @@ C_O1_I2(r, r, ri) C_O1_I2(r, rZ, r) C_O1_I2(v, v, r) C_O1_I2(v, v, v) +C_O1_I3(v, v, v, v) C_O1_I4(r, r, ri, r, 0) C_O1_I4(r, r, ri, rI, 0) C_O2_I2(b, a, 0, r) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 965d9ab54c..ae132e1b75 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -296,6 +296,7 @@ typedef enum S390Opcode { VRRa_VUPH = 0xe7d7, VRRa_VUPL = 0xe7d6, VRRc_VX = 0xe76d, + VRRe_VSEL = 0xe78d, VRRf_VLVGP = 0xe762, VRSa_VERLL = 0xe733, @@ -647,6 +648,18 @@ static void tcg_out_insn_VRRc(TCGContext *s, S390Opcode op, tcg_out16(s, (op & 0x00ff) | RXB(v1, v2, v3, 0) | (m4 << 12)); } +static void tcg_out_insn_VRRe(TCGContext *s, S390Opcode op, + TCGReg v1, TCGReg v2, TCGReg v3, TCGReg v4) +{ + tcg_debug_assert(is_vector_reg(v1)); + tcg_debug_assert(is_vector_reg(v2)); + tcg_debug_assert(is_vector_reg(v3)); + tcg_debug_assert(is_vector_reg(v4)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v2 & 0xf)); + tcg_out16(s, v3 << 12); + tcg_out16(s, (op & 0x00ff) | RXB(v1, v2, v3, v4) | (v4 << 12)); +} + static void tcg_out_insn_VRRf(TCGContext *s, S390Opcode op, TCGReg v1, TCGReg r2, TCGReg r3) { @@ -2787,6 +2800,10 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, VRRc, VMXL, a0, a1, a2, vece); break; + case INDEX_op_bitsel_vec: + tcg_out_insn(s, VRRe, VSEL, a0, a1, a2, args[3]); + break; + case INDEX_op_cmp_vec: switch ((TCGCond)args[3]) { case TCG_COND_EQ: @@ -2827,6 +2844,7 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_add_vec: case INDEX_op_and_vec: case INDEX_op_andc_vec: + case INDEX_op_bitsel_vec: case INDEX_op_neg_vec: case INDEX_op_not_vec: case INDEX_op_or_vec: @@ -3168,6 +3186,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_shrs_vec: case INDEX_op_sars_vec: return C_O1_I2(v, v, r); + case INDEX_op_bitsel_vec: + return C_O1_I3(v, v, v, v); default: g_assert_not_reached(); diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index a79f4f187a..527ada0f63 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -157,7 +157,7 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_mul_vec 1 #define TCG_TARGET_HAS_sat_vec 0 #define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 0 +#define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 /* used for function call generation */ From ea3f2af8f1b87d7bced9b75ef2e788b66ec49961 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Sep 2020 20:44:53 -0700 Subject: [PATCH 0309/1334] tcg/s390x: Implement TCG_TARGET_HAS_cmpsel_vec This is via expansion; don't actually set TCG_TARGET_HAS_cmpsel_vec. Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index ae132e1b75..8938c446c8 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2869,6 +2869,7 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_xor_vec: return 1; case INDEX_op_cmp_vec: + case INDEX_op_cmpsel_vec: case INDEX_op_rotrv_vec: return -1; case INDEX_op_mul_vec: @@ -2931,6 +2932,21 @@ static void expand_vec_cmp(TCGType type, unsigned vece, TCGv_vec v0, } } +static void expand_vec_cmpsel(TCGType type, unsigned vece, TCGv_vec v0, + TCGv_vec c1, TCGv_vec c2, + TCGv_vec v3, TCGv_vec v4, TCGCond cond) +{ + TCGv_vec t = tcg_temp_new_vec(type); + + if (expand_vec_cmp_noinv(type, vece, t, c1, c2, cond)) { + /* Invert the sense of the compare by swapping arguments. */ + tcg_gen_bitsel_vec(vece, v0, t, v4, v3); + } else { + tcg_gen_bitsel_vec(vece, v0, t, v3, v4); + } + tcg_temp_free_vec(t); +} + static void expand_vec_sat(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGv_vec v2, TCGOpcode add_sub_opc) { @@ -2972,7 +2988,7 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, TCGArg a0, ...) { va_list va; - TCGv_vec v0, v1, v2, t0; + TCGv_vec v0, v1, v2, v3, v4, t0; va_start(va, a0); v0 = temp_tcgv_vec(arg_temp(a0)); @@ -2984,6 +3000,12 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, expand_vec_cmp(type, vece, v0, v1, v2, va_arg(va, TCGArg)); break; + case INDEX_op_cmpsel_vec: + v3 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); + v4 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); + expand_vec_cmpsel(type, vece, v0, v1, v2, v3, v4, va_arg(va, TCGArg)); + break; + case INDEX_op_rotrv_vec: t0 = tcg_temp_new_vec(type); tcg_gen_neg_vec(vece, t0, v2); From a6297e1ade6d9981c4a8d43eb426830b49a7913c Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 3 Sep 2021 13:38:00 +0200 Subject: [PATCH 0310/1334] include/block.h: remove outdated comment There are a couple of errors in bdrv_drained_begin header comment: - block_job_pause does not exist anymore, it has been replaced with job_pause in b15de82867 - job_pause is automatically invoked as a .drained_begin callback (child_job_drained_begin) by the child_job BdrvChildClass struct in blockjob.c. So no additional pause should be required. Signed-off-by: Emanuele Giuseppe Esposito Message-Id: <20210903113800.59970-1-eesposit@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- include/block/block.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/block/block.h b/include/block/block.h index 740038a892..ab987e8a99 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -751,9 +751,7 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, * bdrv_drained_begin: * * Begin a quiesced section for exclusive access to the BDS, by disabling - * external request sources including NBD server and device model. Note that - * this doesn't block timers or coroutines from submitting more requests, which - * means block_job_pause is still necessary. + * external request sources including NBD server, block jobs, and device model. * * This function can be recursive. */ From d1bbd965bd96028b0f5a8e3dc01b37f1f8ae4456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Aug 2021 20:04:42 +0200 Subject: [PATCH 0311/1334] qemu-storage-daemon: Only display FUSE help when FUSE is built-in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When configuring QEMU with --disable-fuse, the qemu-storage-daemon still reports FUSE command line options in its help: $ qemu-storage-daemon -h Usage: qemu-storage-daemon [options] QEMU storage daemon --export [type=]fuse,id=,node-name=,mountpoint= [,growable=on|off][,writable=on|off] export the specified block node over FUSE Remove this help message when FUSE is disabled, to avoid: $ qemu-storage-daemon --export fuse qemu-storage-daemon: --export fuse: Invalid parameter 'fuse' Reported-by: Qing Wang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210816180442.2000642-1-philmd@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Hanna Reitz Signed-off-by: Kevin Wolf --- storage-daemon/qemu-storage-daemon.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index fc8b150629..10a1a33761 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -98,10 +98,12 @@ static void help(void) " export the specified block node over NBD\n" " (requires --nbd-server)\n" "\n" +#ifdef CONFIG_FUSE " --export [type=]fuse,id=,node-name=,mountpoint=\n" " [,growable=on|off][,writable=on|off]\n" " export the specified block node over FUSE\n" "\n" +#endif /* CONFIG_FUSE */ " --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n" " configure a QMP monitor\n" "\n" From 621d17378a40509757d5e03eb1c2f5305ff76df3 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 20 Sep 2021 14:55:34 +0300 Subject: [PATCH 0312/1334] block: implement bdrv_new_open_driver_opts() Add version of bdrv_new_open_driver() that supports QDict options. We'll use it in further commit. Simply add one more argument to bdrv_new_open_driver() is worse, as there are too many invocations of bdrv_new_open_driver() to update then. Signed-off-by: Vladimir Sementsov-Ogievskiy Suggested-by: Kevin Wolf Message-Id: <20210920115538.264372-2-vsementsov@virtuozzo.com> Signed-off-by: Kevin Wolf --- block.c | 25 +++++++++++++++++++++---- include/block/block.h | 4 ++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index 5ce08a79fd..917fb7faca 100644 --- a/block.c +++ b/block.c @@ -1604,16 +1604,26 @@ open_failed: return ret; } -BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, - int flags, Error **errp) +/* + * Create and open a block node. + * + * @options is a QDict of options to pass to the block drivers, or NULL for an + * empty set of options. The reference to the QDict belongs to the block layer + * after the call (even on failure), so if the caller intends to reuse the + * dictionary, it needs to use qobject_ref() before calling bdrv_open. + */ +BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, + const char *node_name, + QDict *options, int flags, + Error **errp) { BlockDriverState *bs; int ret; bs = bdrv_new(); bs->open_flags = flags; - bs->explicit_options = qdict_new(); - bs->options = qdict_new(); + bs->options = options ?: qdict_new(); + bs->explicit_options = qdict_clone_shallow(bs->options); bs->opaque = NULL; update_options_from_flags(bs->options, flags); @@ -1631,6 +1641,13 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, return bs; } +/* Create and open a block node. */ +BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, + int flags, Error **errp) +{ + return bdrv_new_open_driver_opts(drv, node_name, NULL, flags, errp); +} + QemuOptsList bdrv_runtime_opts = { .name = "bdrv_common", .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head), diff --git a/include/block/block.h b/include/block/block.h index ab987e8a99..e5dd22b034 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -383,6 +383,10 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, const char *bdref_key, Error **errp); BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp); +BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, + const char *node_name, + QDict *options, int flags, + Error **errp); BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, int flags, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, From f053b7e8005d7f72c2a8e686c4779f75b0ae631f Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 20 Sep 2021 14:55:35 +0300 Subject: [PATCH 0313/1334] block: bdrv_insert_node(): fix and improve error handling - use ERRP_GUARD(): function calls error_prepend(), so it must use ERRP_GUARD(), otherwise error_prepend() would not be called when passed errp is error_fatal - drop error propagation, handle return code instead - for symmetry, do error_prepend() for the second failure Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210920115538.264372-3-vsementsov@virtuozzo.com> Signed-off-by: Kevin Wolf --- block.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index 917fb7faca..5d49188073 100644 --- a/block.c +++ b/block.c @@ -5122,8 +5122,9 @@ static void bdrv_delete(BlockDriverState *bs) BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, int flags, Error **errp) { + ERRP_GUARD(); + int ret; BlockDriverState *new_node_bs; - Error *local_err = NULL; new_node_bs = bdrv_open(NULL, NULL, node_options, flags, errp); if (new_node_bs == NULL) { @@ -5132,12 +5133,12 @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, } bdrv_drained_begin(bs); - bdrv_replace_node(bs, new_node_bs, &local_err); + ret = bdrv_replace_node(bs, new_node_bs, errp); bdrv_drained_end(bs); - if (local_err) { + if (ret < 0) { + error_prepend(errp, "Could not replace node: "); bdrv_unref(new_node_bs); - error_propagate(errp, local_err); return NULL; } From 96796fae6f22931d91223d086e9fa56d0f3e6720 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 20 Sep 2021 14:55:36 +0300 Subject: [PATCH 0314/1334] block: bdrv_insert_node(): doc and style - options & flags is common pair for open-like functions, let's use it - add a comment that specifies use of @options Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210920115538.264372-4-vsementsov@virtuozzo.com> Signed-off-by: Kevin Wolf --- block.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index 5d49188073..3a90407b83 100644 --- a/block.c +++ b/block.c @@ -5119,14 +5119,23 @@ static void bdrv_delete(BlockDriverState *bs) g_free(bs); } -BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, + +/* + * Replace @bs by newly created block node. + * + * @options is a QDict of options to pass to the block drivers, or NULL for an + * empty set of options. The reference to the QDict belongs to the block layer + * after the call (even on failure), so if the caller intends to reuse the + * dictionary, it needs to use qobject_ref() before calling bdrv_open. + */ +BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, int flags, Error **errp) { ERRP_GUARD(); int ret; BlockDriverState *new_node_bs; - new_node_bs = bdrv_open(NULL, NULL, node_options, flags, errp); + new_node_bs = bdrv_open(NULL, NULL, options, flags, errp); if (new_node_bs == NULL) { error_prepend(errp, "Could not create node: "); return NULL; From b11c8739ae38166acac0669cee94b7e236ccb639 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 20 Sep 2021 14:55:37 +0300 Subject: [PATCH 0315/1334] block: bdrv_insert_node(): don't use bdrv_open() Use bdrv_new_open_driver_opts() instead of complicated bdrv_open(). Among other extra things bdrv_open() also check for white-listed formats, which we don't want for internal node creation: currently backup doesn't work when copy-before-write filter is not white-listed. As well block-stream doesn't work when copy-on-read is not white-listed. Fixes: 751cec7a261adaf1145dc7adf6de7c9c084e5a0b Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2004812 Reported-by: Yanan Fu Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210920115538.264372-5-vsementsov@virtuozzo.com> Signed-off-by: Kevin Wolf --- block.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/block.c b/block.c index 3a90407b83..45f653a88b 100644 --- a/block.c +++ b/block.c @@ -5133,12 +5133,30 @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, { ERRP_GUARD(); int ret; - BlockDriverState *new_node_bs; + BlockDriverState *new_node_bs = NULL; + const char *drvname, *node_name; + BlockDriver *drv; - new_node_bs = bdrv_open(NULL, NULL, options, flags, errp); - if (new_node_bs == NULL) { + drvname = qdict_get_try_str(options, "driver"); + if (!drvname) { + error_setg(errp, "driver is not specified"); + goto fail; + } + + drv = bdrv_find_format(drvname); + if (!drv) { + error_setg(errp, "Unknown driver: '%s'", drvname); + goto fail; + } + + node_name = qdict_get_try_str(options, "node-name"); + + new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags, + errp); + options = NULL; /* bdrv_new_open_driver() eats options */ + if (!new_node_bs) { error_prepend(errp, "Could not create node: "); - return NULL; + goto fail; } bdrv_drained_begin(bs); @@ -5147,11 +5165,15 @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, if (ret < 0) { error_prepend(errp, "Could not replace node: "); - bdrv_unref(new_node_bs); - return NULL; + goto fail; } return new_node_bs; + +fail: + qobject_unref(options); + bdrv_unref(new_node_bs); + return NULL; } /* From d318fc20b2ecb785bfc74bd8ad9e0da9e47d2104 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 20 Sep 2021 14:55:38 +0300 Subject: [PATCH 0316/1334] iotests/image-fleecing: declare requirement of copy-before-write Now test fails if copy-before-write is not white-listed. Let's skip test instead. Fixes: c0605985696a19ef034fa25d04f53f3b3b383896 Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210920115538.264372-6-vsementsov@virtuozzo.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/tests/image-fleecing | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/tests/image-fleecing b/tests/qemu-iotests/tests/image-fleecing index f6318492c6..35164e9036 100755 --- a/tests/qemu-iotests/tests/image-fleecing +++ b/tests/qemu-iotests/tests/image-fleecing @@ -28,6 +28,7 @@ from iotests import log, qemu_img, qemu_io, qemu_io_silent iotests.script_initialize( supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 'vhdx', 'raw'], supported_platforms=['linux'], + required_fmts=['copy-before-write'], ) patterns = [('0x5d', '0', '64k'), From cc071629539dc1f303175a7e2d4ab854c0a8b20f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 23 Sep 2021 09:04:36 -0400 Subject: [PATCH 0317/1334] block: introduce max_hw_iov for use in scsi-generic Linux limits the size of iovecs to 1024 (UIO_MAXIOV in the kernel sources, IOV_MAX in POSIX). Because of this, on some host adapters requests with many iovecs are rejected with -EINVAL by the io_submit() or readv()/writev() system calls. In fact, the same limit applies to SG_IO as well. To fix both the EINVAL and the possible performance issues from using fewer iovecs than allowed by Linux (some HBAs have max_segments as low as 128), introduce a separate entry in BlockLimits to hold the max_segments value from sysfs. This new limit is used only for SG_IO and clamped to bs->bl.max_iov anyway, just like max_hw_transfer is clamped to bs->bl.max_transfer. Reported-by: Halil Pasic Cc: Hanna Reitz Cc: Kevin Wolf Cc: qemu-block@nongnu.org Cc: qemu-stable@nongnu.org Fixes: 18473467d5 ("file-posix: try BLKSECTGET on block devices too, do not round to power of 2", 2021-06-25) Signed-off-by: Paolo Bonzini Message-Id: <20210923130436.1187591-1-pbonzini@redhat.com> Signed-off-by: Kevin Wolf --- block/block-backend.c | 6 ++++++ block/file-posix.c | 2 +- block/io.c | 1 + hw/scsi/scsi-generic.c | 2 +- include/block/block_int.h | 7 +++++++ include/sysemu/block-backend.h | 1 + 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 6140d133e2..ba2b5ebb10 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1986,6 +1986,12 @@ uint32_t blk_get_max_transfer(BlockBackend *blk) return ROUND_DOWN(max, blk_get_request_alignment(blk)); } +int blk_get_max_hw_iov(BlockBackend *blk) +{ + return MIN_NON_ZERO(blk->root->bs->bl.max_hw_iov, + blk->root->bs->bl.max_iov); +} + int blk_get_max_iov(BlockBackend *blk) { return blk->root->bs->bl.max_iov; diff --git a/block/file-posix.c b/block/file-posix.c index c62e42743d..53be0bdc1b 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1273,7 +1273,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) ret = hdev_get_max_segments(s->fd, &st); if (ret > 0) { - bs->bl.max_iov = ret; + bs->bl.max_hw_iov = ret; } } } diff --git a/block/io.c b/block/io.c index 18d345a87a..bb0a254def 100644 --- a/block/io.c +++ b/block/io.c @@ -136,6 +136,7 @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) dst->min_mem_alignment = MAX(dst->min_mem_alignment, src->min_mem_alignment); dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov); + dst->max_hw_iov = MIN_NON_ZERO(dst->max_hw_iov, src->max_hw_iov); } typedef struct BdrvRefreshLimitsState { diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 665baf900e..0306ccc7b1 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -180,7 +180,7 @@ static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len) page = r->req.cmd.buf[2]; if (page == 0xb0) { uint64_t max_transfer = blk_get_max_hw_transfer(s->conf.blk); - uint32_t max_iov = blk_get_max_iov(s->conf.blk); + uint32_t max_iov = blk_get_max_hw_iov(s->conf.blk); assert(max_transfer); max_transfer = MIN_NON_ZERO(max_transfer, max_iov * qemu_real_host_page_size) diff --git a/include/block/block_int.h b/include/block/block_int.h index ffe86068d4..f4c75e8ba9 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -718,6 +718,13 @@ typedef struct BlockLimits { */ uint64_t max_hw_transfer; + /* Maximal number of scatter/gather elements allowed by the hardware. + * Applies whenever transfers to the device bypass the kernel I/O + * scheduler, for example with SG_IO. If larger than max_iov + * or if zero, blk_get_max_hw_iov will fall back to max_iov. + */ + int max_hw_iov; + /* memory alignment, in bytes so that no bounce buffer is needed */ size_t min_mem_alignment; diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 29d4fdbf63..82bae55161 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -211,6 +211,7 @@ uint32_t blk_get_request_alignment(BlockBackend *blk); uint32_t blk_get_max_transfer(BlockBackend *blk); uint64_t blk_get_max_hw_transfer(BlockBackend *blk); int blk_get_max_iov(BlockBackend *blk); +int blk_get_max_hw_iov(BlockBackend *blk); void blk_set_guest_block_size(BlockBackend *blk, int align); void *blk_try_blockalign(BlockBackend *blk, size_t size); void *blk_blockalign(BlockBackend *blk, size_t size); From af6d4c56e15a45fb4d0cdf8d0335275b5ed8fbf7 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 23 Sep 2021 14:07:10 -0400 Subject: [PATCH 0318/1334] iotests: add 'qemu' package location to PYTHONPATH in testenv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can drop the sys.path hacking in various places by doing this. Additionally, by doing it in one place right up top, we can print interesting warnings in case the environment does not look correct. (See next commit.) If we ever decide to change how the environment is crafted, all of the "help me find my python packages" goop is all in one place, right in one function. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Message-Id: <20210923180715.4168522-2-jsnow@redhat.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/235 | 2 -- tests/qemu-iotests/297 | 6 ------ tests/qemu-iotests/300 | 5 ++--- tests/qemu-iotests/iotests.py | 2 -- tests/qemu-iotests/testenv.py | 15 +++++++++------ tests/qemu-iotests/tests/mirror-top-perms | 7 +++---- 6 files changed, 14 insertions(+), 23 deletions(-) diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235 index 8aed45f9a7..4de920c380 100755 --- a/tests/qemu-iotests/235 +++ b/tests/qemu-iotests/235 @@ -24,8 +24,6 @@ import os import iotests from iotests import qemu_img_create, qemu_io, file_path, log -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) - from qemu.machine import QEMUMachine iotests.script_initialize(supported_fmts=['qcow2']) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index b04cba5366..467b712280 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -68,12 +68,6 @@ def run_linters(): # Todo notes are fine, but fixme's or xxx's should probably just be # fixed (in tests, at least) env = os.environ.copy() - qemu_module_path = os.path.join(os.path.dirname(__file__), - '..', '..', 'python') - try: - env['PYTHONPATH'] += os.pathsep + qemu_module_path - except KeyError: - env['PYTHONPATH'] = qemu_module_path subprocess.run(('pylint-3', '--score=n', '--notes=FIXME,XXX', *files), env=env, check=False) diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300 index fe94de84ed..10f9f2a8da 100755 --- a/tests/qemu-iotests/300 +++ b/tests/qemu-iotests/300 @@ -24,11 +24,10 @@ import random import re from typing import Dict, List, Optional +from qemu.machine import machine + import iotests -# Import qemu after iotests.py has amended sys.path -# pylint: disable=wrong-import-order -from qemu.machine import machine BlockBitmapMapping = List[Dict[str, object]] diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index ce06cf5630..b06ad76e0c 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -36,8 +36,6 @@ import unittest from contextlib import contextmanager -# pylint: disable=import-error, wrong-import-position -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu.machine import qtest from qemu.qmp import QMPMessage diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 70da0d60c8..99a57a69f3 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -108,12 +108,15 @@ class TestEnv(ContextManager['TestEnv']): SAMPLE_IMG_DIR OUTPUT_DIR """ - self.pythonpath = os.getenv('PYTHONPATH') - if self.pythonpath: - self.pythonpath = self.source_iotests + os.pathsep + \ - self.pythonpath - else: - self.pythonpath = self.source_iotests + + # Path where qemu goodies live in this source tree. + qemu_srctree_path = Path(__file__, '../../../python').resolve() + + self.pythonpath = os.pathsep.join(filter(None, ( + self.source_iotests, + str(qemu_srctree_path), + os.getenv('PYTHONPATH'), + ))) self.test_dir = os.getenv('TEST_DIR', os.path.join(os.getcwd(), 'scratch')) diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms index 2fc8dd66e0..73138a0ef9 100755 --- a/tests/qemu-iotests/tests/mirror-top-perms +++ b/tests/qemu-iotests/tests/mirror-top-perms @@ -20,13 +20,12 @@ # import os + +import qemu + import iotests from iotests import qemu_img -# Import qemu after iotests.py has amended sys.path -# pylint: disable=wrong-import-order -import qemu - image_size = 1 * 1024 * 1024 source = os.path.join(iotests.test_dir, 'source.img') From f39decb583669e4335eaf2d1a5b8183254df51d6 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 23 Sep 2021 14:07:12 -0400 Subject: [PATCH 0319/1334] iotests/linters: check mypy files all at once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can circumvent the '__main__' redefinition problem by passing --scripts-are-modules. Take mypy out of the loop per-filename and check everything in one go: it's quite a bit faster. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Message-Id: <20210923180715.4168522-4-jsnow@redhat.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/297 | 44 +++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index 467b712280..91ec34d952 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -74,32 +74,28 @@ def run_linters(): print('=== mypy ===') sys.stdout.flush() - # We have to call mypy separately for each file. Otherwise, it - # will interpret all given files as belonging together (i.e., they - # may not both define the same classes, etc.; most notably, they - # must not both define the __main__ module). env['MYPYPATH'] = env['PYTHONPATH'] - for filename in files: - p = subprocess.run(('mypy', - '--warn-unused-configs', - '--disallow-subclassing-any', - '--disallow-any-generics', - '--disallow-incomplete-defs', - '--disallow-untyped-decorators', - '--no-implicit-optional', - '--warn-redundant-casts', - '--warn-unused-ignores', - '--no-implicit-reexport', - '--namespace-packages', - filename), - env=env, - check=False, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + p = subprocess.run(('mypy', + '--warn-unused-configs', + '--disallow-subclassing-any', + '--disallow-any-generics', + '--disallow-incomplete-defs', + '--disallow-untyped-decorators', + '--no-implicit-optional', + '--warn-redundant-casts', + '--warn-unused-ignores', + '--no-implicit-reexport', + '--namespace-packages', + '--scripts-are-modules', + *files), + env=env, + check=False, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) - if p.returncode != 0: - print(p.stdout) + if p.returncode != 0: + print(p.stdout) for linter in ('pylint-3', 'mypy'): From ac7424631943c0c015934a3c92dca87b70f8f8e9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 23 Sep 2021 14:07:13 -0400 Subject: [PATCH 0320/1334] iotests/mirror-top-perms: Adjust imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to import subpackages from the qemu namespace package; importing the namespace package alone doesn't bring the subpackages with it -- unless someone else (like iotests.py) imports them too. Adjust the imports. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Hanna Reitz Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Message-Id: <20210923180715.4168522-5-jsnow@redhat.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/tests/mirror-top-perms | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms index 73138a0ef9..3d475aa3a5 100755 --- a/tests/qemu-iotests/tests/mirror-top-perms +++ b/tests/qemu-iotests/tests/mirror-top-perms @@ -21,7 +21,8 @@ import os -import qemu +from qemu import qmp +from qemu.machine import machine import iotests from iotests import qemu_img @@ -46,7 +47,7 @@ class TestMirrorTopPerms(iotests.QMPTestCase): def tearDown(self): try: self.vm.shutdown() - except qemu.machine.machine.AbnormalShutdown: + except machine.AbnormalShutdown: pass if self.vm_b is not None: @@ -101,7 +102,7 @@ class TestMirrorTopPerms(iotests.QMPTestCase): self.vm_b.launch() print('ERROR: VM B launched successfully, this should not have ' 'happened') - except qemu.qmp.QMPConnectError: + except qmp.QMPConnectError: assert 'Is another process using the image' in self.vm_b.get_log() result = self.vm.qmp('block-job-cancel', From 22968996946d1a4eaca7396099ba40867bb58642 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 23 Sep 2021 14:07:14 -0400 Subject: [PATCH 0321/1334] iotests/migrate-bitmaps-test: delint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly uninteresting stuff. Move the test injections under a function named main() so that the variables used during that process aren't in the global scope. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Hanna Reitz Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Message-Id: <20210923180715.4168522-6-jsnow@redhat.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/tests/migrate-bitmaps-test | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test index dc431c35b3..c23df3d75c 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test @@ -19,10 +19,11 @@ # along with this program. If not, see . # -import os import itertools import operator +import os import re + import iotests from iotests import qemu_img, qemu_img_create, Timeout @@ -224,25 +225,6 @@ def inject_test_case(klass, suffix, method, *args, **kwargs): setattr(klass, 'test_' + method + suffix, lambda self: mc(self)) -for cmb in list(itertools.product((True, False), repeat=5)): - name = ('_' if cmb[0] else '_not_') + 'persistent_' - name += ('_' if cmb[1] else '_not_') + 'migbitmap_' - name += '_online' if cmb[2] else '_offline' - name += '_shared' if cmb[3] else '_nonshared' - if cmb[4]: - name += '__pre_shutdown' - - inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', - *list(cmb)) - -for cmb in list(itertools.product((True, False), repeat=2)): - name = ('_' if cmb[0] else '_not_') + 'persistent_' - name += ('_' if cmb[1] else '_not_') + 'migbitmap' - - inject_test_case(TestDirtyBitmapMigration, name, - 'do_test_migration_resume_source', *list(cmb)) - - class TestDirtyBitmapBackingMigration(iotests.QMPTestCase): def setUp(self): qemu_img_create('-f', iotests.imgfmt, base_a, size) @@ -304,6 +286,30 @@ class TestDirtyBitmapBackingMigration(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) +def main() -> None: + for cmb in list(itertools.product((True, False), repeat=5)): + name = ('_' if cmb[0] else '_not_') + 'persistent_' + name += ('_' if cmb[1] else '_not_') + 'migbitmap_' + name += '_online' if cmb[2] else '_offline' + name += '_shared' if cmb[3] else '_nonshared' + if cmb[4]: + name += '__pre_shutdown' + + inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', + *list(cmb)) + + for cmb in list(itertools.product((True, False), repeat=2)): + name = ('_' if cmb[0] else '_not_') + 'persistent_' + name += ('_' if cmb[1] else '_not_') + 'migbitmap' + + inject_test_case(TestDirtyBitmapMigration, name, + 'do_test_migration_resume_source', *list(cmb)) + + iotests.main( + supported_fmts=['qcow2'], + supported_protocols=['file'] + ) + + if __name__ == '__main__': - iotests.main(supported_fmts=['qcow2'], - supported_protocols=['file']) + main() From 3765315d4c84f9c0799744f43a314169baaccc05 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 23 Sep 2021 14:07:15 -0400 Subject: [PATCH 0322/1334] iotests: Update for pylint 2.11.1 1. Ignore the new f-strings warning, we're not interested in doing a full conversion at this time. 2. Just mute the unbalanced-tuple-unpacking warning, it's not a real error in this case and muting the dozens of callsites is just not worth it. 3. Add encodings to read_text(). Signed-off-by: John Snow Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Message-Id: <20210923180715.4168522-7-jsnow@redhat.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/pylintrc | 6 +++++- tests/qemu-iotests/testrunner.py | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index f2c0b522ac..8cb4e1d6a6 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -19,13 +19,17 @@ disable=invalid-name, too-many-public-methods, # pylint warns about Optional[] etc. as unsubscriptable in 3.9 unsubscriptable-object, + # pylint's static analysis causes false positivies for file_path(); + # If we really care to make it statically knowable, we'll use mypy. + unbalanced-tuple-unpacking, # Sometimes we need to disable a newly introduced pylint warning. # Doing so should not produce a warning in older versions of pylint. bad-option-value, # These are temporary, and should be removed: missing-docstring, too-many-return-statements, - too-many-statements + too-many-statements, + consider-using-f-string, [FORMAT] diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 4a6ec421ed..a56b6da396 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -266,12 +266,13 @@ class TestRunner(ContextManager['TestRunner']): diff=file_diff(str(f_reference), str(f_bad))) if f_notrun.exists(): - return TestResult(status='not run', - description=f_notrun.read_text().strip()) + return TestResult( + status='not run', + description=f_notrun.read_text(encoding='utf-8').strip()) casenotrun = '' if f_casenotrun.exists(): - casenotrun = f_casenotrun.read_text() + casenotrun = f_casenotrun.read_text(encoding='utf-8') diff = file_diff(str(f_reference), str(f_bad)) if diff: From c23b5764e79f3951e98160faf6e97284453c4056 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 22 Sep 2021 10:30:46 -0500 Subject: [PATCH 0323/1334] Hexagon (target/hexagon) probe the stores in a packet at start of commit When a packet has 2 stores, either both commit or neither commit. At the beginning of gen_commit_packet, we check for multiple stores. If there are multiple stores, call a helper that will probe each of them before proceeding with the commit. Note that we don't call the probe helper for packets with only one store. Therefore, we call process_store_log before anything else involved in committing the packet. We also fix a typo in the comment in process_store_log. Test case added in tests/tcg/hexagon/hex_sigsegv.c Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson Message-Id: <1633036599-7637-1-git-send-email-tsimpson@quicinc.com> --- target/hexagon/helper.h | 2 + target/hexagon/op_helper.c | 16 +++++ target/hexagon/translate.c | 38 ++++++++++- tests/tcg/hexagon/Makefile.target | 1 + tests/tcg/hexagon/hex_sigsegv.c | 106 ++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 tests/tcg/hexagon/hex_sigsegv.c diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index ca201fb680..89de2a3ee5 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -89,3 +89,5 @@ DEF_HELPER_4(sffms_lib, f32, env, f32, f32, f32) DEF_HELPER_3(dfmpyfix, f64, env, f64, f64) DEF_HELPER_4(dfmpyhh, f64, env, f64, f64, f64) + +DEF_HELPER_2(probe_pkt_scalar_store_s0, void, env, int) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 61d5cde939..af32de4578 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -377,6 +377,22 @@ int32_t HELPER(vacsh_pred)(CPUHexagonState *env, return PeV; } +static void probe_store(CPUHexagonState *env, int slot, int mmu_idx) +{ + if (!(env->slot_cancelled & (1 << slot))) { + size1u_t width = env->mem_log_stores[slot].width; + target_ulong va = env->mem_log_stores[slot].va; + uintptr_t ra = GETPC(); + probe_write(env, va, width, mmu_idx, ra); + } +} + +/* Called during packet commit when there are two scalar stores */ +void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int mmu_idx) +{ + probe_store(env, 0, mmu_idx); +} + /* * mem_noshuf * Section 5.5 of the Hexagon V67 Programmer's Reference Manual diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 6fb4e6853c..51930e85a2 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -419,7 +419,7 @@ static void process_store_log(DisasContext *ctx, Packet *pkt) { /* * When a packet has two stores, the hardware processes - * slot 1 and then slot 2. This will be important when + * slot 1 and then slot 0. This will be important when * the memory accesses overlap. */ if (pkt->pkt_has_store_s1 && !pkt->pkt_has_dczeroa) { @@ -471,10 +471,42 @@ static void update_exec_counters(DisasContext *ctx, Packet *pkt) static void gen_commit_packet(DisasContext *ctx, Packet *pkt) { + /* + * If there is more than one store in a packet, make sure they are all OK + * before proceeding with the rest of the packet commit. + * + * dczeroa has to be the only store operation in the packet, so we go + * ahead and process that first. + * + * When there are two scalar stores, we probe the one in slot 0. + * + * Note that we don't call the probe helper for packets with only one + * store. Therefore, we call process_store_log before anything else + * involved in committing the packet. + */ + bool has_store_s0 = pkt->pkt_has_store_s0; + bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed); + if (pkt->pkt_has_dczeroa) { + /* + * The dczeroa will be the store in slot 0, check that we don't have + * a store in slot 1. + */ + g_assert(has_store_s0 && !has_store_s1); + process_dczeroa(ctx, pkt); + } else if (has_store_s0 && has_store_s1) { + /* + * process_store_log will execute the slot 1 store first, + * so we only have to probe the store in slot 0 + */ + TCGv mem_idx = tcg_const_tl(ctx->mem_idx); + gen_helper_probe_pkt_scalar_store_s0(cpu_env, mem_idx); + tcg_temp_free(mem_idx); + } + + process_store_log(ctx, pkt); + gen_reg_writes(ctx); gen_pred_writes(ctx, pkt); - process_store_log(ctx, pkt); - process_dczeroa(ctx, pkt); update_exec_counters(ctx, pkt); if (HEX_DEBUG) { TCGv has_st0 = diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 050cd61c1a..c1e1650798 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -28,6 +28,7 @@ first: $(HEX_SRC)/first.S $(CC) -static -mv67 -nostdlib $^ -o $@ HEX_TESTS = first +HEX_TESTS += hex_sigsegv HEX_TESTS += misc HEX_TESTS += preg_alias HEX_TESTS += dual_stores diff --git a/tests/tcg/hexagon/hex_sigsegv.c b/tests/tcg/hexagon/hex_sigsegv.c new file mode 100644 index 0000000000..dc2b349257 --- /dev/null +++ b/tests/tcg/hexagon/hex_sigsegv.c @@ -0,0 +1,106 @@ +/* + * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Test the VLIW semantics of two stores in a packet + * + * When a packet has 2 stores, either both commit or neither commit. + * We test this with a packet that does stores to both NULL and a global + * variable, "should_not_change". After the SIGSEGV is caught, we check + * that the "should_not_change" value is the same. + */ + +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char uint8_t; + +int err; +int segv_caught; + +#define SHOULD_NOT_CHANGE_VAL 5 +int should_not_change = SHOULD_NOT_CHANGE_VAL; + +#define BUF_SIZE 300 +unsigned char buf[BUF_SIZE]; + + +static void __check(const char *filename, int line, int x, int expect) +{ + if (x != expect) { + printf("ERROR %s:%d - %d != %d\n", + filename, line, x, expect); + err++; + } +} + +#define check(x, expect) __check(__FILE__, __LINE__, (x), (expect)) + +static void __chk_error(const char *filename, int line, int ret) +{ + if (ret < 0) { + printf("ERROR %s:%d - %d\n", filename, line, ret); + err++; + } +} + +#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret)) + +jmp_buf jmp_env; + +static void sig_segv(int sig, siginfo_t *info, void *puc) +{ + check(sig, SIGSEGV); + segv_caught = 1; + longjmp(jmp_env, 1); +} + +int main() +{ + struct sigaction act; + + /* SIGSEGV test */ + act.sa_sigaction = sig_segv; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + chk_error(sigaction(SIGSEGV, &act, NULL)); + if (setjmp(jmp_env) == 0) { + asm volatile("r18 = ##should_not_change\n\t" + "r19 = #0\n\t" + "{\n\t" + " memw(r18) = #7\n\t" + " memw(r19) = #0\n\t" + "}\n\t" + : : : "r18", "r19", "memory"); + } + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + chk_error(sigaction(SIGSEGV, &act, NULL)); + + check(segv_caught, 1); + check(should_not_change, SHOULD_NOT_CHANGE_VAL); + + puts(err ? "FAIL" : "PASS"); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} From f844f745a81a8b8dc7f85eaa3fe6a3bb880afaff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 02:47:49 +0200 Subject: [PATCH 0324/1334] target/hexagon: Remove unused TCG temporary from predicated loads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gen_pred_cancel() function, introduced in commit a646e99cb90 (Hexagon macros) doesn't use the 'one' TCG temporary; remove it. Reviewed-by: Richard Henderson Reviewed-by: Taylor Simpson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211003004750.3608983-2-f4bug@amsat.org> --- target/hexagon/macros.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 094b8dabb5..ae6663c09f 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -190,7 +190,6 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num) TCGv slot_mask = tcg_const_tl(1 << slot_num); TCGv tmp = tcg_temp_new(); TCGv zero = tcg_const_tl(0); - TCGv one = tcg_const_tl(1); tcg_gen_or_tl(slot_mask, hex_slot_cancelled, slot_mask); tcg_gen_andi_tl(tmp, pred, 1); tcg_gen_movcond_tl(TCG_COND_EQ, hex_slot_cancelled, tmp, zero, @@ -198,7 +197,6 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num) tcg_temp_free(slot_mask); tcg_temp_free(tmp); tcg_temp_free(zero); - tcg_temp_free(one); } #define PRED_LOAD_CANCEL(PRED, EA) \ gen_pred_cancel(PRED, insn->is_endloop ? 4 : insn->slot) From 23803bbe524c34d5725508f169a0a23f652e6584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 02:47:50 +0200 Subject: [PATCH 0325/1334] target/hexagon: Use tcg_constant_* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace uses of tcg_const_* with the allocate and free close together. Inspired-by: Richard Henderson Reviewed-by: Richard Henderson Reviewed-by: Taylor Simpson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211003004750.3608983-3-f4bug@amsat.org> --- target/hexagon/gen_tcg.h | 6 ++--- target/hexagon/gen_tcg_funcs.py | 4 +--- target/hexagon/genptr.c | 39 +++++++++++---------------------- target/hexagon/macros.h | 3 +-- target/hexagon/translate.c | 26 +++++++--------------- 5 files changed, 25 insertions(+), 53 deletions(-) diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index ee94c903db..0361564104 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -684,9 +684,8 @@ gen_helper_sfmin(RdV, cpu_env, RsV, RtV) #define fGEN_TCG_F2_sfclass(SHORTCODE) \ do { \ - TCGv imm = tcg_const_tl(uiV); \ + TCGv imm = tcg_constant_tl(uiV); \ gen_helper_sfclass(PdV, cpu_env, RsV, imm); \ - tcg_temp_free(imm); \ } while (0) #define fGEN_TCG_F2_sffixupn(SHORTCODE) \ gen_helper_sffixupn(RdV, cpu_env, RsV, RtV) @@ -712,9 +711,8 @@ gen_helper_dfcmpuo(PdV, cpu_env, RssV, RttV) #define fGEN_TCG_F2_dfclass(SHORTCODE) \ do { \ - TCGv imm = tcg_const_tl(uiV); \ + TCGv imm = tcg_constant_tl(uiV); \ gen_helper_dfclass(PdV, cpu_env, RssV, imm); \ - tcg_temp_free(imm); \ } while (0) #define fGEN_TCG_F2_sfmpy(SHORTCODE) \ gen_helper_sfmpy(RdV, cpu_env, RsV, RtV) diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index 7ceb25b5f6..ca8a801baa 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -403,7 +403,7 @@ def gen_tcg_func(f, tag, regs, imms): if hex_common.need_part1(tag): f.write(" TCGv part1 = tcg_const_tl(insn->part1);\n") if hex_common.need_slot(tag): - f.write(" TCGv slot = tcg_const_tl(insn->slot);\n") + f.write(" TCGv slot = tcg_constant_tl(insn->slot);\n") f.write(" gen_helper_%s(" % (tag)) i=0 ## If there is a scalar result, it is the return type @@ -424,8 +424,6 @@ def gen_tcg_func(f, tag, regs, imms): if hex_common.need_slot(tag): f.write(", slot") if hex_common.need_part1(tag): f.write(", part1" ) f.write(");\n") - if hex_common.need_slot(tag): - f.write(" tcg_temp_free(slot);\n") if hex_common.need_part1(tag): f.write(" tcg_temp_free(part1);\n") for immlett,bits,immshift in imms: diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 7333299615..4a21fa590f 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -29,7 +29,7 @@ static inline void gen_log_predicated_reg_write(int rnum, TCGv val, int slot) { - TCGv zero = tcg_const_tl(0); + TCGv zero = tcg_constant_tl(0); TCGv slot_mask = tcg_temp_new(); tcg_gen_andi_tl(slot_mask, hex_slot_cancelled, 1 << slot); @@ -47,7 +47,6 @@ static inline void gen_log_predicated_reg_write(int rnum, TCGv val, int slot) tcg_gen_or_tl(hex_reg_written[rnum], hex_reg_written[rnum], slot_mask); } - tcg_temp_free(zero); tcg_temp_free(slot_mask); } @@ -63,7 +62,7 @@ static inline void gen_log_reg_write(int rnum, TCGv val) static void gen_log_predicated_reg_write_pair(int rnum, TCGv_i64 val, int slot) { TCGv val32 = tcg_temp_new(); - TCGv zero = tcg_const_tl(0); + TCGv zero = tcg_constant_tl(0); TCGv slot_mask = tcg_temp_new(); tcg_gen_andi_tl(slot_mask, hex_slot_cancelled, 1 << slot); @@ -92,7 +91,6 @@ static void gen_log_predicated_reg_write_pair(int rnum, TCGv_i64 val, int slot) } tcg_temp_free(val32); - tcg_temp_free(zero); tcg_temp_free(slot_mask); } @@ -181,9 +179,8 @@ static inline void gen_read_ctrl_reg_pair(DisasContext *ctx, const int reg_num, tcg_gen_concat_i32_i64(dest, p3_0, hex_gpr[reg_num + 1]); tcg_temp_free(p3_0); } else if (reg_num == HEX_REG_PC - 1) { - TCGv pc = tcg_const_tl(ctx->base.pc_next); + TCGv pc = tcg_constant_tl(ctx->base.pc_next); tcg_gen_concat_i32_i64(dest, hex_gpr[reg_num], pc); - tcg_temp_free(pc); } else if (reg_num == HEX_REG_QEMU_PKT_CNT) { TCGv pkt_cnt = tcg_temp_new(); TCGv insn_cnt = tcg_temp_new(); @@ -331,15 +328,13 @@ static inline void gen_store_conditional4(DisasContext *ctx, tcg_gen_brcond_tl(TCG_COND_NE, vaddr, hex_llsc_addr, fail); - one = tcg_const_tl(0xff); - zero = tcg_const_tl(0); + one = tcg_constant_tl(0xff); + zero = tcg_constant_tl(0); tmp = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(tmp, hex_llsc_addr, hex_llsc_val, src, ctx->mem_idx, MO_32); tcg_gen_movcond_tl(TCG_COND_EQ, pred, tmp, hex_llsc_val, one, zero); - tcg_temp_free(one); - tcg_temp_free(zero); tcg_temp_free(tmp); tcg_gen_br(done); @@ -359,16 +354,14 @@ static inline void gen_store_conditional8(DisasContext *ctx, tcg_gen_brcond_tl(TCG_COND_NE, vaddr, hex_llsc_addr, fail); - one = tcg_const_i64(0xff); - zero = tcg_const_i64(0); + one = tcg_constant_i64(0xff); + zero = tcg_constant_i64(0); tmp = tcg_temp_new_i64(); tcg_gen_atomic_cmpxchg_i64(tmp, hex_llsc_addr, hex_llsc_val_i64, src, ctx->mem_idx, MO_64); tcg_gen_movcond_i64(TCG_COND_EQ, tmp, tmp, hex_llsc_val_i64, one, zero); tcg_gen_extrl_i64_i32(pred, tmp); - tcg_temp_free_i64(one); - tcg_temp_free_i64(zero); tcg_temp_free_i64(tmp); tcg_gen_br(done); @@ -396,9 +389,8 @@ static inline void gen_store1(TCGv_env cpu_env, TCGv vaddr, TCGv src, static inline void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src, DisasContext *ctx, int slot) { - TCGv tmp = tcg_const_tl(src); + TCGv tmp = tcg_constant_tl(src); gen_store1(cpu_env, vaddr, tmp, ctx, slot); - tcg_temp_free(tmp); } static inline void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, @@ -411,9 +403,8 @@ static inline void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, static inline void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src, DisasContext *ctx, int slot) { - TCGv tmp = tcg_const_tl(src); + TCGv tmp = tcg_constant_tl(src); gen_store2(cpu_env, vaddr, tmp, ctx, slot); - tcg_temp_free(tmp); } static inline void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, @@ -426,9 +417,8 @@ static inline void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, static inline void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src, DisasContext *ctx, int slot) { - TCGv tmp = tcg_const_tl(src); + TCGv tmp = tcg_constant_tl(src); gen_store4(cpu_env, vaddr, tmp, ctx, slot); - tcg_temp_free(tmp); } static inline void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, @@ -443,18 +433,15 @@ static inline void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, static inline void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, DisasContext *ctx, int slot) { - TCGv_i64 tmp = tcg_const_i64(src); + TCGv_i64 tmp = tcg_constant_i64(src); gen_store8(cpu_env, vaddr, tmp, ctx, slot); - tcg_temp_free_i64(tmp); } static TCGv gen_8bitsof(TCGv result, TCGv value) { - TCGv zero = tcg_const_tl(0); - TCGv ones = tcg_const_tl(0xff); + TCGv zero = tcg_constant_tl(0); + TCGv ones = tcg_constant_tl(0xff); tcg_gen_movcond_tl(TCG_COND_NE, result, value, zero, ones, zero); - tcg_temp_free(zero); - tcg_temp_free(ones); return result; } diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index ae6663c09f..44e9b857b5 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -189,14 +189,13 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num) { TCGv slot_mask = tcg_const_tl(1 << slot_num); TCGv tmp = tcg_temp_new(); - TCGv zero = tcg_const_tl(0); + TCGv zero = tcg_constant_tl(0); tcg_gen_or_tl(slot_mask, hex_slot_cancelled, slot_mask); tcg_gen_andi_tl(tmp, pred, 1); tcg_gen_movcond_tl(TCG_COND_EQ, hex_slot_cancelled, tmp, zero, slot_mask, hex_slot_cancelled); tcg_temp_free(slot_mask); tcg_temp_free(tmp); - tcg_temp_free(zero); } #define PRED_LOAD_CANCEL(PRED, EA) \ gen_pred_cancel(PRED, insn->is_endloop ? 4 : insn->slot) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 51930e85a2..4f05ce3388 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -54,9 +54,7 @@ static const char * const hexagon_prednames[] = { static void gen_exception_raw(int excp) { - TCGv_i32 helper_tmp = tcg_const_i32(excp); - gen_helper_raise_exception(cpu_env, helper_tmp); - tcg_temp_free_i32(helper_tmp); + gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); } static void gen_exec_counters(DisasContext *ctx) @@ -288,7 +286,7 @@ static void gen_pred_writes(DisasContext *ctx, Packet *pkt) * write of the predicates. */ if (pkt->pkt_has_endloop) { - TCGv zero = tcg_const_tl(0); + TCGv zero = tcg_constant_tl(0); TCGv pred_written = tcg_temp_new(); for (i = 0; i < ctx->preg_log_idx; i++) { int pred_num = ctx->preg_log[i]; @@ -299,7 +297,6 @@ static void gen_pred_writes(DisasContext *ctx, Packet *pkt) hex_new_pred_value[pred_num], hex_pred[pred_num]); } - tcg_temp_free(zero); tcg_temp_free(pred_written); } else { for (i = 0; i < ctx->preg_log_idx; i++) { @@ -317,11 +314,9 @@ static void gen_pred_writes(DisasContext *ctx, Packet *pkt) static void gen_check_store_width(DisasContext *ctx, int slot_num) { if (HEX_DEBUG) { - TCGv slot = tcg_const_tl(slot_num); - TCGv check = tcg_const_tl(ctx->store_width[slot_num]); + TCGv slot = tcg_constant_tl(slot_num); + TCGv check = tcg_constant_tl(ctx->store_width[slot_num]); gen_helper_debug_check_store_width(cpu_env, slot, check); - tcg_temp_free(slot); - tcg_temp_free(check); } } @@ -403,9 +398,8 @@ void process_store(DisasContext *ctx, Packet *pkt, int slot_num) * TCG generation time, we'll use a helper to * avoid branching based on the width at runtime. */ - TCGv slot = tcg_const_tl(slot_num); + TCGv slot = tcg_constant_tl(slot_num); gen_helper_commit_store(cpu_env, slot); - tcg_temp_free(slot); } } tcg_temp_free(address); @@ -436,7 +430,7 @@ static void process_dczeroa(DisasContext *ctx, Packet *pkt) if (pkt->pkt_has_dczeroa) { /* Store 32 bytes of zero starting at (addr & ~0x1f) */ TCGv addr = tcg_temp_new(); - TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_andi_tl(addr, hex_dczero_addr, ~0x1f); tcg_gen_qemu_st64(zero, addr, ctx->mem_idx); @@ -448,7 +442,6 @@ static void process_dczeroa(DisasContext *ctx, Packet *pkt) tcg_gen_qemu_st64(zero, addr, ctx->mem_idx); tcg_temp_free(addr); - tcg_temp_free_i64(zero); } } @@ -510,15 +503,12 @@ static void gen_commit_packet(DisasContext *ctx, Packet *pkt) update_exec_counters(ctx, pkt); if (HEX_DEBUG) { TCGv has_st0 = - tcg_const_tl(pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa); + tcg_constant_tl(pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa); TCGv has_st1 = - tcg_const_tl(pkt->pkt_has_store_s1 && !pkt->pkt_has_dczeroa); + tcg_constant_tl(pkt->pkt_has_store_s1 && !pkt->pkt_has_dczeroa); /* Handy place to set a breakpoint at the end of execution */ gen_helper_debug_commit_end(cpu_env, has_st0, has_st1); - - tcg_temp_free(has_st0); - tcg_temp_free(has_st1); } if (pkt->pkt_has_cof) { From c5b4ee5bb799685e1c5a4a30ab40013a984daded Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:01 +0200 Subject: [PATCH 0326/1334] target/riscv: Introduce temporary in gen_add_uw() Following the recent changes in translate.c, gen_add_uw() causes failures on CF3 and SPEC2017 due to the reuse of arg1. Fix these regressions by introducing a temporary. Signed-off-by: Philipp Tomsich Reviewed-by: Alistair Francis Reviewed-by: Bin Meng Reviewed-by: Richard Henderson Message-id: 20210911140016.834071-2-philipp.tomsich@vrull.eu Fixes: 191d1dafae9c ("target/riscv: Add DisasExtend to gen_arith*") Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index b72e76255c..c0a6e25826 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -624,8 +624,10 @@ GEN_TRANS_SHADD_UW(3) static void gen_add_uw(TCGv ret, TCGv arg1, TCGv arg2) { - tcg_gen_ext32u_tl(arg1, arg1); - tcg_gen_add_tl(ret, arg1, arg2); + TCGv t = tcg_temp_new(); + tcg_gen_ext32u_tl(t, arg1); + tcg_gen_add_tl(ret, t, arg2); + tcg_temp_free(t); } static bool trans_add_uw(DisasContext *ctx, arg_add_uw *a) From e47fb6c1e96a4e50603c13b8408e0745a09cd867 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:02 +0200 Subject: [PATCH 0327/1334] target/riscv: fix clzw implementation to operate on arg1 The refactored gen_clzw() uses ret as its argument, instead of arg1. Fix it. Signed-off-by: Philipp Tomsich Reviewed-by: Alistair Francis Reviewed-by: Bin Meng Reviewed-by: Richard Henderson Message-id: 20210911140016.834071-3-philipp.tomsich@vrull.eu Fixes: 60903915050 ("target/riscv: Add DisasExtend to gen_unary") Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index c0a6e25826..6c85c89f6d 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -349,7 +349,7 @@ GEN_TRANS_SHADD(3) static void gen_clzw(TCGv ret, TCGv arg1) { - tcg_gen_clzi_tl(ret, ret, 64); + tcg_gen_clzi_tl(ret, arg1, 64); tcg_gen_subi_tl(ret, ret, 32); } From 45d1749c1c32f7f44e02f267407cd6bca88fb84a Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:03 +0200 Subject: [PATCH 0328/1334] target/riscv: clwz must ignore high bits (use shift-left & changed logic) Assume clzw being executed on a register that is not sign-extended, such as for the following sequence that uses (1ULL << 63) | 392 as the operand to clzw: bseti a2, zero, 63 addi a2, a2, 392 clzw a3, a2 The correct result of clzw would be 23, but the current implementation returns -32 (as it performs a 64bit clz, which results in 0 leading zero bits, and then subtracts 32). Fix this by changing the implementation to: 1. shift the original register up by 32 2. performs a target-length (64bit) clz 3. return 32 if no bits are set Marking this instruction as 'w-form' (i.e., setting ctx->w) would not correctly model the behaviour, as the instruction should not perform a zero-extensions on the input (after all, it is not a .uw instruction) and the result is always in the range 0..32 (so neither a sign-extension nor a zero-extension on the result will ever be needed). Consequently, we do not set ctx->w and mark the instruction as EXT_NONE. Signed-off-by: Philipp Tomsich Reviewed-by: LIU Zhiwei Message-id: 20210911140016.834071-4-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 6c85c89f6d..73d1e45026 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -349,15 +349,17 @@ GEN_TRANS_SHADD(3) static void gen_clzw(TCGv ret, TCGv arg1) { - tcg_gen_clzi_tl(ret, arg1, 64); - tcg_gen_subi_tl(ret, ret, 32); + TCGv t = tcg_temp_new(); + tcg_gen_shli_tl(t, arg1, 32); + tcg_gen_clzi_tl(ret, t, 32); + tcg_temp_free(t); } static bool trans_clzw(DisasContext *ctx, arg_clzw *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVB); - return gen_unary(ctx, a, EXT_ZERO, gen_clzw); + return gen_unary(ctx, a, EXT_NONE, gen_clzw); } static void gen_ctzw(TCGv ret, TCGv arg1) From 878dd0e9ac18af657dcbdb598bb2eb1278606d3a Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:04 +0200 Subject: [PATCH 0329/1334] target/riscv: Add x-zba, x-zbb, x-zbc and x-zbs properties The bitmanipulation ISA extensions will be ratified as individual small extension packages instead of a large B-extension. The first new instructions through the door (these have completed public review) are Zb[abcs]. This adds new 'x-zba', 'x-zbb', 'x-zbc' and 'x-zbs' properties for these in target/riscv/cpu.[ch]. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 20210911140016.834071-5-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 4 ++++ target/riscv/cpu.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 7c626d89cd..785a3a8d19 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -617,6 +617,10 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), /* This is experimental so mark with 'x-' */ DEFINE_PROP_BOOL("x-b", RISCVCPU, cfg.ext_b, false), + DEFINE_PROP_BOOL("x-zba", RISCVCPU, cfg.ext_zba, false), + DEFINE_PROP_BOOL("x-zbb", RISCVCPU, cfg.ext_zbb, false), + DEFINE_PROP_BOOL("x-zbc", RISCVCPU, cfg.ext_zbc, false), + DEFINE_PROP_BOOL("x-zbs", RISCVCPU, cfg.ext_zbs, false), DEFINE_PROP_BOOL("x-h", RISCVCPU, cfg.ext_h, false), DEFINE_PROP_BOOL("x-v", RISCVCPU, cfg.ext_v, false), DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 5896aca346..1a38723f2c 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -293,6 +293,10 @@ struct RISCVCPU { bool ext_u; bool ext_h; bool ext_v; + bool ext_zba; + bool ext_zbb; + bool ext_zbc; + bool ext_zbs; bool ext_counters; bool ext_ifencei; bool ext_icsr; From bb4dc158e084c3293b553c37f3ace99997aeff6f Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:05 +0200 Subject: [PATCH 0330/1334] target/riscv: Reassign instructions to the Zba-extension The following instructions are part of Zba: - add.uw (RV64 only) - sh[123]add (RV32 and RV64) - sh[123]add.uw (RV64-only) - slli.uw (RV64-only) Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Acked-by: Bin Meng Message-id: 20210911140016.834071-6-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/insn32.decode | 20 ++++++++++++-------- target/riscv/insn_trans/trans_rvb.c.inc | 16 +++++++++++----- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 2cd921d51c..86f1166dab 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -660,6 +660,18 @@ vamomaxd_v 10100 . . ..... ..... 111 ..... 0101111 @r_wdvm vamominud_v 11000 . . ..... ..... 111 ..... 0101111 @r_wdvm vamomaxud_v 11100 . . ..... ..... 111 ..... 0101111 @r_wdvm +# *** RV32 Zba Standard Extension *** +sh1add 0010000 .......... 010 ..... 0110011 @r +sh2add 0010000 .......... 100 ..... 0110011 @r +sh3add 0010000 .......... 110 ..... 0110011 @r + +# *** RV64 Zba Standard Extension (in addition to RV32 Zba) *** +add_uw 0000100 .......... 000 ..... 0111011 @r +sh1add_uw 0010000 .......... 010 ..... 0111011 @r +sh2add_uw 0010000 .......... 100 ..... 0111011 @r +sh3add_uw 0010000 .......... 110 ..... 0111011 @r +slli_uw 00001 ............ 001 ..... 0011011 @sh + # *** RV32B Standard Extension *** clz 011000 000000 ..... 001 ..... 0010011 @r2 ctz 011000 000001 ..... 001 ..... 0010011 @r2 @@ -687,9 +699,6 @@ ror 0110000 .......... 101 ..... 0110011 @r rol 0110000 .......... 001 ..... 0110011 @r grev 0110100 .......... 101 ..... 0110011 @r gorc 0010100 .......... 101 ..... 0110011 @r -sh1add 0010000 .......... 010 ..... 0110011 @r -sh2add 0010000 .......... 100 ..... 0110011 @r -sh3add 0010000 .......... 110 ..... 0110011 @r bseti 00101. ........... 001 ..... 0010011 @sh bclri 01001. ........... 001 ..... 0010011 @sh @@ -718,10 +727,6 @@ rorw 0110000 .......... 101 ..... 0111011 @r rolw 0110000 .......... 001 ..... 0111011 @r grevw 0110100 .......... 101 ..... 0111011 @r gorcw 0010100 .......... 101 ..... 0111011 @r -sh1add_uw 0010000 .......... 010 ..... 0111011 @r -sh2add_uw 0010000 .......... 100 ..... 0111011 @r -sh3add_uw 0010000 .......... 110 ..... 0111011 @r -add_uw 0000100 .......... 000 ..... 0111011 @r bsetiw 0010100 .......... 001 ..... 0011011 @sh5 bclriw 0100100 .......... 001 ..... 0011011 @sh5 @@ -732,4 +737,3 @@ roriw 0110000 .......... 101 ..... 0011011 @sh5 greviw 0110100 .......... 101 ..... 0011011 @sh5 gorciw 0010100 .......... 101 ..... 0011011 @sh5 -slli_uw 00001. ........... 001 ..... 0011011 @sh diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 73d1e45026..fd549c7b0f 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -1,8 +1,9 @@ /* - * RISC-V translation routines for the RVB Standard Extension. + * RISC-V translation routines for the RVB draft and Zba Standard Extension. * * Copyright (c) 2020 Kito Cheng, kito.cheng@sifive.com * Copyright (c) 2020 Frank Chang, frank.chang@sifive.com + * Copyright (c) 2021 Philipp Tomsich, philipp.tomsich@vrull.eu * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -17,6 +18,11 @@ * this program. If not, see . */ +#define REQUIRE_ZBA(ctx) do { \ + if (!RISCV_CPU(ctx->cs)->cfg.ext_zba) { \ + return false; \ + } \ +} while (0) static void gen_clz(TCGv ret, TCGv arg1) { @@ -339,7 +345,7 @@ GEN_SHADD(3) #define GEN_TRANS_SHADD(SHAMT) \ static bool trans_sh##SHAMT##add(DisasContext *ctx, arg_sh##SHAMT##add *a) \ { \ - REQUIRE_EXT(ctx, RVB); \ + REQUIRE_ZBA(ctx); \ return gen_arith(ctx, a, EXT_NONE, gen_sh##SHAMT##add); \ } @@ -616,7 +622,7 @@ static bool trans_sh##SHAMT##add_uw(DisasContext *ctx, \ arg_sh##SHAMT##add_uw *a) \ { \ REQUIRE_64BIT(ctx); \ - REQUIRE_EXT(ctx, RVB); \ + REQUIRE_ZBA(ctx); \ return gen_arith(ctx, a, EXT_NONE, gen_sh##SHAMT##add_uw); \ } @@ -635,7 +641,7 @@ static void gen_add_uw(TCGv ret, TCGv arg1, TCGv arg2) static bool trans_add_uw(DisasContext *ctx, arg_add_uw *a) { REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBA(ctx); return gen_arith(ctx, a, EXT_NONE, gen_add_uw); } @@ -647,6 +653,6 @@ static void gen_slli_uw(TCGv dest, TCGv src, target_long shamt) static bool trans_slli_uw(DisasContext *ctx, arg_slli_uw *a) { REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBA(ctx); return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_slli_uw); } From dd98a74034a4ae6a80162f2db42e67260875eb5e Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:06 +0200 Subject: [PATCH 0331/1334] target/riscv: Remove the W-form instructions from Zbs Zbs 1.0.0 (just as the 0.93 draft-B before) does not provide for W-form instructions for Zbs (single-bit instructions). Remove them. Note that these instructions had already been removed for the 0.93 version of the draft-B extention and have not been present in the binutils patches circulating in January 2021. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Acked-by: Bin Meng Message-id: 20210911140016.834071-7-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/insn32.decode | 7 ---- target/riscv/insn_trans/trans_rvb.c.inc | 56 ------------------------- 2 files changed, 63 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 86f1166dab..b499691a9e 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -717,10 +717,6 @@ cpopw 0110000 00010 ..... 001 ..... 0011011 @r2 packw 0000100 .......... 100 ..... 0111011 @r packuw 0100100 .......... 100 ..... 0111011 @r -bsetw 0010100 .......... 001 ..... 0111011 @r -bclrw 0100100 .......... 001 ..... 0111011 @r -binvw 0110100 .......... 001 ..... 0111011 @r -bextw 0100100 .......... 101 ..... 0111011 @r slow 0010000 .......... 001 ..... 0111011 @r srow 0010000 .......... 101 ..... 0111011 @r rorw 0110000 .......... 101 ..... 0111011 @r @@ -728,9 +724,6 @@ rolw 0110000 .......... 001 ..... 0111011 @r grevw 0110100 .......... 101 ..... 0111011 @r gorcw 0010100 .......... 101 ..... 0111011 @r -bsetiw 0010100 .......... 001 ..... 0011011 @sh5 -bclriw 0100100 .......... 001 ..... 0011011 @sh5 -binviw 0110100 .......... 001 ..... 0011011 @sh5 sloiw 0010000 .......... 001 ..... 0011011 @sh5 sroiw 0010000 .......... 101 ..... 0011011 @sh5 roriw 0110000 .......... 101 ..... 0011011 @sh5 diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index fd549c7b0f..fbe1c3b410 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -420,62 +420,6 @@ static bool trans_packuw(DisasContext *ctx, arg_packuw *a) return gen_arith(ctx, a, EXT_NONE, gen_packuw); } -static bool trans_bsetw(DisasContext *ctx, arg_bsetw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift(ctx, a, EXT_NONE, gen_bset); -} - -static bool trans_bsetiw(DisasContext *ctx, arg_bsetiw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_bset); -} - -static bool trans_bclrw(DisasContext *ctx, arg_bclrw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift(ctx, a, EXT_NONE, gen_bclr); -} - -static bool trans_bclriw(DisasContext *ctx, arg_bclriw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_bclr); -} - -static bool trans_binvw(DisasContext *ctx, arg_binvw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift(ctx, a, EXT_NONE, gen_binv); -} - -static bool trans_binviw(DisasContext *ctx, arg_binviw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_binv); -} - -static bool trans_bextw(DisasContext *ctx, arg_bextw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift(ctx, a, EXT_NONE, gen_bext); -} - static bool trans_slow(DisasContext *ctx, arg_slow *a) { REQUIRE_64BIT(ctx); From 628d8c88c14e6ee8eef0c6d3b7178dbfc7770f03 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:07 +0200 Subject: [PATCH 0332/1334] target/riscv: Remove shift-one instructions (proposed Zbo in pre-0.93 draft-B) The Zb[abcs] ratification package does not include the proposed shift-one instructions. There currently is no clear plan to whether these (or variants of them) will be ratified as Zbo (or a different extension) or what the timeframe for such a decision could be. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Acked-by: Bin Meng Message-id: 20210911140016.834071-8-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/insn32.decode | 8 --- target/riscv/insn_trans/trans_rvb.c.inc | 70 ------------------------- 2 files changed, 78 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index b499691a9e..e0f6e315a2 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -693,8 +693,6 @@ bset 0010100 .......... 001 ..... 0110011 @r bclr 0100100 .......... 001 ..... 0110011 @r binv 0110100 .......... 001 ..... 0110011 @r bext 0100100 .......... 101 ..... 0110011 @r -slo 0010000 .......... 001 ..... 0110011 @r -sro 0010000 .......... 101 ..... 0110011 @r ror 0110000 .......... 101 ..... 0110011 @r rol 0110000 .......... 001 ..... 0110011 @r grev 0110100 .......... 101 ..... 0110011 @r @@ -704,8 +702,6 @@ bseti 00101. ........... 001 ..... 0010011 @sh bclri 01001. ........... 001 ..... 0010011 @sh binvi 01101. ........... 001 ..... 0010011 @sh bexti 01001. ........... 101 ..... 0010011 @sh -sloi 00100. ........... 001 ..... 0010011 @sh -sroi 00100. ........... 101 ..... 0010011 @sh rori 01100. ........... 101 ..... 0010011 @sh grevi 01101. ........... 101 ..... 0010011 @sh gorci 00101. ........... 101 ..... 0010011 @sh @@ -717,15 +713,11 @@ cpopw 0110000 00010 ..... 001 ..... 0011011 @r2 packw 0000100 .......... 100 ..... 0111011 @r packuw 0100100 .......... 100 ..... 0111011 @r -slow 0010000 .......... 001 ..... 0111011 @r -srow 0010000 .......... 101 ..... 0111011 @r rorw 0110000 .......... 101 ..... 0111011 @r rolw 0110000 .......... 001 ..... 0111011 @r grevw 0110100 .......... 101 ..... 0111011 @r gorcw 0010100 .......... 101 ..... 0111011 @r -sloiw 0010000 .......... 001 ..... 0011011 @sh5 -sroiw 0010000 .......... 101 ..... 0011011 @sh5 roriw 0110000 .......... 101 ..... 0011011 @sh5 greviw 0110100 .......... 101 ..... 0011011 @sh5 gorciw 0010100 .......... 101 ..... 0011011 @sh5 diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index fbe1c3b410..a5bf40f95b 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -237,44 +237,6 @@ static bool trans_bexti(DisasContext *ctx, arg_bexti *a) return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_bext); } -static void gen_slo(TCGv ret, TCGv arg1, TCGv arg2) -{ - tcg_gen_not_tl(ret, arg1); - tcg_gen_shl_tl(ret, ret, arg2); - tcg_gen_not_tl(ret, ret); -} - -static bool trans_slo(DisasContext *ctx, arg_slo *a) -{ - REQUIRE_EXT(ctx, RVB); - return gen_shift(ctx, a, EXT_NONE, gen_slo); -} - -static bool trans_sloi(DisasContext *ctx, arg_sloi *a) -{ - REQUIRE_EXT(ctx, RVB); - return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_slo); -} - -static void gen_sro(TCGv ret, TCGv arg1, TCGv arg2) -{ - tcg_gen_not_tl(ret, arg1); - tcg_gen_shr_tl(ret, ret, arg2); - tcg_gen_not_tl(ret, ret); -} - -static bool trans_sro(DisasContext *ctx, arg_sro *a) -{ - REQUIRE_EXT(ctx, RVB); - return gen_shift(ctx, a, EXT_ZERO, gen_sro); -} - -static bool trans_sroi(DisasContext *ctx, arg_sroi *a) -{ - REQUIRE_EXT(ctx, RVB); - return gen_shift_imm_tl(ctx, a, EXT_ZERO, gen_sro); -} - static bool trans_ror(DisasContext *ctx, arg_ror *a) { REQUIRE_EXT(ctx, RVB); @@ -420,38 +382,6 @@ static bool trans_packuw(DisasContext *ctx, arg_packuw *a) return gen_arith(ctx, a, EXT_NONE, gen_packuw); } -static bool trans_slow(DisasContext *ctx, arg_slow *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift(ctx, a, EXT_NONE, gen_slo); -} - -static bool trans_sloiw(DisasContext *ctx, arg_sloiw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_slo); -} - -static bool trans_srow(DisasContext *ctx, arg_srow *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift(ctx, a, EXT_ZERO, gen_sro); -} - -static bool trans_sroiw(DisasContext *ctx, arg_sroiw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift_imm_tl(ctx, a, EXT_ZERO, gen_sro); -} - static void gen_rorw(TCGv ret, TCGv arg1, TCGv arg2) { TCGv_i32 t1 = tcg_temp_new_i32(); From f36a4a89aad493990084a9b540ed511cb66701ce Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:08 +0200 Subject: [PATCH 0333/1334] target/riscv: Reassign instructions to the Zbs-extension The following instructions are part of Zbs: - b{set,clr,ext,inv} - b{set,clr,ext,inv}i Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Acked-by: Bin Meng Message-id: 20210911140016.834071-9-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/insn32.decode | 17 +++++++++-------- target/riscv/insn_trans/trans_rvb.c.inc | 25 +++++++++++++++---------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index e0f6e315a2..35a3563ff4 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -689,19 +689,11 @@ min 0000101 .......... 100 ..... 0110011 @r minu 0000101 .......... 101 ..... 0110011 @r max 0000101 .......... 110 ..... 0110011 @r maxu 0000101 .......... 111 ..... 0110011 @r -bset 0010100 .......... 001 ..... 0110011 @r -bclr 0100100 .......... 001 ..... 0110011 @r -binv 0110100 .......... 001 ..... 0110011 @r -bext 0100100 .......... 101 ..... 0110011 @r ror 0110000 .......... 101 ..... 0110011 @r rol 0110000 .......... 001 ..... 0110011 @r grev 0110100 .......... 101 ..... 0110011 @r gorc 0010100 .......... 101 ..... 0110011 @r -bseti 00101. ........... 001 ..... 0010011 @sh -bclri 01001. ........... 001 ..... 0010011 @sh -binvi 01101. ........... 001 ..... 0010011 @sh -bexti 01001. ........... 101 ..... 0010011 @sh rori 01100. ........... 101 ..... 0010011 @sh grevi 01101. ........... 101 ..... 0010011 @sh gorci 00101. ........... 101 ..... 0010011 @sh @@ -722,3 +714,12 @@ roriw 0110000 .......... 101 ..... 0011011 @sh5 greviw 0110100 .......... 101 ..... 0011011 @sh5 gorciw 0010100 .......... 101 ..... 0011011 @sh5 +# *** RV32 Zbs Standard Extension *** +bclr 0100100 .......... 001 ..... 0110011 @r +bclri 01001. ........... 001 ..... 0010011 @sh +bext 0100100 .......... 101 ..... 0110011 @r +bexti 01001. ........... 101 ..... 0010011 @sh +binv 0110100 .......... 001 ..... 0110011 @r +binvi 01101. ........... 001 ..... 0010011 @sh +bset 0010100 .......... 001 ..... 0110011 @r +bseti 00101. ........... 001 ..... 0010011 @sh diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index a5bf40f95b..861364e3e5 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -1,5 +1,5 @@ /* - * RISC-V translation routines for the RVB draft and Zba Standard Extension. + * RISC-V translation routines for the RVB draft Zb[as] Standard Extension. * * Copyright (c) 2020 Kito Cheng, kito.cheng@sifive.com * Copyright (c) 2020 Frank Chang, frank.chang@sifive.com @@ -24,11 +24,16 @@ } \ } while (0) +#define REQUIRE_ZBS(ctx) do { \ + if (!RISCV_CPU(ctx->cs)->cfg.ext_zbs) { \ + return false; \ + } \ +} while (0) + static void gen_clz(TCGv ret, TCGv arg1) { tcg_gen_clzi_tl(ret, arg1, TARGET_LONG_BITS); } - static bool trans_clz(DisasContext *ctx, arg_clz *a) { REQUIRE_EXT(ctx, RVB); @@ -165,13 +170,13 @@ static void gen_bset(TCGv ret, TCGv arg1, TCGv shamt) static bool trans_bset(DisasContext *ctx, arg_bset *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBS(ctx); return gen_shift(ctx, a, EXT_NONE, gen_bset); } static bool trans_bseti(DisasContext *ctx, arg_bseti *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBS(ctx); return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_bset); } @@ -187,13 +192,13 @@ static void gen_bclr(TCGv ret, TCGv arg1, TCGv shamt) static bool trans_bclr(DisasContext *ctx, arg_bclr *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBS(ctx); return gen_shift(ctx, a, EXT_NONE, gen_bclr); } static bool trans_bclri(DisasContext *ctx, arg_bclri *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBS(ctx); return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_bclr); } @@ -209,13 +214,13 @@ static void gen_binv(TCGv ret, TCGv arg1, TCGv shamt) static bool trans_binv(DisasContext *ctx, arg_binv *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBS(ctx); return gen_shift(ctx, a, EXT_NONE, gen_binv); } static bool trans_binvi(DisasContext *ctx, arg_binvi *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBS(ctx); return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_binv); } @@ -227,13 +232,13 @@ static void gen_bext(TCGv ret, TCGv arg1, TCGv shamt) static bool trans_bext(DisasContext *ctx, arg_bext *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBS(ctx); return gen_shift(ctx, a, EXT_NONE, gen_bext); } static bool trans_bexti(DisasContext *ctx, arg_bexti *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBS(ctx); return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_bext); } From fd4b81a304a5d50e719019d22eacca2d8ef4de69 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:09 +0200 Subject: [PATCH 0334/1334] target/riscv: Add instructions of the Zbc-extension The following instructions are part of Zbc: - clmul - clmulh - clmulr Note that these instructions were already defined in the pre-0.93 and the 0.93 draft-B proposals, but had not been omitted in the earlier addition of draft-B to QEmu. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-id: 20210911140016.834071-10-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/bitmanip_helper.c | 27 +++++++++++++++++++++ target/riscv/helper.h | 2 ++ target/riscv/insn32.decode | 5 ++++ target/riscv/insn_trans/trans_rvb.c.inc | 32 ++++++++++++++++++++++++- 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/target/riscv/bitmanip_helper.c b/target/riscv/bitmanip_helper.c index 5b2f795d03..73be5a81c7 100644 --- a/target/riscv/bitmanip_helper.c +++ b/target/riscv/bitmanip_helper.c @@ -3,6 +3,7 @@ * * Copyright (c) 2020 Kito Cheng, kito.cheng@sifive.com * Copyright (c) 2020 Frank Chang, frank.chang@sifive.com + * Copyright (c) 2021 Philipp Tomsich, philipp.tomsich@vrull.eu * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -88,3 +89,29 @@ target_ulong HELPER(gorcw)(target_ulong rs1, target_ulong rs2) { return do_gorc(rs1, rs2, 32); } + +target_ulong HELPER(clmul)(target_ulong rs1, target_ulong rs2) +{ + target_ulong result = 0; + + for (int i = 0; i < TARGET_LONG_BITS; i++) { + if ((rs2 >> i) & 1) { + result ^= (rs1 << i); + } + } + + return result; +} + +target_ulong HELPER(clmulr)(target_ulong rs1, target_ulong rs2) +{ + target_ulong result = 0; + + for (int i = 0; i < TARGET_LONG_BITS; i++) { + if ((rs2 >> i) & 1) { + result ^= (rs1 >> (TARGET_LONG_BITS - i - 1)); + } + } + + return result; +} diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 460eee9988..8a318a2dbc 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -63,6 +63,8 @@ DEF_HELPER_FLAGS_2(grev, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(grevw, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(gorc, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(gorcw, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(clmulr, TCG_CALL_NO_RWG_SE, tl, tl, tl) /* Special functions */ DEF_HELPER_2(csrr, tl, env, int) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 35a3563ff4..1658bb4217 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -714,6 +714,11 @@ roriw 0110000 .......... 101 ..... 0011011 @sh5 greviw 0110100 .......... 101 ..... 0011011 @sh5 gorciw 0010100 .......... 101 ..... 0011011 @sh5 +# *** RV32 Zbc Standard Extension *** +clmul 0000101 .......... 001 ..... 0110011 @r +clmulh 0000101 .......... 011 ..... 0110011 @r +clmulr 0000101 .......... 010 ..... 0110011 @r + # *** RV32 Zbs Standard Extension *** bclr 0100100 .......... 001 ..... 0110011 @r bclri 01001. ........... 001 ..... 0010011 @sh diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 861364e3e5..2eb5fa3640 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -1,5 +1,5 @@ /* - * RISC-V translation routines for the RVB draft Zb[as] Standard Extension. + * RISC-V translation routines for the Zb[acs] Standard Extension. * * Copyright (c) 2020 Kito Cheng, kito.cheng@sifive.com * Copyright (c) 2020 Frank Chang, frank.chang@sifive.com @@ -24,6 +24,12 @@ } \ } while (0) +#define REQUIRE_ZBC(ctx) do { \ + if (!RISCV_CPU(ctx->cs)->cfg.ext_zbc) { \ + return false; \ + } \ +} while (0) + #define REQUIRE_ZBS(ctx) do { \ if (!RISCV_CPU(ctx->cs)->cfg.ext_zbs) { \ return false; \ @@ -535,3 +541,27 @@ static bool trans_slli_uw(DisasContext *ctx, arg_slli_uw *a) REQUIRE_ZBA(ctx); return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_slli_uw); } + +static bool trans_clmul(DisasContext *ctx, arg_clmul *a) +{ + REQUIRE_ZBC(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_helper_clmul); +} + +static void gen_clmulh(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_clmulr(dst, src1, src2); + tcg_gen_shri_tl(dst, dst, 1); +} + +static bool trans_clmulh(DisasContext *ctx, arg_clmulr *a) +{ + REQUIRE_ZBC(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_clmulh); +} + +static bool trans_clmulr(DisasContext *ctx, arg_clmulh *a) +{ + REQUIRE_ZBC(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_helper_clmulr); +} From 16c38f36f5cb42bbfe7b9cd4bb29206a3ecab760 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:10 +0200 Subject: [PATCH 0335/1334] target/riscv: Reassign instructions to the Zbb-extension This reassigns the instructions that are part of Zbb into it, with the notable exceptions of the instructions (rev8, zext.w and orc.b) that changed due to gorci, grevi and pack not being part of Zb[abcs]. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Acked-by: Bin Meng Message-id: 20210911140016.834071-11-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/insn32.decode | 40 ++++++++++--------- target/riscv/insn_trans/trans_rvb.c.inc | 51 ++++++++++++++----------- 2 files changed, 50 insertions(+), 41 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 1658bb4217..a509cfee11 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -672,45 +672,47 @@ sh2add_uw 0010000 .......... 100 ..... 0111011 @r sh3add_uw 0010000 .......... 110 ..... 0111011 @r slli_uw 00001 ............ 001 ..... 0011011 @sh -# *** RV32B Standard Extension *** +# *** RV32 Zbb Standard Extension *** +andn 0100000 .......... 111 ..... 0110011 @r clz 011000 000000 ..... 001 ..... 0010011 @r2 -ctz 011000 000001 ..... 001 ..... 0010011 @r2 cpop 011000 000010 ..... 001 ..... 0010011 @r2 +ctz 011000 000001 ..... 001 ..... 0010011 @r2 +max 0000101 .......... 110 ..... 0110011 @r +maxu 0000101 .......... 111 ..... 0110011 @r +min 0000101 .......... 100 ..... 0110011 @r +minu 0000101 .......... 101 ..... 0110011 @r +orn 0100000 .......... 110 ..... 0110011 @r +rol 0110000 .......... 001 ..... 0110011 @r +ror 0110000 .......... 101 ..... 0110011 @r +rori 01100 ............ 101 ..... 0010011 @sh sext_b 011000 000100 ..... 001 ..... 0010011 @r2 sext_h 011000 000101 ..... 001 ..... 0010011 @r2 - -andn 0100000 .......... 111 ..... 0110011 @r -orn 0100000 .......... 110 ..... 0110011 @r xnor 0100000 .......... 100 ..... 0110011 @r + +# *** RV64 Zbb Standard Extension (in addition to RV32 Zbb) *** +clzw 0110000 00000 ..... 001 ..... 0011011 @r2 +ctzw 0110000 00001 ..... 001 ..... 0011011 @r2 +cpopw 0110000 00010 ..... 001 ..... 0011011 @r2 +rolw 0110000 .......... 001 ..... 0111011 @r +roriw 0110000 .......... 101 ..... 0011011 @sh5 +rorw 0110000 .......... 101 ..... 0111011 @r + +# *** RV32B Standard Extension *** pack 0000100 .......... 100 ..... 0110011 @r packu 0100100 .......... 100 ..... 0110011 @r packh 0000100 .......... 111 ..... 0110011 @r -min 0000101 .......... 100 ..... 0110011 @r -minu 0000101 .......... 101 ..... 0110011 @r -max 0000101 .......... 110 ..... 0110011 @r -maxu 0000101 .......... 111 ..... 0110011 @r -ror 0110000 .......... 101 ..... 0110011 @r -rol 0110000 .......... 001 ..... 0110011 @r grev 0110100 .......... 101 ..... 0110011 @r gorc 0010100 .......... 101 ..... 0110011 @r -rori 01100. ........... 101 ..... 0010011 @sh grevi 01101. ........... 101 ..... 0010011 @sh gorci 00101. ........... 101 ..... 0010011 @sh # *** RV64B Standard Extension (in addition to RV32B) *** -clzw 0110000 00000 ..... 001 ..... 0011011 @r2 -ctzw 0110000 00001 ..... 001 ..... 0011011 @r2 -cpopw 0110000 00010 ..... 001 ..... 0011011 @r2 - packw 0000100 .......... 100 ..... 0111011 @r packuw 0100100 .......... 100 ..... 0111011 @r -rorw 0110000 .......... 101 ..... 0111011 @r -rolw 0110000 .......... 001 ..... 0111011 @r grevw 0110100 .......... 101 ..... 0111011 @r gorcw 0010100 .......... 101 ..... 0111011 @r -roriw 0110000 .......... 101 ..... 0011011 @sh5 greviw 0110100 .......... 101 ..... 0011011 @sh5 gorciw 0010100 .......... 101 ..... 0011011 @sh5 diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 2eb5fa3640..bdfb495f24 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -1,5 +1,5 @@ /* - * RISC-V translation routines for the Zb[acs] Standard Extension. + * RISC-V translation routines for the Zb[abcs] Standard Extension. * * Copyright (c) 2020 Kito Cheng, kito.cheng@sifive.com * Copyright (c) 2020 Frank Chang, frank.chang@sifive.com @@ -24,6 +24,12 @@ } \ } while (0) +#define REQUIRE_ZBB(ctx) do { \ + if (!RISCV_CPU(ctx->cs)->cfg.ext_zbb) { \ + return false; \ + } \ +} while (0) + #define REQUIRE_ZBC(ctx) do { \ if (!RISCV_CPU(ctx->cs)->cfg.ext_zbc) { \ return false; \ @@ -40,9 +46,10 @@ static void gen_clz(TCGv ret, TCGv arg1) { tcg_gen_clzi_tl(ret, arg1, TARGET_LONG_BITS); } + static bool trans_clz(DisasContext *ctx, arg_clz *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_unary(ctx, a, EXT_ZERO, gen_clz); } @@ -53,31 +60,31 @@ static void gen_ctz(TCGv ret, TCGv arg1) static bool trans_ctz(DisasContext *ctx, arg_ctz *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_unary(ctx, a, EXT_ZERO, gen_ctz); } static bool trans_cpop(DisasContext *ctx, arg_cpop *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_unary(ctx, a, EXT_ZERO, tcg_gen_ctpop_tl); } static bool trans_andn(DisasContext *ctx, arg_andn *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_arith(ctx, a, EXT_NONE, tcg_gen_andc_tl); } static bool trans_orn(DisasContext *ctx, arg_orn *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_arith(ctx, a, EXT_NONE, tcg_gen_orc_tl); } static bool trans_xnor(DisasContext *ctx, arg_xnor *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_arith(ctx, a, EXT_NONE, tcg_gen_eqv_tl); } @@ -124,37 +131,37 @@ static bool trans_packh(DisasContext *ctx, arg_packh *a) static bool trans_min(DisasContext *ctx, arg_min *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_arith(ctx, a, EXT_SIGN, tcg_gen_smin_tl); } static bool trans_max(DisasContext *ctx, arg_max *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_arith(ctx, a, EXT_SIGN, tcg_gen_smax_tl); } static bool trans_minu(DisasContext *ctx, arg_minu *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_arith(ctx, a, EXT_SIGN, tcg_gen_umin_tl); } static bool trans_maxu(DisasContext *ctx, arg_maxu *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_arith(ctx, a, EXT_SIGN, tcg_gen_umax_tl); } static bool trans_sext_b(DisasContext *ctx, arg_sext_b *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext8s_tl); } static bool trans_sext_h(DisasContext *ctx, arg_sext_h *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext16s_tl); } @@ -250,19 +257,19 @@ static bool trans_bexti(DisasContext *ctx, arg_bexti *a) static bool trans_ror(DisasContext *ctx, arg_ror *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_shift(ctx, a, EXT_NONE, tcg_gen_rotr_tl); } static bool trans_rori(DisasContext *ctx, arg_rori *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_shift_imm_fn(ctx, a, EXT_NONE, tcg_gen_rotri_tl); } static bool trans_rol(DisasContext *ctx, arg_rol *a) { - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_shift(ctx, a, EXT_NONE, tcg_gen_rotl_tl); } @@ -337,7 +344,7 @@ static void gen_clzw(TCGv ret, TCGv arg1) static bool trans_clzw(DisasContext *ctx, arg_clzw *a) { REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_unary(ctx, a, EXT_NONE, gen_clzw); } @@ -350,14 +357,14 @@ static void gen_ctzw(TCGv ret, TCGv arg1) static bool trans_ctzw(DisasContext *ctx, arg_ctzw *a) { REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); return gen_unary(ctx, a, EXT_NONE, gen_ctzw); } static bool trans_cpopw(DisasContext *ctx, arg_cpopw *a) { REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); ctx->w = true; return gen_unary(ctx, a, EXT_ZERO, tcg_gen_ctpop_tl); } @@ -414,7 +421,7 @@ static void gen_rorw(TCGv ret, TCGv arg1, TCGv arg2) static bool trans_rorw(DisasContext *ctx, arg_rorw *a) { REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); ctx->w = true; return gen_shift(ctx, a, EXT_NONE, gen_rorw); } @@ -422,7 +429,7 @@ static bool trans_rorw(DisasContext *ctx, arg_rorw *a) static bool trans_roriw(DisasContext *ctx, arg_roriw *a) { REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); ctx->w = true; return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_rorw); } @@ -448,7 +455,7 @@ static void gen_rolw(TCGv ret, TCGv arg1, TCGv arg2) static bool trans_rolw(DisasContext *ctx, arg_rolw *a) { REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); + REQUIRE_ZBB(ctx); ctx->w = true; return gen_shift(ctx, a, EXT_NONE, gen_rolw); } From d7a4fcb03433edefd19b7db3c4d20ed750b5833b Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:11 +0200 Subject: [PATCH 0336/1334] target/riscv: Add orc.b instruction for Zbb, removing gorc/gorci The 1.0.0 version of Zbb does not contain gorc/gorci. Instead, a orc.b instruction (equivalent to the orc.b pseudo-instruction built on gorci from pre-0.93 draft-B) is available, mainly targeting string-processing workloads. This commit adds the new orc.b instruction and removed gorc/gorci. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-id: 20210911140016.834071-12-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/bitmanip_helper.c | 26 ----------------- target/riscv/helper.h | 2 -- target/riscv/insn32.decode | 6 +--- target/riscv/insn_trans/trans_rvb.c.inc | 39 +++++++++++-------------- 4 files changed, 18 insertions(+), 55 deletions(-) diff --git a/target/riscv/bitmanip_helper.c b/target/riscv/bitmanip_helper.c index 73be5a81c7..bb48388fcd 100644 --- a/target/riscv/bitmanip_helper.c +++ b/target/riscv/bitmanip_helper.c @@ -64,32 +64,6 @@ target_ulong HELPER(grevw)(target_ulong rs1, target_ulong rs2) return do_grev(rs1, rs2, 32); } -static target_ulong do_gorc(target_ulong rs1, - target_ulong rs2, - int bits) -{ - target_ulong x = rs1; - int i, shift; - - for (i = 0, shift = 1; shift < bits; i++, shift <<= 1) { - if (rs2 & shift) { - x |= do_swap(x, adjacent_masks[i], shift); - } - } - - return x; -} - -target_ulong HELPER(gorc)(target_ulong rs1, target_ulong rs2) -{ - return do_gorc(rs1, rs2, TARGET_LONG_BITS); -} - -target_ulong HELPER(gorcw)(target_ulong rs1, target_ulong rs2) -{ - return do_gorc(rs1, rs2, 32); -} - target_ulong HELPER(clmul)(target_ulong rs1, target_ulong rs2) { target_ulong result = 0; diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 8a318a2dbc..a9bda2c8ac 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -61,8 +61,6 @@ DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64) /* Bitmanip */ DEF_HELPER_FLAGS_2(grev, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(grevw, TCG_CALL_NO_RWG_SE, tl, tl, tl) -DEF_HELPER_FLAGS_2(gorc, TCG_CALL_NO_RWG_SE, tl, tl, tl) -DEF_HELPER_FLAGS_2(gorcw, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(clmulr, TCG_CALL_NO_RWG_SE, tl, tl, tl) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index a509cfee11..59202196dc 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -681,6 +681,7 @@ max 0000101 .......... 110 ..... 0110011 @r maxu 0000101 .......... 111 ..... 0110011 @r min 0000101 .......... 100 ..... 0110011 @r minu 0000101 .......... 101 ..... 0110011 @r +orc_b 001010 000111 ..... 101 ..... 0010011 @r2 orn 0100000 .......... 110 ..... 0110011 @r rol 0110000 .......... 001 ..... 0110011 @r ror 0110000 .......... 101 ..... 0110011 @r @@ -702,19 +703,14 @@ pack 0000100 .......... 100 ..... 0110011 @r packu 0100100 .......... 100 ..... 0110011 @r packh 0000100 .......... 111 ..... 0110011 @r grev 0110100 .......... 101 ..... 0110011 @r -gorc 0010100 .......... 101 ..... 0110011 @r - grevi 01101. ........... 101 ..... 0010011 @sh -gorci 00101. ........... 101 ..... 0010011 @sh # *** RV64B Standard Extension (in addition to RV32B) *** packw 0000100 .......... 100 ..... 0111011 @r packuw 0100100 .......... 100 ..... 0111011 @r grevw 0110100 .......... 101 ..... 0111011 @r -gorcw 0010100 .......... 101 ..... 0111011 @r greviw 0110100 .......... 101 ..... 0011011 @sh5 -gorciw 0010100 .......... 101 ..... 0011011 @sh5 # *** RV32 Zbc Standard Extension *** clmul 0000101 .......... 001 ..... 0110011 @r diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index bdfb495f24..d32af5915a 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -295,16 +295,27 @@ static bool trans_grevi(DisasContext *ctx, arg_grevi *a) return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_grevi); } -static bool trans_gorc(DisasContext *ctx, arg_gorc *a) +static void gen_orc_b(TCGv ret, TCGv source1) { - REQUIRE_EXT(ctx, RVB); - return gen_shift(ctx, a, EXT_ZERO, gen_helper_gorc); + TCGv tmp = tcg_temp_new(); + TCGv ones = tcg_constant_tl(dup_const_tl(MO_8, 0x01)); + + /* Set lsb in each byte if the byte was zero. */ + tcg_gen_sub_tl(tmp, source1, ones); + tcg_gen_andc_tl(tmp, tmp, source1); + tcg_gen_shri_tl(tmp, tmp, 7); + tcg_gen_andc_tl(tmp, ones, tmp); + + /* Replicate the lsb of each byte across the byte. */ + tcg_gen_muli_tl(ret, tmp, 0xff); + + tcg_temp_free(tmp); } -static bool trans_gorci(DisasContext *ctx, arg_gorci *a) +static bool trans_orc_b(DisasContext *ctx, arg_orc_b *a) { - REQUIRE_EXT(ctx, RVB); - return gen_shift_imm_tl(ctx, a, EXT_ZERO, gen_helper_gorc); + REQUIRE_ZBB(ctx); + return gen_unary(ctx, a, EXT_ZERO, gen_orc_b); } #define GEN_SHADD(SHAMT) \ @@ -476,22 +487,6 @@ static bool trans_greviw(DisasContext *ctx, arg_greviw *a) return gen_shift_imm_tl(ctx, a, EXT_ZERO, gen_helper_grev); } -static bool trans_gorcw(DisasContext *ctx, arg_gorcw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift(ctx, a, EXT_ZERO, gen_helper_gorc); -} - -static bool trans_gorciw(DisasContext *ctx, arg_gorciw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift_imm_tl(ctx, a, EXT_ZERO, gen_helper_gorc); -} - #define GEN_SHADD_UW(SHAMT) \ static void gen_sh##SHAMT##add_uw(TCGv ret, TCGv arg1, TCGv arg2) \ { \ From 7e68e6c79b9de5c923e478ea6794a5143610b765 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:12 +0200 Subject: [PATCH 0337/1334] target/riscv: Add a REQUIRE_32BIT macro With the changes to Zb[abcs], there's some encodings that are different in RV64 and RV32 (e.g., for rev8 and zext.h). For these, we'll need a helper macro allowing us to select on RV32, as well. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 20210911140016.834071-13-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/translate.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 74b33fa3c9..b2d3444bc5 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -337,6 +337,12 @@ EX_SH(12) } \ } while (0) +#define REQUIRE_32BIT(ctx) do { \ + if (!is_32bit(ctx)) { \ + return false; \ + } \ +} while (0) + #define REQUIRE_64BIT(ctx) do { \ if (is_32bit(ctx)) { \ return false; \ From a1095bdcb050f0a17afb3fcb8a36543fb58f4ea9 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:13 +0200 Subject: [PATCH 0338/1334] target/riscv: Add rev8 instruction, removing grev/grevi The 1.0.0 version of Zbb does not contain grev/grevi. Instead, a rev8 instruction (equivalent to the rev8 pseudo-instruction built on grevi from pre-0.93 draft-B) is available. This commit adds the new rev8 instruction and removes grev/grevi. Note that there is no W-form of this instruction (both a sign-extending and zero-extending 32-bit version can easily be synthesized by following rev8 with either a srai or srli instruction on RV64) and that the opcode encodings for rev8 in RV32 and RV64 are different. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-id: 20210911140016.834071-14-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/bitmanip_helper.c | 40 ------------------------- target/riscv/helper.h | 2 -- target/riscv/insn32.decode | 12 ++++---- target/riscv/insn_trans/trans_rvb.c.inc | 40 +++++-------------------- 4 files changed, 15 insertions(+), 79 deletions(-) diff --git a/target/riscv/bitmanip_helper.c b/target/riscv/bitmanip_helper.c index bb48388fcd..f1b5e5549f 100644 --- a/target/riscv/bitmanip_helper.c +++ b/target/riscv/bitmanip_helper.c @@ -24,46 +24,6 @@ #include "exec/helper-proto.h" #include "tcg/tcg.h" -static const uint64_t adjacent_masks[] = { - dup_const(MO_8, 0x55), - dup_const(MO_8, 0x33), - dup_const(MO_8, 0x0f), - dup_const(MO_16, 0xff), - dup_const(MO_32, 0xffff), - UINT32_MAX -}; - -static inline target_ulong do_swap(target_ulong x, uint64_t mask, int shift) -{ - return ((x & mask) << shift) | ((x & ~mask) >> shift); -} - -static target_ulong do_grev(target_ulong rs1, - target_ulong rs2, - int bits) -{ - target_ulong x = rs1; - int i, shift; - - for (i = 0, shift = 1; shift < bits; i++, shift <<= 1) { - if (rs2 & shift) { - x = do_swap(x, adjacent_masks[i], shift); - } - } - - return x; -} - -target_ulong HELPER(grev)(target_ulong rs1, target_ulong rs2) -{ - return do_grev(rs1, rs2, TARGET_LONG_BITS); -} - -target_ulong HELPER(grevw)(target_ulong rs1, target_ulong rs2) -{ - return do_grev(rs1, rs2, 32); -} - target_ulong HELPER(clmul)(target_ulong rs1, target_ulong rs2) { target_ulong result = 0; diff --git a/target/riscv/helper.h b/target/riscv/helper.h index a9bda2c8ac..c7a5376227 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -59,8 +59,6 @@ DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64) /* Bitmanip */ -DEF_HELPER_FLAGS_2(grev, TCG_CALL_NO_RWG_SE, tl, tl, tl) -DEF_HELPER_FLAGS_2(grevw, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(clmulr, TCG_CALL_NO_RWG_SE, tl, tl, tl) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 59202196dc..901a66c0f5 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -683,6 +683,9 @@ min 0000101 .......... 100 ..... 0110011 @r minu 0000101 .......... 101 ..... 0110011 @r orc_b 001010 000111 ..... 101 ..... 0010011 @r2 orn 0100000 .......... 110 ..... 0110011 @r +# The encoding for rev8 differs between RV32 and RV64. +# rev8_32 denotes the RV32 variant. +rev8_32 011010 011000 ..... 101 ..... 0010011 @r2 rol 0110000 .......... 001 ..... 0110011 @r ror 0110000 .......... 101 ..... 0110011 @r rori 01100 ............ 101 ..... 0010011 @sh @@ -694,6 +697,10 @@ xnor 0100000 .......... 100 ..... 0110011 @r clzw 0110000 00000 ..... 001 ..... 0011011 @r2 ctzw 0110000 00001 ..... 001 ..... 0011011 @r2 cpopw 0110000 00010 ..... 001 ..... 0011011 @r2 +# The encoding for rev8 differs between RV32 and RV64. +# When executing on RV64, the encoding used in RV32 is an illegal +# instruction, so we use different handler functions to differentiate. +rev8_64 011010 111000 ..... 101 ..... 0010011 @r2 rolw 0110000 .......... 001 ..... 0111011 @r roriw 0110000 .......... 101 ..... 0011011 @sh5 rorw 0110000 .......... 101 ..... 0111011 @r @@ -702,15 +709,10 @@ rorw 0110000 .......... 101 ..... 0111011 @r pack 0000100 .......... 100 ..... 0110011 @r packu 0100100 .......... 100 ..... 0110011 @r packh 0000100 .......... 111 ..... 0110011 @r -grev 0110100 .......... 101 ..... 0110011 @r -grevi 01101. ........... 101 ..... 0010011 @sh # *** RV64B Standard Extension (in addition to RV32B) *** packw 0000100 .......... 100 ..... 0111011 @r packuw 0100100 .......... 100 ..... 0111011 @r -grevw 0110100 .......... 101 ..... 0111011 @r - -greviw 0110100 .......... 101 ..... 0011011 @sh5 # *** RV32 Zbc Standard Extension *** clmul 0000101 .......... 001 ..... 0110011 @r diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index d32af5915a..48a7c9ca5e 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -273,26 +273,18 @@ static bool trans_rol(DisasContext *ctx, arg_rol *a) return gen_shift(ctx, a, EXT_NONE, tcg_gen_rotl_tl); } -static bool trans_grev(DisasContext *ctx, arg_grev *a) +static bool trans_rev8_32(DisasContext *ctx, arg_rev8_32 *a) { - REQUIRE_EXT(ctx, RVB); - return gen_shift(ctx, a, EXT_NONE, gen_helper_grev); + REQUIRE_32BIT(ctx); + REQUIRE_ZBB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_bswap_tl); } -static void gen_grevi(TCGv dest, TCGv src, target_long shamt) +static bool trans_rev8_64(DisasContext *ctx, arg_rev8_64 *a) { - if (shamt == TARGET_LONG_BITS - 8) { - /* rev8, byte swaps */ - tcg_gen_bswap_tl(dest, src); - } else { - gen_helper_grev(dest, src, tcg_constant_tl(shamt)); - } -} - -static bool trans_grevi(DisasContext *ctx, arg_grevi *a) -{ - REQUIRE_EXT(ctx, RVB); - return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_grevi); + REQUIRE_64BIT(ctx); + REQUIRE_ZBB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_bswap_tl); } static void gen_orc_b(TCGv ret, TCGv source1) @@ -471,22 +463,6 @@ static bool trans_rolw(DisasContext *ctx, arg_rolw *a) return gen_shift(ctx, a, EXT_NONE, gen_rolw); } -static bool trans_grevw(DisasContext *ctx, arg_grevw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift(ctx, a, EXT_ZERO, gen_helper_grev); -} - -static bool trans_greviw(DisasContext *ctx, arg_greviw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - ctx->w = true; - return gen_shift_imm_tl(ctx, a, EXT_ZERO, gen_helper_grev); -} - #define GEN_SHADD_UW(SHAMT) \ static void gen_sh##SHAMT##add_uw(TCGv ret, TCGv arg1, TCGv arg2) \ { \ From 06dfa8a5c5e79c2be7672b0a56e08c7f6d350148 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:14 +0200 Subject: [PATCH 0339/1334] target/riscv: Add zext.h instructions to Zbb, removing pack/packu/packh The 1.0.0 version of Zbb does not contain pack/packu/packh. However, a zext.h instruction is provided (built on pack/packh from pre-0.93 draft-B) is available. This commit adds zext.h and removes the pack* instructions. Note that the encodings for zext.h are different between RV32 and RV64, which is handled through REQUIRE_32BIT. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-id: 20210911140016.834071-15-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/insn32.decode | 12 ++-- target/riscv/insn_trans/trans_rvb.c.inc | 86 ++++--------------------- 2 files changed, 21 insertions(+), 77 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 901a66c0f5..affb99b3e6 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -692,6 +692,9 @@ rori 01100 ............ 101 ..... 0010011 @sh sext_b 011000 000100 ..... 001 ..... 0010011 @r2 sext_h 011000 000101 ..... 001 ..... 0010011 @r2 xnor 0100000 .......... 100 ..... 0110011 @r +# The encoding for zext.h differs between RV32 and RV64. +# zext_h_32 denotes the RV32 variant. +zext_h_32 0000100 00000 ..... 100 ..... 0110011 @r2 # *** RV64 Zbb Standard Extension (in addition to RV32 Zbb) *** clzw 0110000 00000 ..... 001 ..... 0011011 @r2 @@ -704,15 +707,14 @@ rev8_64 011010 111000 ..... 101 ..... 0010011 @r2 rolw 0110000 .......... 001 ..... 0111011 @r roriw 0110000 .......... 101 ..... 0011011 @sh5 rorw 0110000 .......... 101 ..... 0111011 @r +# The encoding for zext.h differs between RV32 and RV64. +# When executing on RV64, the encoding used in RV32 is an illegal +# instruction, so we use different handler functions to differentiate. +zext_h_64 0000100 00000 ..... 100 ..... 0111011 @r2 # *** RV32B Standard Extension *** -pack 0000100 .......... 100 ..... 0110011 @r -packu 0100100 .......... 100 ..... 0110011 @r -packh 0000100 .......... 111 ..... 0110011 @r # *** RV64B Standard Extension (in addition to RV32B) *** -packw 0000100 .......... 100 ..... 0111011 @r -packuw 0100100 .......... 100 ..... 0111011 @r # *** RV32 Zbc Standard Extension *** clmul 0000101 .......... 001 ..... 0110011 @r diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 48a7c9ca5e..185c3e9a60 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -88,47 +88,6 @@ static bool trans_xnor(DisasContext *ctx, arg_xnor *a) return gen_arith(ctx, a, EXT_NONE, tcg_gen_eqv_tl); } -static void gen_pack(TCGv ret, TCGv arg1, TCGv arg2) -{ - tcg_gen_deposit_tl(ret, arg1, arg2, - TARGET_LONG_BITS / 2, - TARGET_LONG_BITS / 2); -} - -static bool trans_pack(DisasContext *ctx, arg_pack *a) -{ - REQUIRE_EXT(ctx, RVB); - return gen_arith(ctx, a, EXT_NONE, gen_pack); -} - -static void gen_packu(TCGv ret, TCGv arg1, TCGv arg2) -{ - TCGv t = tcg_temp_new(); - tcg_gen_shri_tl(t, arg1, TARGET_LONG_BITS / 2); - tcg_gen_deposit_tl(ret, arg2, t, 0, TARGET_LONG_BITS / 2); - tcg_temp_free(t); -} - -static bool trans_packu(DisasContext *ctx, arg_packu *a) -{ - REQUIRE_EXT(ctx, RVB); - return gen_arith(ctx, a, EXT_NONE, gen_packu); -} - -static void gen_packh(TCGv ret, TCGv arg1, TCGv arg2) -{ - TCGv t = tcg_temp_new(); - tcg_gen_ext8u_tl(t, arg2); - tcg_gen_deposit_tl(ret, arg1, t, 8, TARGET_LONG_BITS - 8); - tcg_temp_free(t); -} - -static bool trans_packh(DisasContext *ctx, arg_packh *a) -{ - REQUIRE_EXT(ctx, RVB); - return gen_arith(ctx, a, EXT_NONE, gen_packh); -} - static bool trans_min(DisasContext *ctx, arg_min *a) { REQUIRE_ZBB(ctx); @@ -336,6 +295,20 @@ GEN_TRANS_SHADD(1) GEN_TRANS_SHADD(2) GEN_TRANS_SHADD(3) +static bool trans_zext_h_32(DisasContext *ctx, arg_zext_h_32 *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZBB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext16u_tl); +} + +static bool trans_zext_h_64(DisasContext *ctx, arg_zext_h_64 *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZBB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext16u_tl); +} + static void gen_clzw(TCGv ret, TCGv arg1) { TCGv t = tcg_temp_new(); @@ -372,37 +345,6 @@ static bool trans_cpopw(DisasContext *ctx, arg_cpopw *a) return gen_unary(ctx, a, EXT_ZERO, tcg_gen_ctpop_tl); } -static void gen_packw(TCGv ret, TCGv arg1, TCGv arg2) -{ - TCGv t = tcg_temp_new(); - tcg_gen_ext16s_tl(t, arg2); - tcg_gen_deposit_tl(ret, arg1, t, 16, 48); - tcg_temp_free(t); -} - -static bool trans_packw(DisasContext *ctx, arg_packw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - return gen_arith(ctx, a, EXT_NONE, gen_packw); -} - -static void gen_packuw(TCGv ret, TCGv arg1, TCGv arg2) -{ - TCGv t = tcg_temp_new(); - tcg_gen_shri_tl(t, arg1, 16); - tcg_gen_deposit_tl(ret, arg2, t, 0, 16); - tcg_gen_ext32s_tl(ret, ret); - tcg_temp_free(t); -} - -static bool trans_packuw(DisasContext *ctx, arg_packuw *a) -{ - REQUIRE_64BIT(ctx); - REQUIRE_EXT(ctx, RVB); - return gen_arith(ctx, a, EXT_NONE, gen_packuw); -} - static void gen_rorw(TCGv ret, TCGv arg1, TCGv arg2) { TCGv_i32 t1 = tcg_temp_new_i32(); From 9916ea3c97f6cbd46d912ecc80dfe94baed5d288 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:15 +0200 Subject: [PATCH 0340/1334] target/riscv: Remove RVB (replaced by Zb[abcs]) With everything classified as Zb[abcs] and pre-0.93 draft-B instructions that are not part of Zb[abcs] removed, we can remove the remaining support code for RVB. Note that RVB has been retired for good and misa.B will neither mean 'some' or 'all of' Zb*: https://lists.riscv.org/g/tech-bitmanip/message/532 Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 20210911140016.834071-16-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 26 -------------------------- target/riscv/cpu.h | 3 --- target/riscv/insn32.decode | 4 ---- 3 files changed, 33 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 785a3a8d19..1d69d1887e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -127,11 +127,6 @@ static void set_priv_version(CPURISCVState *env, int priv_ver) env->priv_ver = priv_ver; } -static void set_bext_version(CPURISCVState *env, int bext_ver) -{ - env->bext_ver = bext_ver; -} - static void set_vext_version(CPURISCVState *env, int vext_ver) { env->vext_ver = vext_ver; @@ -496,25 +491,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) if (cpu->cfg.ext_h) { target_misa |= RVH; } - if (cpu->cfg.ext_b) { - int bext_version = BEXT_VERSION_0_93_0; - target_misa |= RVB; - - if (cpu->cfg.bext_spec) { - if (!g_strcmp0(cpu->cfg.bext_spec, "v0.93")) { - bext_version = BEXT_VERSION_0_93_0; - } else { - error_setg(errp, - "Unsupported bitmanip spec version '%s'", - cpu->cfg.bext_spec); - return; - } - } else { - qemu_log("bitmanip version is not specified, " - "use the default value v0.93\n"); - } - set_bext_version(env, bext_version); - } if (cpu->cfg.ext_v) { int vext_version = VEXT_VERSION_0_07_1; target_misa |= RVV; @@ -616,7 +592,6 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true), DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), /* This is experimental so mark with 'x-' */ - DEFINE_PROP_BOOL("x-b", RISCVCPU, cfg.ext_b, false), DEFINE_PROP_BOOL("x-zba", RISCVCPU, cfg.ext_zba, false), DEFINE_PROP_BOOL("x-zbb", RISCVCPU, cfg.ext_zbb, false), DEFINE_PROP_BOOL("x-zbc", RISCVCPU, cfg.ext_zbc, false), @@ -627,7 +602,6 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), - DEFINE_PROP_STRING("bext_spec", RISCVCPU, cfg.bext_spec), DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec), DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 1a38723f2c..bd519c9090 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -67,7 +67,6 @@ #define RVS RV('S') #define RVU RV('U') #define RVH RV('H') -#define RVB RV('B') /* S extension denotes that Supervisor mode exists, however it is possible to have a core that support S mode but does not have an MMU and there @@ -83,7 +82,6 @@ enum { #define PRIV_VERSION_1_10_0 0x00011000 #define PRIV_VERSION_1_11_0 0x00011100 -#define BEXT_VERSION_0_93_0 0x00009300 #define VEXT_VERSION_0_07_1 0x00000701 enum { @@ -288,7 +286,6 @@ struct RISCVCPU { bool ext_f; bool ext_d; bool ext_c; - bool ext_b; bool ext_s; bool ext_u; bool ext_h; diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index affb99b3e6..2f251dac1b 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -712,10 +712,6 @@ rorw 0110000 .......... 101 ..... 0111011 @r # instruction, so we use different handler functions to differentiate. zext_h_64 0000100 00000 ..... 100 ..... 0111011 @r2 -# *** RV32B Standard Extension *** - -# *** RV64B Standard Extension (in addition to RV32B) *** - # *** RV32 Zbc Standard Extension *** clmul 0000101 .......... 001 ..... 0110011 @r clmulh 0000101 .......... 011 ..... 0110011 @r From 02c1b569a15b4b06a3c69b6cb1713830a29cb01f Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Sat, 11 Sep 2021 16:00:16 +0200 Subject: [PATCH 0341/1334] disas/riscv: Add Zb[abcs] instructions With the addition of Zb[abcs], we also need to add disassembler support for these new instructions. Signed-off-by: Philipp Tomsich Acked-by: Alistair Francis Message-id: 20210911140016.834071-17-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- disas/riscv.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 3 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 278d9be924..793ad14c27 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -478,6 +478,49 @@ typedef enum { rv_op_fsflags = 316, rv_op_fsrmi = 317, rv_op_fsflagsi = 318, + rv_op_bseti = 319, + rv_op_bclri = 320, + rv_op_binvi = 321, + rv_op_bexti = 322, + rv_op_rori = 323, + rv_op_clz = 324, + rv_op_ctz = 325, + rv_op_cpop = 326, + rv_op_sext_h = 327, + rv_op_sext_b = 328, + rv_op_xnor = 329, + rv_op_orn = 330, + rv_op_andn = 331, + rv_op_rol = 332, + rv_op_ror = 333, + rv_op_sh1add = 334, + rv_op_sh2add = 335, + rv_op_sh3add = 336, + rv_op_sh1add_uw = 337, + rv_op_sh2add_uw = 338, + rv_op_sh3add_uw = 339, + rv_op_clmul = 340, + rv_op_clmulr = 341, + rv_op_clmulh = 342, + rv_op_min = 343, + rv_op_minu = 344, + rv_op_max = 345, + rv_op_maxu = 346, + rv_op_clzw = 347, + rv_op_ctzw = 348, + rv_op_cpopw = 349, + rv_op_slli_uw = 350, + rv_op_add_uw = 351, + rv_op_rolw = 352, + rv_op_rorw = 353, + rv_op_rev8 = 354, + rv_op_zext_h = 355, + rv_op_roriw = 356, + rv_op_orc_b = 357, + rv_op_bset = 358, + rv_op_bclr = 359, + rv_op_binv = 360, + rv_op_bext = 361, } rv_op; /* structures */ @@ -1117,6 +1160,49 @@ const rv_opcode_data opcode_data[] = { { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, + { "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "clz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "ctz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "cpop", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "sext.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "sext.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "xnor", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "orn", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "andn", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "rol", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "ror", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "min", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "minu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "max", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "clzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "clzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "cpopw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "slli.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "rev8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "zext.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "orc.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "bset", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "binv", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "bext", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -1507,7 +1593,20 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: op = rv_op_addi; break; case 1: switch (((inst >> 27) & 0b11111)) { - case 0: op = rv_op_slli; break; + case 0b00000: op = rv_op_slli; break; + case 0b00101: op = rv_op_bseti; break; + case 0b01001: op = rv_op_bclri; break; + case 0b01101: op = rv_op_binvi; break; + case 0b01100: + switch (((inst >> 20) & 0b1111111)) { + case 0b0000000: op = rv_op_clz; break; + case 0b0000001: op = rv_op_ctz; break; + case 0b0000010: op = rv_op_cpop; break; + /* 0b0000011 */ + case 0b0000100: op = rv_op_sext_b; break; + case 0b0000101: op = rv_op_sext_h; break; + } + break; } break; case 2: op = rv_op_slti; break; @@ -1515,8 +1614,16 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 4: op = rv_op_xori; break; case 5: switch (((inst >> 27) & 0b11111)) { - case 0: op = rv_op_srli; break; - case 8: op = rv_op_srai; break; + case 0b00000: op = rv_op_srli; break; + case 0b00101: op = rv_op_orc_b; break; + case 0b01000: op = rv_op_srai; break; + case 0b01001: op = rv_op_bexti; break; + case 0b01100: op = rv_op_rori; break; + case 0b01101: + switch ((inst >> 20) & 0b1111111) { + case 0b0111000: op = rv_op_rev8; break; + } + break; } break; case 6: op = rv_op_ori; break; @@ -1530,12 +1637,21 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: switch (((inst >> 25) & 0b1111111)) { case 0: op = rv_op_slliw; break; + case 4: op = rv_op_slli_uw; break; + case 48: + switch ((inst >> 20) & 0b11111) { + case 0b00000: op = rv_op_clzw; break; + case 0b00001: op = rv_op_ctzw; break; + case 0b00010: op = rv_op_cpopw; break; + } + break; } break; case 5: switch (((inst >> 25) & 0b1111111)) { case 0: op = rv_op_srliw; break; case 32: op = rv_op_sraiw; break; + case 48: op = rv_op_roriw; break; } break; } @@ -1623,8 +1739,32 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 13: op = rv_op_divu; break; case 14: op = rv_op_rem; break; case 15: op = rv_op_remu; break; + case 36: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_zext_h; break; + } + break; + case 41: op = rv_op_clmul; break; + case 42: op = rv_op_clmulr; break; + case 43: op = rv_op_clmulh; break; + case 44: op = rv_op_min; break; + case 45: op = rv_op_minu; break; + case 46: op = rv_op_max; break; + case 47: op = rv_op_maxu; break; + case 130: op = rv_op_sh1add; break; + case 132: op = rv_op_sh2add; break; + case 134: op = rv_op_sh3add; break; + case 161: op = rv_op_bset; break; case 256: op = rv_op_sub; break; + case 260: op = rv_op_xnor; break; case 261: op = rv_op_sra; break; + case 262: op = rv_op_orn; break; + case 263: op = rv_op_andn; break; + case 289: op = rv_op_bclr; break; + case 293: op = rv_op_bext; break; + case 385: op = rv_op_rol; break; + case 386: op = rv_op_ror; break; + case 417: op = rv_op_binv; break; } break; case 13: op = rv_op_lui; break; @@ -1638,8 +1778,19 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 13: op = rv_op_divuw; break; case 14: op = rv_op_remw; break; case 15: op = rv_op_remuw; break; + case 32: op = rv_op_add_uw; break; + case 36: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_zext_h; break; + } + break; + case 130: op = rv_op_sh1add_uw; break; + case 132: op = rv_op_sh2add_uw; break; + case 134: op = rv_op_sh3add_uw; break; case 256: op = rv_op_subw; break; case 261: op = rv_op_sraw; break; + case 385: op = rv_op_rolw; break; + case 389: op = rv_op_rorw; break; } break; case 16: From a88f0402391e9075df774affe5482d1286b9237e Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Tue, 21 Sep 2021 10:02:33 +0800 Subject: [PATCH 0342/1334] target/riscv: Set mstatus_hs.[SD|FS] bits if Clean and V=1 in mark_fs_dirty() When V=1, both vsstauts.FS and HS-level sstatus.FS are in effect. Modifying the floating-point state when V=1 causes both fields to be set to 3 (Dirty). However, it's possible that HS-level sstatus.FS is Clean and VS-level vsstatus.FS is Dirty at the time mark_fs_dirty() is called when V=1. We can't early return for this case because we still need to set sstatus.FS to Dirty according to spec. Signed-off-by: Frank Chang Reviewed-by: Vincent Chen Tested-by: Vincent Chen Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-id: 20210921020234.123448-1-frank.chang@sifive.com Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 4 ++++ target/riscv/translate.c | 30 +++++++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index bd519c9090..9e55b2f5b1 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -393,6 +393,7 @@ FIELD(TB_FLAGS, SEW, 5, 3) FIELD(TB_FLAGS, VILL, 8, 1) /* Is a Hypervisor instruction load/store allowed? */ FIELD(TB_FLAGS, HLSX, 9, 1) +FIELD(TB_FLAGS, MSTATUS_HS_FS, 10, 2) bool riscv_cpu_is_32bit(CPURISCVState *env); @@ -449,6 +450,9 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, get_field(env->hstatus, HSTATUS_HU))) { flags = FIELD_DP32(flags, TB_FLAGS, HLSX, 1); } + + flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_FS, + get_field(env->mstatus_hs, MSTATUS_FS)); } #endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c index b2d3444bc5..d2442f0cf5 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -58,6 +58,7 @@ typedef struct DisasContext { target_ulong misa; uint32_t opcode; uint32_t mstatus_fs; + uint32_t mstatus_hs_fs; uint32_t mem_idx; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for @@ -280,27 +281,29 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) static void mark_fs_dirty(DisasContext *ctx) { TCGv tmp; - target_ulong sd; + target_ulong sd = is_32bit(ctx) ? MSTATUS32_SD : MSTATUS64_SD; - if (ctx->mstatus_fs == MSTATUS_FS) { - return; + if (ctx->mstatus_fs != MSTATUS_FS) { + /* Remember the state change for the rest of the TB. */ + ctx->mstatus_fs = MSTATUS_FS; + + tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | sd); + tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_temp_free(tmp); } - /* Remember the state change for the rest of the TB. */ - ctx->mstatus_fs = MSTATUS_FS; - tmp = tcg_temp_new(); - sd = is_32bit(ctx) ? MSTATUS32_SD : MSTATUS64_SD; + if (ctx->virt_enabled && ctx->mstatus_hs_fs != MSTATUS_FS) { + /* Remember the stage change for the rest of the TB. */ + ctx->mstatus_hs_fs = MSTATUS_FS; - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | sd); - tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); - - if (ctx->virt_enabled) { + tmp = tcg_temp_new(); tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | sd); tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_temp_free(tmp); } - tcg_temp_free(tmp); } #else static inline void mark_fs_dirty(DisasContext *ctx) { } @@ -539,6 +542,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->frm = -1; /* unknown rounding mode */ ctx->ext_ifencei = cpu->cfg.ext_ifencei; ctx->vlen = cpu->cfg.vlen; + ctx->mstatus_hs_fs = FIELD_EX32(tb_flags, TB_FLAGS, MSTATUS_HS_FS); ctx->hlsx = FIELD_EX32(tb_flags, TB_FLAGS, HLSX); ctx->vill = FIELD_EX32(tb_flags, TB_FLAGS, VILL); ctx->sew = FIELD_EX32(tb_flags, TB_FLAGS, SEW); From 34229c46a721b2acf3cc1c1af3324f7468852330 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 26 Sep 2021 18:50:01 +0800 Subject: [PATCH 0343/1334] hw/char: ibex_uart: Register device in 'input' category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The category of ibex_uart device is not set. Put it into the 'input' category. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20210926105003.2716-1-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/char/ibex_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c index 9b0a817713..e58181fcf4 100644 --- a/hw/char/ibex_uart.c +++ b/hw/char/ibex_uart.c @@ -550,6 +550,7 @@ static void ibex_uart_class_init(ObjectClass *klass, void *data) dc->realize = ibex_uart_realize; dc->vmsd = &vmstate_ibex_uart; device_class_set_props(dc, ibex_uart_properties); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } static const TypeInfo ibex_uart_info = { From 5515ff162e8c20bfe907e21b2a3b9bed12a8ef67 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 26 Sep 2021 18:50:02 +0800 Subject: [PATCH 0344/1334] hw/char: shakti_uart: Register device in 'input' category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The category of shakti_uart device is not set. Put it into the 'input' category. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20210926105003.2716-2-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/char/shakti_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/shakti_uart.c b/hw/char/shakti_uart.c index 6870821325..98b142c7df 100644 --- a/hw/char/shakti_uart.c +++ b/hw/char/shakti_uart.c @@ -168,6 +168,7 @@ static void shakti_uart_class_init(ObjectClass *klass, void *data) dc->reset = shakti_uart_reset; dc->realize = shakti_uart_realize; device_class_set_props(dc, shakti_uart_properties); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } static const TypeInfo shakti_uart_info = { From 6a03349007f84e514e2e3def25c3f85400fdb47a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 26 Sep 2021 18:50:03 +0800 Subject: [PATCH 0345/1334] hw/char: sifive_uart: Register device in 'input' category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The category of sifive_uart device is not set. Put it into the 'input' category. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20210926105003.2716-3-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/char/sifive_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 278e21c434..1c75f792b3 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -248,6 +248,7 @@ static void sifive_uart_class_init(ObjectClass *oc, void *data) rc->phases.enter = sifive_uart_reset_enter; rc->phases.hold = sifive_uart_reset_hold; device_class_set_props(dc, sifive_uart_properties); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } static const TypeInfo sifive_uart_info = { From 284a66a8f6ffc8a720071b3f3cbc10cff0637337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Sep 2021 15:34:05 +0200 Subject: [PATCH 0346/1334] hw/char/mchp_pfsoc_mmuart: Simplify MCHP_PFSOC_MMUART_REG definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current MCHP_PFSOC_MMUART_REG_SIZE definition represent the size occupied by all the registers. However all registers are 32-bit wide, and the MemoryRegionOps handlers are restricted to 32-bit: static const MemoryRegionOps mchp_pfsoc_mmuart_ops = { .read = mchp_pfsoc_mmuart_read, .write = mchp_pfsoc_mmuart_write, .impl = { .min_access_size = 4, .max_access_size = 4, }, Avoid being triskaidekaphobic, simplify by using the number of registers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Tested-by: Bin Meng Reviewed-by: Alistair Francis Message-id: 20210925133407.1259392-2-f4bug@amsat.org Signed-off-by: Alistair Francis --- hw/char/mchp_pfsoc_mmuart.c | 14 ++++++++------ include/hw/char/mchp_pfsoc_mmuart.h | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/char/mchp_pfsoc_mmuart.c b/hw/char/mchp_pfsoc_mmuart.c index 2facf85c2d..584e7fec17 100644 --- a/hw/char/mchp_pfsoc_mmuart.c +++ b/hw/char/mchp_pfsoc_mmuart.c @@ -29,13 +29,14 @@ static uint64_t mchp_pfsoc_mmuart_read(void *opaque, hwaddr addr, unsigned size) { MchpPfSoCMMUartState *s = opaque; - if (addr >= MCHP_PFSOC_MMUART_REG_SIZE) { + addr >>= 2; + if (addr >= MCHP_PFSOC_MMUART_REG_COUNT) { qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", - __func__, addr); + __func__, addr << 2); return 0; } - return s->reg[addr / sizeof(uint32_t)]; + return s->reg[addr]; } static void mchp_pfsoc_mmuart_write(void *opaque, hwaddr addr, @@ -44,13 +45,14 @@ static void mchp_pfsoc_mmuart_write(void *opaque, hwaddr addr, MchpPfSoCMMUartState *s = opaque; uint32_t val32 = (uint32_t)value; - if (addr >= MCHP_PFSOC_MMUART_REG_SIZE) { + addr >>= 2; + if (addr >= MCHP_PFSOC_MMUART_REG_COUNT) { qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx - " v=0x%x\n", __func__, addr, val32); + " v=0x%x\n", __func__, addr << 2, val32); return; } - s->reg[addr / sizeof(uint32_t)] = val32; + s->reg[addr] = val32; } static const MemoryRegionOps mchp_pfsoc_mmuart_ops = { diff --git a/include/hw/char/mchp_pfsoc_mmuart.h b/include/hw/char/mchp_pfsoc_mmuart.h index f61990215f..9c012e6c97 100644 --- a/include/hw/char/mchp_pfsoc_mmuart.h +++ b/include/hw/char/mchp_pfsoc_mmuart.h @@ -30,7 +30,7 @@ #include "hw/char/serial.h" -#define MCHP_PFSOC_MMUART_REG_SIZE 52 +#define MCHP_PFSOC_MMUART_REG_COUNT 13 typedef struct MchpPfSoCMMUartState { MemoryRegion iomem; @@ -39,7 +39,7 @@ typedef struct MchpPfSoCMMUartState { SerialMM *serial; - uint32_t reg[MCHP_PFSOC_MMUART_REG_SIZE / sizeof(uint32_t)]; + uint32_t reg[MCHP_PFSOC_MMUART_REG_COUNT]; } MchpPfSoCMMUartState; /** From 24ce762df7d10175db1f91962d4fb1355b2645d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Sep 2021 15:34:06 +0200 Subject: [PATCH 0347/1334] hw/char/mchp_pfsoc_mmuart: Use a MemoryRegion container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our device have 2 different I/O regions: - a 16550 UART mapped for 32-bit accesses - 13 extra registers Instead of mapping each region on the main bus, introduce a container, map the 2 devices regions on the container, and map the container on the main bus. Before: (qemu) info mtree ... 0000000020100000-000000002010001f (prio 0, i/o): serial 0000000020100020-000000002010101f (prio 0, i/o): mchp.pfsoc.mmuart 0000000020102000-000000002010201f (prio 0, i/o): serial 0000000020102020-000000002010301f (prio 0, i/o): mchp.pfsoc.mmuart 0000000020104000-000000002010401f (prio 0, i/o): serial 0000000020104020-000000002010501f (prio 0, i/o): mchp.pfsoc.mmuart 0000000020106000-000000002010601f (prio 0, i/o): serial 0000000020106020-000000002010701f (prio 0, i/o): mchp.pfsoc.mmuart After: (qemu) info mtree ... 0000000020100000-0000000020100fff (prio 0, i/o): mchp.pfsoc.mmuart 0000000020100000-000000002010001f (prio 0, i/o): serial 0000000020100020-0000000020100fff (prio 0, i/o): mchp.pfsoc.mmuart.regs 0000000020102000-0000000020102fff (prio 0, i/o): mchp.pfsoc.mmuart 0000000020102000-000000002010201f (prio 0, i/o): serial 0000000020102020-0000000020102fff (prio 0, i/o): mchp.pfsoc.mmuart.regs 0000000020104000-0000000020104fff (prio 0, i/o): mchp.pfsoc.mmuart 0000000020104000-000000002010401f (prio 0, i/o): serial 0000000020104020-0000000020104fff (prio 0, i/o): mchp.pfsoc.mmuart.regs 0000000020106000-0000000020106fff (prio 0, i/o): mchp.pfsoc.mmuart 0000000020106000-000000002010601f (prio 0, i/o): serial 0000000020106020-0000000020106fff (prio 0, i/o): mchp.pfsoc.mmuart.regs Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Reviewed-by: Alistair Francis Tested-by: Bin Meng Message-id: 20210925133407.1259392-3-f4bug@amsat.org Signed-off-by: Alistair Francis --- hw/char/mchp_pfsoc_mmuart.c | 11 ++++++++--- include/hw/char/mchp_pfsoc_mmuart.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/char/mchp_pfsoc_mmuart.c b/hw/char/mchp_pfsoc_mmuart.c index 584e7fec17..ea58655976 100644 --- a/hw/char/mchp_pfsoc_mmuart.c +++ b/hw/char/mchp_pfsoc_mmuart.c @@ -25,6 +25,8 @@ #include "chardev/char.h" #include "hw/char/mchp_pfsoc_mmuart.h" +#define REGS_OFFSET 0x20 + static uint64_t mchp_pfsoc_mmuart_read(void *opaque, hwaddr addr, unsigned size) { MchpPfSoCMMUartState *s = opaque; @@ -72,16 +74,19 @@ MchpPfSoCMMUartState *mchp_pfsoc_mmuart_create(MemoryRegion *sysmem, s = g_new0(MchpPfSoCMMUartState, 1); + memory_region_init(&s->container, NULL, "mchp.pfsoc.mmuart", 0x1000); + memory_region_init_io(&s->iomem, NULL, &mchp_pfsoc_mmuart_ops, s, - "mchp.pfsoc.mmuart", 0x1000); + "mchp.pfsoc.mmuart.regs", 0x1000 - REGS_OFFSET); + memory_region_add_subregion(&s->container, REGS_OFFSET, &s->iomem); s->base = base; s->irq = irq; - s->serial = serial_mm_init(sysmem, base, 2, irq, 399193, chr, + s->serial = serial_mm_init(&s->container, 0, 2, irq, 399193, chr, DEVICE_LITTLE_ENDIAN); - memory_region_add_subregion(sysmem, base + 0x20, &s->iomem); + memory_region_add_subregion(sysmem, base, &s->container); return s; } diff --git a/include/hw/char/mchp_pfsoc_mmuart.h b/include/hw/char/mchp_pfsoc_mmuart.h index 9c012e6c97..864ac1a36b 100644 --- a/include/hw/char/mchp_pfsoc_mmuart.h +++ b/include/hw/char/mchp_pfsoc_mmuart.h @@ -33,6 +33,7 @@ #define MCHP_PFSOC_MMUART_REG_COUNT 13 typedef struct MchpPfSoCMMUartState { + MemoryRegion container; MemoryRegion iomem; hwaddr base; qemu_irq irq; From 31ca70b5ff7c6ac600211f60e3ab024642fe5abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 25 Sep 2021 15:34:07 +0200 Subject: [PATCH 0348/1334] hw/char/mchp_pfsoc_mmuart: QOM'ify PolarFire MMUART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Embed SerialMM in MchpPfSoCMMUartState and QOM-initialize it - Alias SERIAL_MM 'chardev' property on MCHP_PFSOC_UART - Forward SerialMM sysbus IRQ in mchp_pfsoc_mmuart_realize() - Add DeviceReset() method - Add vmstate structure for migration - Register device in 'input' category - Keep mchp_pfsoc_mmuart_create() behavior Note, serial_mm_init() calls qdev_set_legacy_instance_id(). This call is only needed for backwards-compatibility of incoming migration data with old versions of QEMU which implemented migration of devices with hand-rolled code. Since this device didn't previously handle migration at all, then it doesn't need to set the legacy instance ID. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Tested-by: Bin Meng Reviewed-by: Alistair Francis Message-id: 20210925133407.1259392-4-f4bug@amsat.org Signed-off-by: Alistair Francis --- hw/char/mchp_pfsoc_mmuart.c | 105 +++++++++++++++++++++++----- include/hw/char/mchp_pfsoc_mmuart.h | 12 +++- 2 files changed, 97 insertions(+), 20 deletions(-) diff --git a/hw/char/mchp_pfsoc_mmuart.c b/hw/char/mchp_pfsoc_mmuart.c index ea58655976..22f3e78eb9 100644 --- a/hw/char/mchp_pfsoc_mmuart.c +++ b/hw/char/mchp_pfsoc_mmuart.c @@ -22,8 +22,10 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "chardev/char.h" +#include "qapi/error.h" +#include "migration/vmstate.h" #include "hw/char/mchp_pfsoc_mmuart.h" +#include "hw/qdev-properties.h" #define REGS_OFFSET 0x20 @@ -67,26 +69,95 @@ static const MemoryRegionOps mchp_pfsoc_mmuart_ops = { }, }; -MchpPfSoCMMUartState *mchp_pfsoc_mmuart_create(MemoryRegion *sysmem, - hwaddr base, qemu_irq irq, Chardev *chr) +static void mchp_pfsoc_mmuart_reset(DeviceState *dev) { - MchpPfSoCMMUartState *s; + MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(dev); - s = g_new0(MchpPfSoCMMUartState, 1); + memset(s->reg, 0, sizeof(s->reg)); + device_cold_reset(DEVICE(&s->serial_mm)); +} - memory_region_init(&s->container, NULL, "mchp.pfsoc.mmuart", 0x1000); +static void mchp_pfsoc_mmuart_init(Object *obj) +{ + MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(obj); - memory_region_init_io(&s->iomem, NULL, &mchp_pfsoc_mmuart_ops, s, + object_initialize_child(obj, "serial-mm", &s->serial_mm, TYPE_SERIAL_MM); + object_property_add_alias(obj, "chardev", OBJECT(&s->serial_mm), "chardev"); +} + +static void mchp_pfsoc_mmuart_realize(DeviceState *dev, Error **errp) +{ + MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(dev); + + qdev_prop_set_uint8(DEVICE(&s->serial_mm), "regshift", 2); + qdev_prop_set_uint32(DEVICE(&s->serial_mm), "baudbase", 399193); + qdev_prop_set_uint8(DEVICE(&s->serial_mm), "endianness", + DEVICE_LITTLE_ENDIAN); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->serial_mm), errp)) { + return; + } + + sysbus_pass_irq(SYS_BUS_DEVICE(dev), SYS_BUS_DEVICE(&s->serial_mm)); + + memory_region_init(&s->container, OBJECT(s), "mchp.pfsoc.mmuart", 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container); + + memory_region_add_subregion(&s->container, 0, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->serial_mm), 0)); + + memory_region_init_io(&s->iomem, OBJECT(s), &mchp_pfsoc_mmuart_ops, s, "mchp.pfsoc.mmuart.regs", 0x1000 - REGS_OFFSET); memory_region_add_subregion(&s->container, REGS_OFFSET, &s->iomem); - - s->base = base; - s->irq = irq; - - s->serial = serial_mm_init(&s->container, 0, 2, irq, 399193, chr, - DEVICE_LITTLE_ENDIAN); - - memory_region_add_subregion(sysmem, base, &s->container); - - return s; +} + +static const VMStateDescription mchp_pfsoc_mmuart_vmstate = { + .name = "mchp.pfsoc.uart", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg, MchpPfSoCMMUartState, + MCHP_PFSOC_MMUART_REG_COUNT), + VMSTATE_END_OF_LIST() + } +}; + +static void mchp_pfsoc_mmuart_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = mchp_pfsoc_mmuart_realize; + dc->reset = mchp_pfsoc_mmuart_reset; + dc->vmsd = &mchp_pfsoc_mmuart_vmstate; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo mchp_pfsoc_mmuart_info = { + .name = TYPE_MCHP_PFSOC_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MchpPfSoCMMUartState), + .instance_init = mchp_pfsoc_mmuart_init, + .class_init = mchp_pfsoc_mmuart_class_init, +}; + +static void mchp_pfsoc_mmuart_register_types(void) +{ + type_register_static(&mchp_pfsoc_mmuart_info); +} + +type_init(mchp_pfsoc_mmuart_register_types) + +MchpPfSoCMMUartState *mchp_pfsoc_mmuart_create(MemoryRegion *sysmem, + hwaddr base, + qemu_irq irq, Chardev *chr) +{ + DeviceState *dev = qdev_new(TYPE_MCHP_PFSOC_UART); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + qdev_prop_set_chr(dev, "chardev", chr); + sysbus_realize(sbd, &error_fatal); + + memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(sbd, 0, irq); + + return MCHP_PFSOC_UART(dev); } diff --git a/include/hw/char/mchp_pfsoc_mmuart.h b/include/hw/char/mchp_pfsoc_mmuart.h index 864ac1a36b..b0e14ca355 100644 --- a/include/hw/char/mchp_pfsoc_mmuart.h +++ b/include/hw/char/mchp_pfsoc_mmuart.h @@ -28,17 +28,23 @@ #ifndef HW_MCHP_PFSOC_MMUART_H #define HW_MCHP_PFSOC_MMUART_H +#include "hw/sysbus.h" #include "hw/char/serial.h" #define MCHP_PFSOC_MMUART_REG_COUNT 13 +#define TYPE_MCHP_PFSOC_UART "mchp.pfsoc.uart" +OBJECT_DECLARE_SIMPLE_TYPE(MchpPfSoCMMUartState, MCHP_PFSOC_UART) + typedef struct MchpPfSoCMMUartState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ MemoryRegion container; MemoryRegion iomem; - hwaddr base; - qemu_irq irq; - SerialMM *serial; + SerialMM serial_mm; uint32_t reg[MCHP_PFSOC_MMUART_REG_COUNT]; } MchpPfSoCMMUartState; From b7af62ae2ca4a5f36a36d98e37d59e96fb3f8ef5 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 27 Sep 2021 15:21:23 +0800 Subject: [PATCH 0349/1334] hw/dma: sifive_pdma: Fix Control.claim bit detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At present the codes detect whether the DMA channel is claimed by: claimed = !!s->chan[ch].control & CONTROL_CLAIM; As ! has higher precedence over & (bitwise and), this is essentially claimed = (!!s->chan[ch].control) & CONTROL_CLAIM; which is wrong, as any non-zero bit set in the control register will produce a result of a claimed channel. Fixes: de7c7988d25d ("hw/dma: sifive_pdma: reset Next* registers when Control.claim is set") Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210927072124.1564129-1-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/dma/sifive_pdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c index b4fd40573a..b8ec7621f3 100644 --- a/hw/dma/sifive_pdma.c +++ b/hw/dma/sifive_pdma.c @@ -243,7 +243,7 @@ static void sifive_pdma_write(void *opaque, hwaddr offset, offset &= 0xfff; switch (offset) { case DMA_CONTROL: - claimed = !!s->chan[ch].control & CONTROL_CLAIM; + claimed = !!(s->chan[ch].control & CONTROL_CLAIM); if (!claimed && (value & CONTROL_CLAIM)) { /* reset Next* registers */ From 47b5fbf5a3f9c88936a0532ecb918f5e58ad348c Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 27 Sep 2021 15:21:24 +0800 Subject: [PATCH 0350/1334] hw/dma: sifive_pdma: Don't run DMA when channel is disclaimed If Control.run bit is set while not preserving the Control.claim bit, the DMA transfer shall not be started. The following result is PDMA tested in U-Boot on Unleashed board: => mw.l 0x3000000 0x0 <= Disclaim channel 0 => mw.l 0x3000000 0x1 <= Claim channel 0 => mw.l 0x3000004 0x55000000 <= wsize = rsize = 5 (2^5 = 32 bytes) => mw.q 0x3000008 0x2 <= NextBytes = 2 => mw.q 0x3000010 0x84000000 <= NextDestination = 0x84000000 => mw.q 0x3000018 0x84001000 <= NextSource = 0x84001000 => mw.l 0x84000000 0x87654321 <= Fill test data to dst => mw.l 0x84001000 0x12345678 <= Fill test data to src => md.l 0x84000000 1; md.l 0x84001000 1 <= Dump src/dst memory contents 84000000: 87654321 !Ce. 84001000: 12345678 xV4. => md.l 0x3000000 8 <= Dump PDMA status 03000000: 00000001 55000000 00000002 00000000 .......U........ 03000010: 84000000 00000000 84001000 00000000 ................ => mw.l 0x3000000 0x2 <= Set channel 0 run bit only => md.l 0x3000000 8 <= Dump PDMA status 03000000: 00000000 55000000 00000002 00000000 .......U........ 03000010: 84000000 00000000 84001000 00000000 ................ => md.l 0x84000000 1; md.l 0x84001000 1 <= Dump src/dst memory contents 84000000: 87654321 !Ce. 84001000: 12345678 xV4. Signed-off-by: Bin Meng Acked-by: Alistair Francis Message-id: 20210927072124.1564129-2-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/dma/sifive_pdma.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c index b8ec7621f3..85fe34f5f3 100644 --- a/hw/dma/sifive_pdma.c +++ b/hw/dma/sifive_pdma.c @@ -232,7 +232,7 @@ static void sifive_pdma_write(void *opaque, hwaddr offset, { SiFivePDMAState *s = opaque; int ch = SIFIVE_PDMA_CHAN_NO(offset); - bool claimed; + bool claimed, run; if (ch >= SIFIVE_PDMA_CHANS) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid channel no %d\n", @@ -244,6 +244,7 @@ static void sifive_pdma_write(void *opaque, hwaddr offset, switch (offset) { case DMA_CONTROL: claimed = !!(s->chan[ch].control & CONTROL_CLAIM); + run = !!(s->chan[ch].control & CONTROL_RUN); if (!claimed && (value & CONTROL_CLAIM)) { /* reset Next* registers */ @@ -254,13 +255,19 @@ static void sifive_pdma_write(void *opaque, hwaddr offset, s->chan[ch].next_src = 0; } + /* claim bit can only be cleared when run is low */ + if (run && !(value & CONTROL_CLAIM)) { + value |= CONTROL_CLAIM; + } + s->chan[ch].control = value; /* * If channel was not claimed before run bit is set, + * or if the channel is disclaimed when run was low, * DMA won't run. */ - if (!claimed) { + if (!claimed || (!run && !(value & CONTROL_CLAIM))) { s->chan[ch].control &= ~CONTROL_RUN; return; } From 9ae6ecd848dcd1b32003526ab65a0d4c644dfb07 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Wed, 29 Sep 2021 09:29:55 +1000 Subject: [PATCH 0351/1334] hw/riscv: shakti_c: Mark as not user creatable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark the shakti_c machine as not user creatable. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/639 Signed-off-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Tested-by: Philippe Mathieu-Daudé Message-Id: --- hw/riscv/shakti_c.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index 2f084d3c8d..d7d1f91fa5 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -150,6 +150,13 @@ static void shakti_c_soc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = shakti_c_soc_state_realize; + /* + * Reasons: + * - Creates CPUS in riscv_hart_realize(), and can create unintended + * CPUs + * - Uses serial_hds in realize function, thus can't be used twice + */ + dc->user_creatable = false; } static void shakti_c_soc_instance_init(Object *obj) From d431131439f228626aedc6d211c73b2a3616d5e1 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:28 +0200 Subject: [PATCH 0352/1334] job: Context changes in job_completed_txn_abort() Finalizing the job may cause its AioContext to change. This is noted by job_exit(), which points at job_txn_apply() to take this fact into account. However, job_completed() does not necessarily invoke job_txn_apply() (through job_completed_txn_success()), but potentially also job_completed_txn_abort(). The latter stores the context in a local variable, and so always acquires the same context at its end that it has released in the beginning -- which may be a different context from the one that job_exit() releases at its end. If it is different, qemu aborts ("qemu_mutex_unlock_impl: Operation not permitted"). Drop the local @outer_ctx variable from job_completed_txn_abort(), and instead re-acquire the actual job's context at the end of the function, so job_exit() will release the same. Signed-off-by: Hanna Reitz Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-2-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- job.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/job.c b/job.c index e7a5d28854..810e6a2065 100644 --- a/job.c +++ b/job.c @@ -737,7 +737,6 @@ static void job_cancel_async(Job *job, bool force) static void job_completed_txn_abort(Job *job) { - AioContext *outer_ctx = job->aio_context; AioContext *ctx; JobTxn *txn = job->txn; Job *other_job; @@ -751,10 +750,14 @@ static void job_completed_txn_abort(Job *job) txn->aborting = true; job_txn_ref(txn); - /* We can only hold the single job's AioContext lock while calling + /* + * We can only hold the single job's AioContext lock while calling * job_finalize_single() because the finalization callbacks can involve - * calls of AIO_WAIT_WHILE(), which could deadlock otherwise. */ - aio_context_release(outer_ctx); + * calls of AIO_WAIT_WHILE(), which could deadlock otherwise. + * Note that the job's AioContext may change when it is finalized. + */ + job_ref(job); + aio_context_release(job->aio_context); /* Other jobs are effectively cancelled by us, set the status for * them; this job, however, may or may not be cancelled, depending @@ -769,6 +772,10 @@ static void job_completed_txn_abort(Job *job) } while (!QLIST_EMPTY(&txn->jobs)) { other_job = QLIST_FIRST(&txn->jobs); + /* + * The job's AioContext may change, so store it in @ctx so we + * release the same context that we have acquired before. + */ ctx = other_job->aio_context; aio_context_acquire(ctx); if (!job_is_completed(other_job)) { @@ -779,7 +786,12 @@ static void job_completed_txn_abort(Job *job) aio_context_release(ctx); } - aio_context_acquire(outer_ctx); + /* + * Use job_ref()/job_unref() so we can read the AioContext here + * even if the job went away during job_finalize_single(). + */ + aio_context_acquire(job->aio_context); + job_unref(job); job_txn_unref(txn); } From a3810da5cff152a2a1b2d25a8b8080f640e491b6 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:29 +0200 Subject: [PATCH 0353/1334] mirror: Keep s->synced on error An error does not take us out of the READY phase, which is what s->synced signifies. It does of course mean that source and target are no longer in sync, but that is what s->actively_sync is for -- s->synced never meant that source and target are in sync, only that they were at some point (and at that point we transitioned into the READY phase). The tangible problem is that we transition to READY once we are in sync and s->synced is false. By resetting s->synced here, we will transition from READY to READY once the error is resolved (if the job keeps running), and that transition is not allowed. Signed-off-by: Hanna Reitz Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Message-Id: <20211006151940.214590-3-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/mirror.c | 1 - 1 file changed, 1 deletion(-) diff --git a/block/mirror.c b/block/mirror.c index c962e8b471..b367b29324 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -121,7 +121,6 @@ typedef enum MirrorMethod { static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { - s->synced = false; s->actively_synced = false; if (read) { return block_job_error_action(&s->common, s->on_source_error, From 447162242803bdc38bb198312e1d0a0675948424 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:30 +0200 Subject: [PATCH 0354/1334] mirror: Drop s->synced As of HEAD^, there is no meaning to s->synced other than whether the job is READY or not. job_is_ready() gives us that information, too. Suggested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Message-Id: <20211006151940.214590-4-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/mirror.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index b367b29324..035106bbb4 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -56,7 +56,6 @@ typedef struct MirrorBlockJob { bool zero_target; MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; - bool synced; /* Set when the target is synced (dirty bitmap is clean, nothing * in flight) and the job is running in active mode */ bool actively_synced; @@ -943,7 +942,6 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) if (s->bdev_length == 0) { /* Transition to the READY state and wait for complete. */ job_transition_to_ready(&s->common.job); - s->synced = true; s->actively_synced = true; while (!job_is_cancelled(&s->common.job) && !s->should_complete) { job_yield(&s->common.job); @@ -1035,7 +1033,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) should_complete = false; if (s->in_flight == 0 && cnt == 0) { trace_mirror_before_flush(s); - if (!s->synced) { + if (!job_is_ready(&s->common.job)) { if (mirror_flush(s) < 0) { /* Go check s->ret. */ continue; @@ -1046,7 +1044,6 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) * the target in a consistent state. */ job_transition_to_ready(&s->common.job); - s->synced = true; if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { s->actively_synced = true; } @@ -1090,14 +1087,15 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) ret = 0; - if (s->synced && !should_complete) { + if (job_is_ready(&s->common.job) && !should_complete) { delay_ns = (s->in_flight == 0 && cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); } - trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); + trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), + delay_ns); job_sleep_ns(&s->common.job, delay_ns); if (job_is_cancelled(&s->common.job) && - (!s->synced || s->common.job.force_cancel)) + (!job_is_ready(&s->common.job) || s->common.job.force_cancel)) { break; } @@ -1110,8 +1108,9 @@ immediate_exit: * or it was cancelled prematurely so that we do not guarantee that * the target is a copy of the source. */ - assert(ret < 0 || ((s->common.job.force_cancel || !s->synced) && - job_is_cancelled(&s->common.job))); + assert(ret < 0 || + ((s->common.job.force_cancel || !job_is_ready(&s->common.job)) && + job_is_cancelled(&s->common.job))); assert(need_drain); mirror_wait_for_all_io(s); } @@ -1134,7 +1133,7 @@ static void mirror_complete(Job *job, Error **errp) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); - if (!s->synced) { + if (!job_is_ready(job)) { error_setg(errp, "The active block job '%s' cannot be completed", job->id); return; From 1d4a43e9464f8945bd8aa2ed9d95f184b011befe Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:31 +0200 Subject: [PATCH 0355/1334] job: Force-cancel jobs in a failed transaction When a transaction is aborted, no result matters, and so all jobs within should be force-cancelled. Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-5-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- job.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/job.c b/job.c index 810e6a2065..e74d81928d 100644 --- a/job.c +++ b/job.c @@ -766,7 +766,12 @@ static void job_completed_txn_abort(Job *job) if (other_job != job) { ctx = other_job->aio_context; aio_context_acquire(ctx); - job_cancel_async(other_job, false); + /* + * This is a transaction: If one job failed, no result will matter. + * Therefore, pass force=true to terminate all other jobs as quickly + * as possible. + */ + job_cancel_async(other_job, true); aio_context_release(ctx); } } From 4cfb3f05627ad82af473e7f7ae113c3884cd04e3 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:32 +0200 Subject: [PATCH 0356/1334] job: @force parameter for job_cancel_sync() Callers should be able to specify whether they want job_cancel_sync() to force-cancel the job or not. In fact, almost all invocations do not care about consistency of the result and just want the job to terminate as soon as possible, so they should pass force=true. The replication block driver is the exception, specifically the active commit job it runs. As for job_cancel_sync_all(), all callers want it to force-cancel all jobs, because that is the point of it: To cancel all remaining jobs as quickly as possible (generally on process termination). So make it invoke job_cancel_sync() with force=true. This changes some iotest outputs, because quitting qemu while a mirror job is active will now lead to it being cancelled instead of completed, which is what we want. (Cancelling a READY mirror job with force=false may take an indefinite amount of time, which we do not want when quitting. If users want consistent results, they must have all jobs be done before they quit qemu.) Buglink: https://gitlab.com/qemu-project/qemu/-/issues/462 Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-6-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/replication.c | 4 +- blockdev.c | 4 +- include/qemu/job.h | 10 ++--- job.c | 18 ++++++-- tests/qemu-iotests/109.out | 60 +++++++++++---------------- tests/qemu-iotests/tests/qsd-jobs.out | 2 +- tests/unit/test-blockjob.c | 2 +- 7 files changed, 50 insertions(+), 50 deletions(-) diff --git a/block/replication.c b/block/replication.c index 32444b9a8f..55c8f894aa 100644 --- a/block/replication.c +++ b/block/replication.c @@ -149,7 +149,7 @@ static void replication_close(BlockDriverState *bs) if (s->stage == BLOCK_REPLICATION_FAILOVER) { commit_job = &s->commit_job->job; assert(commit_job->aio_context == qemu_get_current_aio_context()); - job_cancel_sync(commit_job); + job_cancel_sync(commit_job, false); } if (s->mode == REPLICATION_MODE_SECONDARY) { @@ -726,7 +726,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) * disk, secondary disk in backup_job_completed(). */ if (s->backup_job) { - job_cancel_sync(&s->backup_job->job); + job_cancel_sync(&s->backup_job->job, true); } if (!failover) { diff --git a/blockdev.c b/blockdev.c index e79c5f3b5e..b35072644e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1847,7 +1847,7 @@ static void drive_backup_abort(BlkActionState *common) aio_context = bdrv_get_aio_context(state->bs); aio_context_acquire(aio_context); - job_cancel_sync(&state->job->job); + job_cancel_sync(&state->job->job, true); aio_context_release(aio_context); } @@ -1948,7 +1948,7 @@ static void blockdev_backup_abort(BlkActionState *common) aio_context = bdrv_get_aio_context(state->bs); aio_context_acquire(aio_context); - job_cancel_sync(&state->job->job); + job_cancel_sync(&state->job->job, true); aio_context_release(aio_context); } diff --git a/include/qemu/job.h b/include/qemu/job.h index 41162ed494..2eddf3b536 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -506,18 +506,18 @@ void job_user_cancel(Job *job, bool force, Error **errp); /** * Synchronously cancel the @job. The completion callback is called - * before the function returns. The job may actually complete - * instead of canceling itself; the circumstances under which this - * happens depend on the kind of job that is active. + * before the function returns. If @force is false, the job may + * actually complete instead of canceling itself; the circumstances + * under which this happens depend on the kind of job that is active. * * Returns the return value from the job if the job actually completed * during the call, or -ECANCELED if it was canceled. * * Callers must hold the AioContext lock of job->aio_context. */ -int job_cancel_sync(Job *job); +int job_cancel_sync(Job *job, bool force); -/** Synchronously cancels all jobs using job_cancel_sync(). */ +/** Synchronously force-cancels all jobs using job_cancel_sync(). */ void job_cancel_sync_all(void); /** diff --git a/job.c b/job.c index e74d81928d..dfac35d553 100644 --- a/job.c +++ b/job.c @@ -981,9 +981,21 @@ static void job_cancel_err(Job *job, Error **errp) job_cancel(job, false); } -int job_cancel_sync(Job *job) +/** + * Same as job_cancel_err(), but force-cancel. + */ +static void job_force_cancel_err(Job *job, Error **errp) { - return job_finish_sync(job, &job_cancel_err, NULL); + job_cancel(job, true); +} + +int job_cancel_sync(Job *job, bool force) +{ + if (force) { + return job_finish_sync(job, &job_force_cancel_err, NULL); + } else { + return job_finish_sync(job, &job_cancel_err, NULL); + } } void job_cancel_sync_all(void) @@ -994,7 +1006,7 @@ void job_cancel_sync_all(void) while ((job = job_next(NULL))) { aio_context = job->aio_context; aio_context_acquire(aio_context); - job_cancel_sync(job); + job_cancel_sync(job, true); aio_context_release(aio_context); } } diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 8f839b4b7f..e29280015e 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -44,9 +44,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -95,9 +94,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -146,9 +144,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -197,9 +194,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -248,9 +244,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -299,9 +294,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -349,9 +343,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -399,9 +392,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -449,9 +441,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -499,9 +490,8 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -529,9 +519,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. @@ -552,9 +541,8 @@ Images are identical. {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Images are identical. diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out index 189423354b..c1bc9b8356 100644 --- a/tests/qemu-iotests/tests/qsd-jobs.out +++ b/tests/qemu-iotests/tests/qsd-jobs.out @@ -8,7 +8,7 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} === Streaming can't get permission on base node === diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index dcacfa6c7c..4c9e1bf1e5 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -230,7 +230,7 @@ static void cancel_common(CancelJob *s) ctx = job->job.aio_context; aio_context_acquire(ctx); - job_cancel_sync(&job->job); + job_cancel_sync(&job->job, true); if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { Job *dummy = &job->job; job_dismiss(&dummy, &error_abort); From 73895f3838cd7fdaf185cf1dbc47be58844a966f Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:33 +0200 Subject: [PATCH 0357/1334] jobs: Give Job.force_cancel more meaning We largely have two cancel modes for jobs: First, there is actual cancelling. The job is terminated as soon as possible, without trying to reach a consistent result. Second, we have mirror in the READY state. Technically, the job is not really cancelled, but it just is a different completion mode. The job can still run for an indefinite amount of time while it tries to reach a consistent result. We want to be able to clearly distinguish which cancel mode a job is in (when it has been cancelled). We can use Job.force_cancel for this, but right now it only reflects cancel requests from the user with force=true, but clearly, jobs that do not even distinguish between force=false and force=true are effectively always force-cancelled. So this patch has Job.force_cancel signify whether the job will terminate as soon as possible (force_cancel=true) or whether it will effectively remain running despite being "cancelled" (force_cancel=false). To this end, we let jobs that provide JobDriver.cancel() tell the generic job code whether they will terminate as soon as possible or not, and for jobs that do not provide that method we assume they will. Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Message-Id: <20211006151940.214590-7-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/backup.c | 3 ++- block/mirror.c | 24 ++++++++++++++++++------ include/qemu/job.h | 11 ++++++++++- job.c | 6 +++++- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/block/backup.c b/block/backup.c index 8b072db5d9..21d5983779 100644 --- a/block/backup.c +++ b/block/backup.c @@ -327,11 +327,12 @@ static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed) } } -static void backup_cancel(Job *job, bool force) +static bool backup_cancel(Job *job, bool force) { BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); bdrv_cancel_in_flight(s->target_bs); + return true; } static const BlockJobDriver backup_job_driver = { diff --git a/block/mirror.c b/block/mirror.c index 035106bbb4..010b9e1672 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1094,9 +1094,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), delay_ns); job_sleep_ns(&s->common.job, delay_ns); - if (job_is_cancelled(&s->common.job) && - (!job_is_ready(&s->common.job) || s->common.job.force_cancel)) - { + if (job_is_cancelled(&s->common.job) && s->common.job.force_cancel) { break; } s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); @@ -1109,7 +1107,7 @@ immediate_exit: * the target is a copy of the source. */ assert(ret < 0 || - ((s->common.job.force_cancel || !job_is_ready(&s->common.job)) && + (s->common.job.force_cancel && job_is_cancelled(&s->common.job))); assert(need_drain); mirror_wait_for_all_io(s); @@ -1195,14 +1193,27 @@ static bool mirror_drained_poll(BlockJob *job) return !!s->in_flight; } -static void mirror_cancel(Job *job, bool force) +static bool mirror_cancel(Job *job, bool force) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockDriverState *target = blk_bs(s->target); - if (force || !job_is_ready(job)) { + /* + * Before the job is READY, we treat any cancellation like a + * force-cancellation. + */ + force = force || !job_is_ready(job); + + if (force) { bdrv_cancel_in_flight(target); } + return force; +} + +static bool commit_active_cancel(Job *job, bool force) +{ + /* Same as above in mirror_cancel() */ + return force || !job_is_ready(job); } static const BlockJobDriver mirror_job_driver = { @@ -1232,6 +1243,7 @@ static const BlockJobDriver commit_active_job_driver = { .abort = mirror_abort, .pause = mirror_pause, .complete = mirror_complete, + .cancel = commit_active_cancel, }, .drained_poll = mirror_drained_poll, }; diff --git a/include/qemu/job.h b/include/qemu/job.h index 2eddf3b536..90f6abbd6a 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -253,8 +253,17 @@ struct JobDriver { /** * If the callback is not NULL, it will be invoked in job_cancel_async + * + * This function must return true if the job will be cancelled + * immediately without any further I/O (mandatory if @force is + * true), and false otherwise. This lets the generic job layer + * know whether a job has been truly (force-)cancelled, or whether + * it is just in a special completion mode (like mirror after + * READY). + * (If the callback is NULL, the job is assumed to terminate + * without I/O.) */ - void (*cancel)(Job *job, bool force); + bool (*cancel)(Job *job, bool force); /** Called when the job is freed */ diff --git a/job.c b/job.c index dfac35d553..81c016eb10 100644 --- a/job.c +++ b/job.c @@ -719,8 +719,12 @@ static int job_finalize_single(Job *job) static void job_cancel_async(Job *job, bool force) { if (job->driver->cancel) { - job->driver->cancel(job, force); + force = job->driver->cancel(job, force); + } else { + /* No .cancel() means the job will behave as if force-cancelled */ + force = true; } + if (job->user_paused) { /* Do not call job_enter here, the caller will handle it. */ if (job->driver->user_resume) { From 401dd096ef9d19956977b371362107448575ef40 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:34 +0200 Subject: [PATCH 0358/1334] job: Do not soft-cancel after a job is done The only job that supports a soft cancel mode is the mirror job, and in such a case it resets its .cancelled field before it leaves its .run() function, so it does not really count as cancelled. However, it is possible to cancel the job after .run() returns and before job_exit() (which is run in the main loop) is executed. Then, .cancelled would still be true and the job would count as cancelled. This does not seem to be in the interest of the mirror job, so adjust job_cancel_async() to not set .cancelled in such a case, and job_cancel() to not invoke job_completed_txn_abort(). Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-8-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- job.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/job.c b/job.c index 81c016eb10..44e741ebd4 100644 --- a/job.c +++ b/job.c @@ -734,9 +734,19 @@ static void job_cancel_async(Job *job, bool force) assert(job->pause_count > 0); job->pause_count--; } - job->cancelled = true; - /* To prevent 'force == false' overriding a previous 'force == true' */ - job->force_cancel |= force; + + /* + * Ignore soft cancel requests after the job is already done + * (We will still invoke job->driver->cancel() above, but if the + * job driver supports soft cancelling and the job is done, that + * should be a no-op, too. We still call it so it can override + * @force.) + */ + if (force || !job->deferred_to_main_loop) { + job->cancelled = true; + /* To prevent 'force == false' overriding a previous 'force == true' */ + job->force_cancel |= force; + } } static void job_completed_txn_abort(Job *job) @@ -963,7 +973,14 @@ void job_cancel(Job *job, bool force) if (!job_started(job)) { job_completed(job); } else if (job->deferred_to_main_loop) { - job_completed_txn_abort(job); + /* + * job_cancel_async() ignores soft-cancel requests for jobs + * that are already done (i.e. deferred to the main loop). We + * have to check again whether the job is really cancelled. + */ + if (job_is_cancelled(job)) { + job_completed_txn_abort(job); + } } else { job_enter(job); } From 08b83bff2a77e082d0b29680a40f0aaf9996bd16 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:35 +0200 Subject: [PATCH 0359/1334] job: Add job_cancel_requested() Most callers of job_is_cancelled() actually want to know whether the job is on its way to immediate termination. For example, we refuse to pause jobs that are cancelled; but this only makes sense for jobs that are really actually cancelled. A mirror job that is cancelled during READY with force=false should absolutely be allowed to pause. This "cancellation" (which is actually a kind of completion) may take an indefinite amount of time, and so should behave like any job during normal operation. For example, with on-target-error=stop, the job should stop on write errors. (In contrast, force-cancelled jobs should not get write errors, as they should just terminate and not do further I/O.) Therefore, redefine job_is_cancelled() to only return true for jobs that are force-cancelled (which as of HEAD^ means any job that interprets the cancellation request as a request for immediate termination), and add job_cancel_requested() as the general variant, which returns true for any jobs which have been requested to be cancelled, whether it be immediately or after an arbitrarily long completion phase. Finally, here is a justification for how different job_is_cancelled() invocations are treated by this patch: - block/mirror.c (mirror_run()): - The first invocation is a while loop that should loop until the job has been cancelled or scheduled for completion. What kind of cancel does not matter, only the fact that the job is supposed to end. - The second invocation wants to know whether the job has been soft-cancelled. Calling job_cancel_requested() is a bit too broad, but if the job were force-cancelled, we should leave the main loop as soon as possible anyway, so this should not matter here. - The last two invocations already check force_cancel, so they should continue to use job_is_cancelled(). - block/backup.c, block/commit.c, block/stream.c, anything in tests/: These jobs know only force-cancel, so there is no difference between job_is_cancelled() and job_cancel_requested(). We can continue using job_is_cancelled(). - job.c: - job_pause_point(), job_yield(), job_sleep_ns(): Only force-cancelled jobs should be prevented from being paused. Continue using job_is_cancelled(). - job_update_rc(), job_finalize_single(), job_finish_sync(): These functions are all called after the job has left its main loop. The mirror job (the only job that can be soft-cancelled) will clear .cancelled before leaving the main loop if it has been soft-cancelled. Therefore, these functions will observe .cancelled to be true only if the job has been force-cancelled. We can continue to use job_is_cancelled(). (Furthermore, conceptually, a soft-cancelled mirror job should not report to have been cancelled. It should report completion (see also the block-job-cancel QAPI documentation). Therefore, it makes sense for these functions not to distinguish between a soft-cancelled mirror job and a job that has completed as normal.) - job_completed_txn_abort(): All jobs other than @job have been force-cancelled. job_is_cancelled() must be true for them. Regarding @job itself: job_completed_txn_abort() is mostly called when the job's return value is not 0. A soft-cancelled mirror has a return value of 0, and so will not end up here then. However, job_cancel() invokes job_completed_txn_abort() if the job has been deferred to the main loop, which is mostly the case for completed jobs (which skip the assertion), but not for sure. To be safe, use job_cancel_requested() in this assertion. - job_complete(): This is function eventually invoked by the user (through qmp_block_job_complete() or qmp_job_complete(), or job_complete_sync(), which comes from qemu-img). The intention here is to prevent a user from invoking job-complete after the job has been cancelled. This should also apply to soft cancelling: After a mirror job has been soft-cancelled, the user should not be able to decide otherwise and have it complete as normal (i.e. pivoting to the target). - job_cancel(): Both functions are equivalent (see comment there), but we want to use job_is_cancelled(), because this shows that we call job_completed_txn_abort() only for force-cancelled jobs. (As explained for job_update_rc(), soft-cancelled jobs should be treated as if they have completed as normal.) Buglink: https://gitlab.com/qemu-project/qemu/-/issues/462 Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-9-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/mirror.c | 10 ++++------ include/qemu/job.h | 8 +++++++- job.c | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 010b9e1672..2d9642cb00 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -943,7 +943,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) /* Transition to the READY state and wait for complete. */ job_transition_to_ready(&s->common.job); s->actively_synced = true; - while (!job_is_cancelled(&s->common.job) && !s->should_complete) { + while (!job_cancel_requested(&s->common.job) && !s->should_complete) { job_yield(&s->common.job); } s->common.job.cancelled = false; @@ -1050,7 +1050,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) } should_complete = s->should_complete || - job_is_cancelled(&s->common.job); + job_cancel_requested(&s->common.job); cnt = bdrv_get_dirty_count(s->dirty_bitmap); } @@ -1094,7 +1094,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), delay_ns); job_sleep_ns(&s->common.job, delay_ns); - if (job_is_cancelled(&s->common.job) && s->common.job.force_cancel) { + if (job_is_cancelled(&s->common.job)) { break; } s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); @@ -1106,9 +1106,7 @@ immediate_exit: * or it was cancelled prematurely so that we do not guarantee that * the target is a copy of the source. */ - assert(ret < 0 || - (s->common.job.force_cancel && - job_is_cancelled(&s->common.job))); + assert(ret < 0 || job_is_cancelled(&s->common.job)); assert(need_drain); mirror_wait_for_all_io(s); } diff --git a/include/qemu/job.h b/include/qemu/job.h index 90f6abbd6a..6e67b6977f 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -436,9 +436,15 @@ const char *job_type_str(const Job *job); /** Returns true if the job should not be visible to the management layer. */ bool job_is_internal(Job *job); -/** Returns whether the job is scheduled for cancellation. */ +/** Returns whether the job is being cancelled. */ bool job_is_cancelled(Job *job); +/** + * Returns whether the job is scheduled for cancellation (at an + * indefinite point). + */ +bool job_cancel_requested(Job *job); + /** Returns whether the job is in a completed state. */ bool job_is_completed(Job *job); diff --git a/job.c b/job.c index 44e741ebd4..b0cf2d8374 100644 --- a/job.c +++ b/job.c @@ -216,6 +216,11 @@ const char *job_type_str(const Job *job) } bool job_is_cancelled(Job *job) +{ + return job->cancelled && job->force_cancel; +} + +bool job_cancel_requested(Job *job) { return job->cancelled; } @@ -798,7 +803,7 @@ static void job_completed_txn_abort(Job *job) ctx = other_job->aio_context; aio_context_acquire(ctx); if (!job_is_completed(other_job)) { - assert(job_is_cancelled(other_job)); + assert(job_cancel_requested(other_job)); job_finish_sync(other_job, NULL, NULL); } job_finalize_single(other_job); @@ -977,6 +982,11 @@ void job_cancel(Job *job, bool force) * job_cancel_async() ignores soft-cancel requests for jobs * that are already done (i.e. deferred to the main loop). We * have to check again whether the job is really cancelled. + * (job_cancel_requested() and job_is_cancelled() are equivalent + * here, because job_cancel_async() will make soft-cancel + * requests no-ops when deferred_to_main_loop is true. We + * choose to call job_is_cancelled() to show that we invoke + * job_completed_txn_abort() only for force-cancelled jobs.) */ if (job_is_cancelled(job)) { job_completed_txn_abort(job); @@ -1044,7 +1054,7 @@ void job_complete(Job *job, Error **errp) if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) { return; } - if (job_is_cancelled(job) || !job->driver->complete) { + if (job_cancel_requested(job) || !job->driver->complete) { error_setg(errp, "The active block job '%s' cannot be completed", job->id); return; From 20ad4d204accb27346b05b046ee8225c6725ef49 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:36 +0200 Subject: [PATCH 0360/1334] mirror: Use job_is_cancelled() mirror_drained_poll() returns true whenever the job is cancelled, because "we [can] be sure that it won't issue more requests". However, this is only true for force-cancelled jobs, so use job_is_cancelled(). Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-10-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/mirror.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/mirror.c b/block/mirror.c index 2d9642cb00..0eaba88060 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1184,7 +1184,7 @@ static bool mirror_drained_poll(BlockJob *job) * from one of our own drain sections, to avoid a deadlock waiting for * ourselves. */ - if (!s->common.job.paused && !s->common.job.cancelled && !s->in_drain) { + if (!s->common.job.paused && !job_is_cancelled(&job->job) && !s->in_drain) { return true; } From 4feeec7e23b4151c962cb1b9f1a0e403803dfaee Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:37 +0200 Subject: [PATCH 0361/1334] mirror: Check job_is_cancelled() earlier We must check whether the job is force-cancelled early in our main loop, most importantly before any `continue` statement. For example, we used to have `continue`s before our current checking location that are triggered by `mirror_flush()` failing. So, if `mirror_flush()` kept failing, force-cancelling the job would not terminate it. Jobs can be cancelled while they yield, and once they are (force-cancelled), they should not generate new I/O requests. Therefore, we should put the check after the last yield before mirror_iteration() is invoked. Buglink: https://gitlab.com/qemu-project/qemu/-/issues/462 Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-11-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/mirror.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 0eaba88060..3393ede0e1 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1007,6 +1007,11 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) job_pause_point(&s->common.job); + if (job_is_cancelled(&s->common.job)) { + ret = 0; + goto immediate_exit; + } + cnt = bdrv_get_dirty_count(s->dirty_bitmap); /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is * the number of bytes currently being processed; together those are @@ -1085,8 +1090,6 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) break; } - ret = 0; - if (job_is_ready(&s->common.job) && !should_complete) { delay_ns = (s->in_flight == 0 && cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); @@ -1094,9 +1097,6 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), delay_ns); job_sleep_ns(&s->common.job, delay_ns); - if (job_is_cancelled(&s->common.job)) { - break; - } s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } From 9b230ef93e406dc46b82eebde996ef6a08b929d6 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:38 +0200 Subject: [PATCH 0362/1334] mirror: Stop active mirroring after force-cancel Once the mirror job is force-cancelled (job_is_cancelled() is true), we should not generate new I/O requests. This applies to active mirroring, too, so stop it once the job is cancelled. (We must still forward all I/O requests to the source, though, of course, but those are not really I/O requests generated by the job, so this is fine.) Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-12-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/mirror.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block/mirror.c b/block/mirror.c index 3393ede0e1..75874c1806 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1425,6 +1425,7 @@ static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs, bool copy_to_target; copy_to_target = s->job->ret >= 0 && + !job_is_cancelled(&s->job->common.job) && s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; if (copy_to_target) { @@ -1473,6 +1474,7 @@ static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, bool copy_to_target; copy_to_target = s->job->ret >= 0 && + !job_is_cancelled(&s->job->common.job) && s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; if (copy_to_target) { From a640fa0e381caf572266c6c07d026dd07cf66a49 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:39 +0200 Subject: [PATCH 0363/1334] mirror: Do not clear .cancelled Clearing .cancelled before leaving the main loop when the job has been soft-cancelled is no longer necessary since job_is_cancelled() only returns true for jobs that have been force-cancelled. Therefore, this only makes a differences in places that call job_cancel_requested(). In block/mirror.c, this is done only before .cancelled was cleared. In job.c, there are two callers: - job_completed_txn_abort() asserts that .cancelled is true, so keeping it true will not affect this place. - job_complete() refuses to let a job complete that has .cancelled set. It is correct to refuse to let the user invoke job-complete on mirror jobs that have already been soft-cancelled. With this change, there are no places that reset .cancelled to false and so we can be sure that .force_cancel can only be true if .cancelled is true as well. Assert this in job_is_cancelled(). Signed-off-by: Hanna Reitz Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-13-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/mirror.c | 2 -- job.c | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 75874c1806..efec2c7674 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -946,7 +946,6 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) while (!job_cancel_requested(&s->common.job) && !s->should_complete) { job_yield(&s->common.job); } - s->common.job.cancelled = false; goto immediate_exit; } @@ -1085,7 +1084,6 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) * completion. */ assert(QLIST_EMPTY(&bs->tracked_requests)); - s->common.job.cancelled = false; need_drain = false; break; } diff --git a/job.c b/job.c index b0cf2d8374..dbfa67bb0a 100644 --- a/job.c +++ b/job.c @@ -217,7 +217,9 @@ const char *job_type_str(const Job *job) bool job_is_cancelled(Job *job) { - return job->cancelled && job->force_cancel; + /* force_cancel may be true only if cancelled is true, too */ + assert(job->cancelled || !job->force_cancel); + return job->force_cancel; } bool job_cancel_requested(Job *job) From 2451f72527d8760566a499b7513e17aaceb0f131 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 6 Oct 2021 17:19:40 +0200 Subject: [PATCH 0364/1334] iotests: Add mirror-ready-cancel-error test Test what happens when there is an I/O error after a mirror job in the READY phase has been cancelled. Signed-off-by: Hanna Reitz Reviewed-by: Vladimir Sementsov-Ogievskiy Tested-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006151940.214590-14-hreitz@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- .../tests/mirror-ready-cancel-error | 143 ++++++++++++++++++ .../tests/mirror-ready-cancel-error.out | 5 + 2 files changed, 148 insertions(+) create mode 100755 tests/qemu-iotests/tests/mirror-ready-cancel-error create mode 100644 tests/qemu-iotests/tests/mirror-ready-cancel-error.out diff --git a/tests/qemu-iotests/tests/mirror-ready-cancel-error b/tests/qemu-iotests/tests/mirror-ready-cancel-error new file mode 100755 index 0000000000..f2dc88881f --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-ready-cancel-error @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Test what happens when errors occur to a mirror job after it has +# been cancelled in the READY phase +# +# Copyright (C) 2021 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import iotests + + +image_size = 1 * 1024 * 1024 +source = os.path.join(iotests.test_dir, 'source.img') +target = os.path.join(iotests.test_dir, 'target.img') + + +class TestMirrorReadyCancelError(iotests.QMPTestCase): + def setUp(self) -> None: + assert iotests.qemu_img_create('-f', iotests.imgfmt, source, + str(image_size)) == 0 + assert iotests.qemu_img_create('-f', iotests.imgfmt, target, + str(image_size)) == 0 + + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self) -> None: + self.vm.shutdown() + os.remove(source) + os.remove(target) + + def add_blockdevs(self, once: bool) -> None: + res = self.vm.qmp('blockdev-add', + **{'node-name': 'source', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source + }}) + self.assert_qmp(res, 'return', {}) + + # blkdebug notes: + # Enter state 2 on the first flush, which happens before the + # job enters the READY state. The second flush will happen + # when the job is about to complete, and we want that one to + # fail. + res = self.vm.qmp('blockdev-add', + **{'node-name': 'target', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': target + }, + 'set-state': [{ + 'event': 'flush_to_disk', + 'state': 1, + 'new_state': 2 + }], + 'inject-error': [{ + 'event': 'flush_to_disk', + 'once': once, + 'immediately': True, + 'state': 2 + }]}}) + self.assert_qmp(res, 'return', {}) + + def start_mirror(self) -> None: + res = self.vm.qmp('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + filter_node_name='mirror-top', + sync='full', + on_target_error='stop') + self.assert_qmp(res, 'return', {}) + + def cancel_mirror_with_error(self) -> None: + self.vm.event_wait('BLOCK_JOB_READY') + + # Write something so will not leave the job immediately, but + # flush first (which will fail, thanks to blkdebug) + res = self.vm.qmp('human-monitor-command', + command_line='qemu-io mirror-top "write 0 64k"') + self.assert_qmp(res, 'return', '') + + # Drain status change events + while self.vm.event_wait('JOB_STATUS_CHANGE', timeout=0.0) is not None: + pass + + res = self.vm.qmp('block-job-cancel', device='mirror') + self.assert_qmp(res, 'return', {}) + + self.vm.event_wait('BLOCK_JOB_ERROR') + + def test_transient_error(self) -> None: + self.add_blockdevs(True) + self.start_mirror() + self.cancel_mirror_with_error() + + while True: + e = self.vm.event_wait('JOB_STATUS_CHANGE') + if e['data']['status'] == 'standby': + # Transient error, try again + self.vm.qmp('block-job-resume', device='mirror') + elif e['data']['status'] == 'null': + break + + def test_persistent_error(self) -> None: + self.add_blockdevs(False) + self.start_mirror() + self.cancel_mirror_with_error() + + while True: + e = self.vm.event_wait('JOB_STATUS_CHANGE') + if e['data']['status'] == 'standby': + # Persistent error, no point in continuing + self.vm.qmp('block-job-cancel', device='mirror', force=True) + elif e['data']['status'] == 'null': + break + + +if __name__ == '__main__': + # LUKS would require special key-secret handling in add_blockdevs() + iotests.main(supported_fmts=['generic'], + unsupported_fmts=['luks'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/mirror-ready-cancel-error.out b/tests/qemu-iotests/tests/mirror-ready-cancel-error.out new file mode 100644 index 0000000000..fbc63e62f8 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-ready-cancel-error.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK From f0ed36a64f1750a0ebc5d2a5437f4740985461d7 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 27 Jul 2021 16:59:35 +0200 Subject: [PATCH 0365/1334] iothread: rename PollParamInfo to IOThreadParamInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1793ad0247 ("iothread: add aio-max-batch parameter") added a new parameter (aio-max-batch) to IOThread and used PollParamInfo structure to handle it. Since it is not a parameter of the polling mechanism, we rename the structure to a more generic IOThreadParamInfo. Suggested-by: Kevin Wolf Signed-off-by: Stefano Garzarella Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210727145936.147032-2-sgarzare@redhat.com Signed-off-by: Stefan Hajnoczi --- iothread.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/iothread.c b/iothread.c index ddbbde61f7..a73e560ba0 100644 --- a/iothread.c +++ b/iothread.c @@ -215,18 +215,18 @@ static void iothread_complete(UserCreatable *obj, Error **errp) typedef struct { const char *name; ptrdiff_t offset; /* field's byte offset in IOThread struct */ -} PollParamInfo; +} IOThreadParamInfo; -static PollParamInfo poll_max_ns_info = { +static IOThreadParamInfo poll_max_ns_info = { "poll-max-ns", offsetof(IOThread, poll_max_ns), }; -static PollParamInfo poll_grow_info = { +static IOThreadParamInfo poll_grow_info = { "poll-grow", offsetof(IOThread, poll_grow), }; -static PollParamInfo poll_shrink_info = { +static IOThreadParamInfo poll_shrink_info = { "poll-shrink", offsetof(IOThread, poll_shrink), }; -static PollParamInfo aio_max_batch_info = { +static IOThreadParamInfo aio_max_batch_info = { "aio-max-batch", offsetof(IOThread, aio_max_batch), }; @@ -234,7 +234,7 @@ static void iothread_get_param(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { IOThread *iothread = IOTHREAD(obj); - PollParamInfo *info = opaque; + IOThreadParamInfo *info = opaque; int64_t *field = (void *)iothread + info->offset; visit_type_int64(v, name, field, errp); @@ -244,7 +244,7 @@ static bool iothread_set_param(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { IOThread *iothread = IOTHREAD(obj); - PollParamInfo *info = opaque; + IOThreadParamInfo *info = opaque; int64_t *field = (void *)iothread + info->offset; int64_t value; From 1cc7eada97914f090125e588497986f6f7900514 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 27 Jul 2021 16:59:36 +0200 Subject: [PATCH 0366/1334] iothread: use IOThreadParamInfo in iothread_[set|get]_param() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 0445409d74 ("iothread: generalize iothread_set_param/iothread_get_param") moved common code to set and get IOThread parameters in two new functions. These functions are called inside callbacks, so we don't need to use an opaque pointer. Let's replace `void *opaque` parameter with `IOThreadParamInfo *info`. Suggested-by: Kevin Wolf Signed-off-by: Stefano Garzarella Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210727145936.147032-3-sgarzare@redhat.com Signed-off-by: Stefan Hajnoczi --- iothread.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/iothread.c b/iothread.c index a73e560ba0..0f98af0f2a 100644 --- a/iothread.c +++ b/iothread.c @@ -231,20 +231,18 @@ static IOThreadParamInfo aio_max_batch_info = { }; static void iothread_get_param(Object *obj, Visitor *v, - const char *name, void *opaque, Error **errp) + const char *name, IOThreadParamInfo *info, Error **errp) { IOThread *iothread = IOTHREAD(obj); - IOThreadParamInfo *info = opaque; int64_t *field = (void *)iothread + info->offset; visit_type_int64(v, name, field, errp); } static bool iothread_set_param(Object *obj, Visitor *v, - const char *name, void *opaque, Error **errp) + const char *name, IOThreadParamInfo *info, Error **errp) { IOThread *iothread = IOTHREAD(obj); - IOThreadParamInfo *info = opaque; int64_t *field = (void *)iothread + info->offset; int64_t value; @@ -266,16 +264,18 @@ static bool iothread_set_param(Object *obj, Visitor *v, static void iothread_get_poll_param(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { + IOThreadParamInfo *info = opaque; - iothread_get_param(obj, v, name, opaque, errp); + iothread_get_param(obj, v, name, info, errp); } static void iothread_set_poll_param(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { IOThread *iothread = IOTHREAD(obj); + IOThreadParamInfo *info = opaque; - if (!iothread_set_param(obj, v, name, opaque, errp)) { + if (!iothread_set_param(obj, v, name, info, errp)) { return; } @@ -291,16 +291,18 @@ static void iothread_set_poll_param(Object *obj, Visitor *v, static void iothread_get_aio_param(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { + IOThreadParamInfo *info = opaque; - iothread_get_param(obj, v, name, opaque, errp); + iothread_get_param(obj, v, name, info, errp); } static void iothread_set_aio_param(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { IOThread *iothread = IOTHREAD(obj); + IOThreadParamInfo *info = opaque; - if (!iothread_set_param(obj, v, name, opaque, errp)) { + if (!iothread_set_param(obj, v, name, info, errp)) { return; } From 7437b13eacfd05ed6817c2f05c4712ed618544e1 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:41 +0100 Subject: [PATCH 0367/1334] macfb: handle errors that occur during realize Make sure any errors that occur within the macfb realize chain are detected and handled correctly to prevent crashes and to ensure that error messages are reported back to the user. Signed-off-by: Mark Cave-Ayland Reviewed-by: BALATON Zoltan Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 76808b69cc..2b747a8de8 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -379,6 +379,10 @@ static void macfb_sysbus_realize(DeviceState *dev, Error **errp) MacfbState *ms = &s->macfb; macfb_common_realize(dev, ms, errp); + if (*errp) { + return; + } + sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_ctrl); sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram); } @@ -391,8 +395,15 @@ static void macfb_nubus_realize(DeviceState *dev, Error **errp) MacfbState *ms = &s->macfb; ndc->parent_realize(dev, errp); + if (*errp) { + return; + } macfb_common_realize(dev, ms, errp); + if (*errp) { + return; + } + memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl); memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram); } From c047862acd502c305fbdc66a4a3fd717c04fa6d2 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:42 +0100 Subject: [PATCH 0368/1334] macfb: update macfb.c to use the Error API best practices As per the current Error API best practices, change macfb_commom_realize() to return a boolean indicating success to reduce errp boiler-plate handling code. Note that memory_region_init_ram_nomigrate() is also updated to use &error_abort to indicate a non-recoverable error, matching the behaviour recommended after similar discussions on memory API failures for the recent nubus changes. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 2b747a8de8..2ec25c5d6f 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -343,14 +343,14 @@ static const GraphicHwOps macfb_ops = { .gfx_update = macfb_update_display, }; -static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) +static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) { DisplaySurface *surface; if (s->depth != 1 && s->depth != 2 && s->depth != 4 && s->depth != 8 && s->depth != 16 && s->depth != 24) { error_setg(errp, "unknown guest depth %d", s->depth); - return; + return false; } s->con = graphic_console_init(dev, 0, &macfb_ops, s); @@ -359,18 +359,20 @@ static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) if (surface_bits_per_pixel(surface) != 32) { error_setg(errp, "unknown host depth %d", surface_bits_per_pixel(surface)); - return; + return false; } memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &macfb_ctrl_ops, s, "macfb-ctrl", 0x1000); memory_region_init_ram_nomigrate(&s->mem_vram, OBJECT(s), "macfb-vram", - MACFB_VRAM_SIZE, errp); + MACFB_VRAM_SIZE, &error_abort); s->vram = memory_region_get_ram_ptr(&s->mem_vram); s->vram_bit_mask = MACFB_VRAM_SIZE - 1; vmstate_register_ram(&s->mem_vram, dev); memory_region_set_coalescing(&s->mem_vram); + + return true; } static void macfb_sysbus_realize(DeviceState *dev, Error **errp) @@ -378,8 +380,7 @@ static void macfb_sysbus_realize(DeviceState *dev, Error **errp) MacfbSysBusState *s = MACFB(dev); MacfbState *ms = &s->macfb; - macfb_common_realize(dev, ms, errp); - if (*errp) { + if (!macfb_common_realize(dev, ms, errp)) { return; } @@ -399,8 +400,7 @@ static void macfb_nubus_realize(DeviceState *dev, Error **errp) return; } - macfb_common_realize(dev, ms, errp); - if (*errp) { + if (!macfb_common_realize(dev, ms, errp)) { return; } From 906c2323f1edf41b1851e7e36231023ee930aa3c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:43 +0100 Subject: [PATCH 0369/1334] macfb: fix invalid object reference in macfb_common_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During realize memory_region_init_ram_nomigrate() is used to initialise the RAM memory region used for the framebuffer but the owner object reference is incorrect since MacFbState is a typedef and not a QOM type. Change the memory region owner to be the corresponding DeviceState to fix the issue and prevent random crashes during macfb_common_realize(). Signed-off-by: Mark Cave-Ayland Fixes: 8ac919a0654 ("hw/m68k: add Nubus macfb video card") Reviewed-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-4-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 2ec25c5d6f..b363bab889 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -365,7 +365,7 @@ static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &macfb_ctrl_ops, s, "macfb-ctrl", 0x1000); - memory_region_init_ram_nomigrate(&s->mem_vram, OBJECT(s), "macfb-vram", + memory_region_init_ram_nomigrate(&s->mem_vram, OBJECT(dev), "macfb-vram", MACFB_VRAM_SIZE, &error_abort); s->vram = memory_region_get_ram_ptr(&s->mem_vram); s->vram_bit_mask = MACFB_VRAM_SIZE - 1; From 14d0ddfce781374b7ce40062c0afc2ed00419267 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:44 +0100 Subject: [PATCH 0370/1334] macfb: fix overflow of color_palette array The palette_current index counter has a maximum size of 256 * 3 to cover a full color palette of 256 RGB entries. Linux assumes that the palette_current index wraps back around to zero after writing 256 RGB entries so ensure that palette_current is reset at this point to prevent data corruption within MacfbState. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-5-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index b363bab889..39dab49026 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -303,7 +303,9 @@ static void macfb_ctrl_write(void *opaque, s->palette_current = 0; break; case DAFB_LUT: - s->color_palette[s->palette_current++] = val; + s->color_palette[s->palette_current] = val; + s->palette_current = (s->palette_current + 1) % + ARRAY_SIZE(s->color_palette); if (s->palette_current % 3) { macfb_invalidate_display(s); } From 3b10b5673c902981129d1817fcc235e467648200 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:45 +0100 Subject: [PATCH 0371/1334] macfb: use memory_region_init_ram() in macfb_common_realize() for the framebuffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently macfb_common_realize() defines the framebuffer RAM memory region as being non-migrateable but then immediately registers it for migration. Replace memory_region_init_ram_nomigrate() with memory_region_init_ram() which is clearer and does exactly the same thing. Signed-off-by: Mark Cave-Ayland Reviewed-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-6-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 39dab49026..f88f5a6523 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -367,11 +367,10 @@ static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &macfb_ctrl_ops, s, "macfb-ctrl", 0x1000); - memory_region_init_ram_nomigrate(&s->mem_vram, OBJECT(dev), "macfb-vram", - MACFB_VRAM_SIZE, &error_abort); + memory_region_init_ram(&s->mem_vram, OBJECT(dev), "macfb-vram", + MACFB_VRAM_SIZE, &error_abort); s->vram = memory_region_get_ram_ptr(&s->mem_vram); s->vram_bit_mask = MACFB_VRAM_SIZE - 1; - vmstate_register_ram(&s->mem_vram, dev); memory_region_set_coalescing(&s->mem_vram); return true; From 4ec27073fd0e5a82a87b1122dfdca7a820cb1561 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:46 +0100 Subject: [PATCH 0372/1334] macfb: add trace events for reading and writing the control registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-7-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 8 +++++++- hw/display/trace-events | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index f88f5a6523..1128a51c98 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -20,6 +20,7 @@ #include "qapi/error.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" +#include "trace.h" #define VIDEO_BASE 0x00001000 #define DAFB_BASE 0x00800000 @@ -289,7 +290,10 @@ static uint64_t macfb_ctrl_read(void *opaque, hwaddr addr, unsigned int size) { - return 0; + uint64_t val = 0; + + trace_macfb_ctrl_read(addr, val, size); + return val; } static void macfb_ctrl_write(void *opaque, @@ -311,6 +315,8 @@ static void macfb_ctrl_write(void *opaque, } break; } + + trace_macfb_ctrl_write(addr, val, size); } static const MemoryRegionOps macfb_ctrl_ops = { diff --git a/hw/display/trace-events b/hw/display/trace-events index f03f6655bc..f227de1bb9 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -167,3 +167,7 @@ sm501_disp_ctrl_read(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" sm501_disp_ctrl_write(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" sm501_2d_engine_read(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" sm501_2d_engine_write(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" + +# macfb.c +macfb_ctrl_read(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%"PRIx64 " value 0x%"PRIx64 " size %u" +macfb_ctrl_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%"PRIx64 " value 0x%"PRIx64 " size %u" From e6108b96363bda0704ca69e5dfdb4b07dc589336 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:47 +0100 Subject: [PATCH 0373/1334] macfb: implement mode sense to allow display type to be detected The MacOS toolbox ROM uses the monitor sense to detect the display type and then offer a fixed set of resolutions and colour depths accordingly. Implement the monitor sense using information found in Apple Technical Note HW26: "Macintosh Quadra Built-In Video" along with some local experiments. Since the default configuration is 640 x 480 with 8-bit colour then hardcode the sense register to return MACFB_DISPLAY_VGA for now. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-8-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 117 ++++++++++++++++++++++++++++++++++++- hw/display/trace-events | 2 + include/hw/display/macfb.h | 20 +++++++ 3 files changed, 137 insertions(+), 2 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 1128a51c98..6e485d7aef 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -28,8 +28,66 @@ #define MACFB_PAGE_SIZE 4096 #define MACFB_VRAM_SIZE (4 * MiB) -#define DAFB_RESET 0x200 -#define DAFB_LUT 0x213 +#define DAFB_MODE_SENSE 0x1c +#define DAFB_RESET 0x200 +#define DAFB_LUT 0x213 + + +/* + * Quadra sense codes taken from Apple Technical Note HW26: + * "Macintosh Quadra Built-In Video". The sense codes and + * extended sense codes have different meanings: + * + * Sense: + * bit 2: SENSE2 (pin 10) + * bit 1: SENSE1 (pin 7) + * bit 0: SENSE0 (pin 4) + * + * 0 = pin tied to ground + * 1 = pin unconnected + * + * Extended Sense: + * bit 2: pins 4-10 + * bit 1: pins 10-7 + * bit 0: pins 7-4 + * + * 0 = pins tied together + * 1 = pins unconnected + * + * Reads from the sense register appear to be active low, i.e. a 1 indicates + * that the pin is tied to ground, a 0 indicates the pin is disconnected. + * + * Writes to the sense register appear to activate pulldowns i.e. a 1 enables + * a pulldown on a particular pin. + * + * The MacOS toolbox appears to use a series of reads and writes to first + * determine if extended sense is to be used, and then check which pins are + * tied together in order to determine the display type. + */ + +typedef struct MacFbSense { + uint8_t type; + uint8_t sense; + uint8_t ext_sense; +} MacFbSense; + +static MacFbSense macfb_sense_table[] = { + { MACFB_DISPLAY_APPLE_21_COLOR, 0x0, 0 }, + { MACFB_DISPLAY_APPLE_PORTRAIT, 0x1, 0 }, + { MACFB_DISPLAY_APPLE_12_RGB, 0x2, 0 }, + { MACFB_DISPLAY_APPLE_2PAGE_MONO, 0x3, 0 }, + { MACFB_DISPLAY_NTSC_UNDERSCAN, 0x4, 0 }, + { MACFB_DISPLAY_NTSC_OVERSCAN, 0x4, 0 }, + { MACFB_DISPLAY_APPLE_12_MONO, 0x6, 0 }, + { MACFB_DISPLAY_APPLE_13_RGB, 0x6, 0 }, + { MACFB_DISPLAY_16_COLOR, 0x7, 0x3 }, + { MACFB_DISPLAY_PAL1_UNDERSCAN, 0x7, 0x0 }, + { MACFB_DISPLAY_PAL1_OVERSCAN, 0x7, 0x0 }, + { MACFB_DISPLAY_PAL2_UNDERSCAN, 0x7, 0x6 }, + { MACFB_DISPLAY_PAL2_OVERSCAN, 0x7, 0x6 }, + { MACFB_DISPLAY_VGA, 0x7, 0x5 }, + { MACFB_DISPLAY_SVGA, 0x7, 0x5 }, +}; typedef void macfb_draw_line_func(MacfbState *s, uint8_t *d, uint32_t addr, @@ -253,6 +311,50 @@ static void macfb_invalidate_display(void *opaque) memory_region_set_dirty(&s->mem_vram, 0, MACFB_VRAM_SIZE); } +static uint32_t macfb_sense_read(MacfbState *s) +{ + MacFbSense *macfb_sense; + uint8_t sense; + + macfb_sense = &macfb_sense_table[MACFB_DISPLAY_VGA]; + if (macfb_sense->sense == 0x7) { + /* Extended sense */ + sense = 0; + if (!(macfb_sense->ext_sense & 1)) { + /* Pins 7-4 together */ + if (~s->sense & 3) { + sense = (~s->sense & 7) | 3; + } + } + if (!(macfb_sense->ext_sense & 2)) { + /* Pins 10-7 together */ + if (~s->sense & 6) { + sense = (~s->sense & 7) | 6; + } + } + if (!(macfb_sense->ext_sense & 4)) { + /* Pins 4-10 together */ + if (~s->sense & 5) { + sense = (~s->sense & 7) | 5; + } + } + } else { + /* Normal sense */ + sense = (~macfb_sense->sense & 7) | (~s->sense & 7); + } + + trace_macfb_sense_read(sense); + return sense; +} + +static void macfb_sense_write(MacfbState *s, uint32_t val) +{ + s->sense = val; + + trace_macfb_sense_write(val); + return; +} + static void macfb_update_display(void *opaque) { MacfbState *s = opaque; @@ -290,8 +392,15 @@ static uint64_t macfb_ctrl_read(void *opaque, hwaddr addr, unsigned int size) { + MacfbState *s = opaque; uint64_t val = 0; + switch (addr) { + case DAFB_MODE_SENSE: + val = macfb_sense_read(s); + break; + } + trace_macfb_ctrl_read(addr, val, size); return val; } @@ -303,6 +412,9 @@ static void macfb_ctrl_write(void *opaque, { MacfbState *s = opaque; switch (addr) { + case DAFB_MODE_SENSE: + macfb_sense_write(s, val); + break; case DAFB_RESET: s->palette_current = 0; break; @@ -342,6 +454,7 @@ static const VMStateDescription vmstate_macfb = { .fields = (VMStateField[]) { VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3), VMSTATE_UINT32(palette_current, MacfbState), + VMSTATE_UINT32(sense, MacfbState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/display/trace-events b/hw/display/trace-events index f227de1bb9..30cb460e4d 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -171,3 +171,5 @@ sm501_2d_engine_write(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" # macfb.c macfb_ctrl_read(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%"PRIx64 " value 0x%"PRIx64 " size %u" macfb_ctrl_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%"PRIx64 " value 0x%"PRIx64 " size %u" +macfb_sense_read(uint32_t value) "video sense: 0x%"PRIx32 +macfb_sense_write(uint32_t value) "video sense: 0x%"PRIx32 diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h index 80806b0306..febf4ce0e8 100644 --- a/include/hw/display/macfb.h +++ b/include/hw/display/macfb.h @@ -17,6 +17,24 @@ #include "ui/console.h" #include "qom/object.h" +typedef enum { + MACFB_DISPLAY_APPLE_21_COLOR = 0, + MACFB_DISPLAY_APPLE_PORTRAIT = 1, + MACFB_DISPLAY_APPLE_12_RGB = 2, + MACFB_DISPLAY_APPLE_2PAGE_MONO = 3, + MACFB_DISPLAY_NTSC_UNDERSCAN = 4, + MACFB_DISPLAY_NTSC_OVERSCAN = 5, + MACFB_DISPLAY_APPLE_12_MONO = 6, + MACFB_DISPLAY_APPLE_13_RGB = 7, + MACFB_DISPLAY_16_COLOR = 8, + MACFB_DISPLAY_PAL1_UNDERSCAN = 9, + MACFB_DISPLAY_PAL1_OVERSCAN = 10, + MACFB_DISPLAY_PAL2_UNDERSCAN = 11, + MACFB_DISPLAY_PAL2_OVERSCAN = 12, + MACFB_DISPLAY_VGA = 13, + MACFB_DISPLAY_SVGA = 14, +} MacfbDisplayType; + typedef struct MacfbState { MemoryRegion mem_vram; MemoryRegion mem_ctrl; @@ -28,6 +46,8 @@ typedef struct MacfbState { uint8_t color_palette[256 * 3]; uint32_t width, height; /* in pixels */ uint8_t depth; + + uint32_t sense; } MacfbState; #define TYPE_MACFB "sysbus-macfb" From 4317c518618adcd5d41637c849be17e94cecf003 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:48 +0100 Subject: [PATCH 0374/1334] macfb: add qdev property to specify display type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the available resolutions and colour depths are determined by the attached display type, add a qdev property to allow the display type to be specified. The main resolutions of interest are high resolution 1152x870 with 8-bit colour and SVGA resolution up to 800x600 with 24-bit colour so update the q800 machine to allow high resolution mode if specified and otherwise fall back to SVGA. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-9-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 7 ++++++- hw/m68k/q800.c | 5 +++++ include/hw/display/macfb.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 6e485d7aef..f98bcdec2d 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -316,7 +316,8 @@ static uint32_t macfb_sense_read(MacfbState *s) MacFbSense *macfb_sense; uint8_t sense; - macfb_sense = &macfb_sense_table[MACFB_DISPLAY_VGA]; + assert(s->type < ARRAY_SIZE(macfb_sense_table)); + macfb_sense = &macfb_sense_table[s->type]; if (macfb_sense->sense == 0x7) { /* Extended sense */ sense = 0; @@ -544,6 +545,8 @@ static Property macfb_sysbus_properties[] = { DEFINE_PROP_UINT32("width", MacfbSysBusState, macfb.width, 640), DEFINE_PROP_UINT32("height", MacfbSysBusState, macfb.height, 480), DEFINE_PROP_UINT8("depth", MacfbSysBusState, macfb.depth, 8), + DEFINE_PROP_UINT8("display", MacfbSysBusState, macfb.type, + MACFB_DISPLAY_VGA), DEFINE_PROP_END_OF_LIST(), }; @@ -551,6 +554,8 @@ static Property macfb_nubus_properties[] = { DEFINE_PROP_UINT32("width", MacfbNubusState, macfb.width, 640), DEFINE_PROP_UINT32("height", MacfbNubusState, macfb.height, 480), DEFINE_PROP_UINT8("depth", MacfbNubusState, macfb.depth, 8), + DEFINE_PROP_UINT8("display", MacfbNubusState, macfb.type, + MACFB_DISPLAY_VGA), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 09b3366024..5223b880bc 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -421,6 +421,11 @@ static void q800_init(MachineState *machine) qdev_prop_set_uint32(dev, "width", graphic_width); qdev_prop_set_uint32(dev, "height", graphic_height); qdev_prop_set_uint8(dev, "depth", graphic_depth); + if (graphic_width == 1152 && graphic_height == 870 && graphic_depth == 8) { + qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_APPLE_21_COLOR); + } else { + qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_VGA); + } qdev_realize_and_unref(dev, BUS(nubus), &error_fatal); cs = CPU(cpu); diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h index febf4ce0e8..e95a97ebdc 100644 --- a/include/hw/display/macfb.h +++ b/include/hw/display/macfb.h @@ -46,6 +46,7 @@ typedef struct MacfbState { uint8_t color_palette[256 * 3]; uint32_t width, height; /* in pixels */ uint8_t depth; + uint8_t type; uint32_t sense; } MacfbState; From df8abbbadf743bef6be5543b26f51231285b8923 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:49 +0100 Subject: [PATCH 0375/1334] macfb: add common monitor modes supported by the MacOS toolbox ROM The monitor modes table is found by experimenting with the Monitors Control Panel in MacOS and analysing the reads/writes. From this it can be found that the mode is controlled by writes to the DAFB_MODE_CTRL1 and DAFB_MODE_CTRL2 registers. Implement the first block of DAFB registers as a register array including the existing sense register, the newly discovered control registers above, and also the DAFB_MODE_VADDR1 and DAFB_MODE_VADDR2 registers which are used by NetBSD to determine the current video mode. These experiments also show that the offset of the start of video RAM and the stride can change depending upon the monitor mode, so update macfb_draw_graphic() and both the BI_MAC_VADDR and BI_MAC_VROW bootinfo for the q800 machine accordingly. Finally update macfb_common_realize() so that only the resolution and depth supported by the display type can be specified on the command line, and add an error hint showing the list of supported resolutions and depths if the user tries to specify an invalid display mode. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-10-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 149 +++++++++++++++++++++++++++++++++---- hw/display/trace-events | 1 + hw/m68k/q800.c | 11 ++- include/hw/display/macfb.h | 16 +++- 4 files changed, 156 insertions(+), 21 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index f98bcdec2d..2759fb5e34 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -22,12 +22,16 @@ #include "migration/vmstate.h" #include "trace.h" -#define VIDEO_BASE 0x00001000 +#define VIDEO_BASE 0x0 #define DAFB_BASE 0x00800000 #define MACFB_PAGE_SIZE 4096 #define MACFB_VRAM_SIZE (4 * MiB) +#define DAFB_MODE_VADDR1 0x0 +#define DAFB_MODE_VADDR2 0x4 +#define DAFB_MODE_CTRL1 0x8 +#define DAFB_MODE_CTRL2 0xc #define DAFB_MODE_SENSE 0x1c #define DAFB_RESET 0x200 #define DAFB_LUT 0x213 @@ -89,6 +93,22 @@ static MacFbSense macfb_sense_table[] = { { MACFB_DISPLAY_SVGA, 0x7, 0x5 }, }; +static MacFbMode macfb_mode_table[] = { + { MACFB_DISPLAY_VGA, 1, 0x100, 0x71e, 640, 480, 0x400, 0x1000 }, + { MACFB_DISPLAY_VGA, 2, 0x100, 0x70e, 640, 480, 0x400, 0x1000 }, + { MACFB_DISPLAY_VGA, 4, 0x100, 0x706, 640, 480, 0x400, 0x1000 }, + { MACFB_DISPLAY_VGA, 8, 0x100, 0x702, 640, 480, 0x400, 0x1000 }, + { MACFB_DISPLAY_VGA, 24, 0x100, 0x7ff, 640, 480, 0x1000, 0x1000 }, + { MACFB_DISPLAY_VGA, 1, 0xd0 , 0x70e, 800, 600, 0x340, 0xe00 }, + { MACFB_DISPLAY_VGA, 2, 0xd0 , 0x706, 800, 600, 0x340, 0xe00 }, + { MACFB_DISPLAY_VGA, 4, 0xd0 , 0x702, 800, 600, 0x340, 0xe00 }, + { MACFB_DISPLAY_VGA, 8, 0xd0, 0x700, 800, 600, 0x340, 0xe00 }, + { MACFB_DISPLAY_VGA, 24, 0x340, 0x100, 800, 600, 0xd00, 0xe00 }, + { MACFB_DISPLAY_APPLE_21_COLOR, 1, 0x90, 0x506, 1152, 870, 0x240, 0x80 }, + { MACFB_DISPLAY_APPLE_21_COLOR, 2, 0x90, 0x502, 1152, 870, 0x240, 0x80 }, + { MACFB_DISPLAY_APPLE_21_COLOR, 4, 0x90, 0x500, 1152, 870, 0x240, 0x80 }, + { MACFB_DISPLAY_APPLE_21_COLOR, 8, 0x120, 0x5ff, 1152, 870, 0x480, 0x80 }, +}; typedef void macfb_draw_line_func(MacfbState *s, uint8_t *d, uint32_t addr, int width); @@ -246,7 +266,7 @@ static void macfb_draw_graphic(MacfbState *s) ram_addr_t page; uint32_t v = 0; int y, ymin; - int macfb_stride = (s->depth * s->width + 7) / 8; + int macfb_stride = s->mode->stride; macfb_draw_line_func *macfb_draw_line; switch (s->depth) { @@ -278,7 +298,7 @@ static void macfb_draw_graphic(MacfbState *s) DIRTY_MEMORY_VGA); ymin = -1; - page = 0; + page = s->mode->offset; for (y = 0; y < s->height; y++, page += macfb_stride) { if (macfb_check_dirty(s, snap, page, macfb_stride)) { uint8_t *data_display; @@ -323,25 +343,26 @@ static uint32_t macfb_sense_read(MacfbState *s) sense = 0; if (!(macfb_sense->ext_sense & 1)) { /* Pins 7-4 together */ - if (~s->sense & 3) { - sense = (~s->sense & 7) | 3; + if (~s->regs[DAFB_MODE_SENSE >> 2] & 3) { + sense = (~s->regs[DAFB_MODE_SENSE >> 2] & 7) | 3; } } if (!(macfb_sense->ext_sense & 2)) { /* Pins 10-7 together */ - if (~s->sense & 6) { - sense = (~s->sense & 7) | 6; + if (~s->regs[DAFB_MODE_SENSE >> 2] & 6) { + sense = (~s->regs[DAFB_MODE_SENSE >> 2] & 7) | 6; } } if (!(macfb_sense->ext_sense & 4)) { /* Pins 4-10 together */ - if (~s->sense & 5) { - sense = (~s->sense & 7) | 5; + if (~s->regs[DAFB_MODE_SENSE >> 2] & 5) { + sense = (~s->regs[DAFB_MODE_SENSE >> 2] & 7) | 5; } } } else { /* Normal sense */ - sense = (~macfb_sense->sense & 7) | (~s->sense & 7); + sense = (~macfb_sense->sense & 7) | + (~s->regs[DAFB_MODE_SENSE >> 2] & 7); } trace_macfb_sense_read(sense); @@ -350,12 +371,84 @@ static uint32_t macfb_sense_read(MacfbState *s) static void macfb_sense_write(MacfbState *s, uint32_t val) { - s->sense = val; + s->regs[DAFB_MODE_SENSE >> 2] = val; trace_macfb_sense_write(val); return; } +static void macfb_update_mode(MacfbState *s) +{ + s->width = s->mode->width; + s->height = s->mode->height; + s->depth = s->mode->depth; + + trace_macfb_update_mode(s->width, s->height, s->depth); + macfb_invalidate_display(s); +} + +static void macfb_mode_write(MacfbState *s) +{ + MacFbMode *macfb_mode; + int i; + + for (i = 0; i < ARRAY_SIZE(macfb_mode_table); i++) { + macfb_mode = &macfb_mode_table[i]; + + if (s->type != macfb_mode->type) { + continue; + } + + if ((s->regs[DAFB_MODE_CTRL1 >> 2] & 0xff) == + (macfb_mode->mode_ctrl1 & 0xff) && + (s->regs[DAFB_MODE_CTRL2 >> 2] & 0xff) == + (macfb_mode->mode_ctrl2 & 0xff)) { + s->mode = macfb_mode; + macfb_update_mode(s); + break; + } + } +} + +static MacFbMode *macfb_find_mode(MacfbDisplayType display_type, + uint16_t width, uint16_t height, + uint8_t depth) +{ + MacFbMode *macfb_mode; + int i; + + for (i = 0; i < ARRAY_SIZE(macfb_mode_table); i++) { + macfb_mode = &macfb_mode_table[i]; + + if (display_type == macfb_mode->type && width == macfb_mode->width && + height == macfb_mode->height && depth == macfb_mode->depth) { + return macfb_mode; + } + } + + return NULL; +} + +static gchar *macfb_mode_list(void) +{ + gchar *list = NULL; + gchar *mode; + MacFbMode *macfb_mode; + int i; + + for (i = 0; i < ARRAY_SIZE(macfb_mode_table); i++) { + macfb_mode = &macfb_mode_table[i]; + + mode = g_strdup_printf(" %dx%dx%d\n", macfb_mode->width, + macfb_mode->height, macfb_mode->depth); + list = g_strconcat(mode, list, NULL); + g_free(mode); + } + + return list; +} + + static void macfb_update_display(void *opaque) { MacfbState *s = opaque; @@ -397,6 +490,12 @@ static uint64_t macfb_ctrl_read(void *opaque, uint64_t val = 0; switch (addr) { + case DAFB_MODE_VADDR1: + case DAFB_MODE_VADDR2: + case DAFB_MODE_CTRL1: + case DAFB_MODE_CTRL2: + val = s->regs[addr >> 2]; + break; case DAFB_MODE_SENSE: val = macfb_sense_read(s); break; @@ -413,6 +512,17 @@ static void macfb_ctrl_write(void *opaque, { MacfbState *s = opaque; switch (addr) { + case DAFB_MODE_VADDR1: + case DAFB_MODE_VADDR2: + s->regs[addr >> 2] = val; + break; + case DAFB_MODE_CTRL1 ... DAFB_MODE_CTRL1 + 3: + case DAFB_MODE_CTRL2 ... DAFB_MODE_CTRL2 + 3: + s->regs[addr >> 2] = val; + if (val) { + macfb_mode_write(s); + } + break; case DAFB_MODE_SENSE: macfb_sense_write(s, val); break; @@ -442,7 +552,7 @@ static const MemoryRegionOps macfb_ctrl_ops = { static int macfb_post_load(void *opaque, int version_id) { - macfb_invalidate_display(opaque); + macfb_mode_write(opaque); return 0; } @@ -455,7 +565,7 @@ static const VMStateDescription vmstate_macfb = { .fields = (VMStateField[]) { VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3), VMSTATE_UINT32(palette_current, MacfbState), - VMSTATE_UINT32(sense, MacfbState), + VMSTATE_UINT32_ARRAY(regs, MacfbState, MACFB_NUM_REGS), VMSTATE_END_OF_LIST() } }; @@ -469,9 +579,15 @@ static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) { DisplaySurface *surface; - if (s->depth != 1 && s->depth != 2 && s->depth != 4 && s->depth != 8 && - s->depth != 16 && s->depth != 24) { - error_setg(errp, "unknown guest depth %d", s->depth); + s->mode = macfb_find_mode(s->type, s->width, s->height, s->depth); + if (!s->mode) { + gchar *list; + error_setg(errp, "unknown display mode: width %d, height %d, depth %d", + s->width, s->height, s->depth); + list = macfb_mode_list(); + error_append_hint(errp, "Available modes:\n%s", list); + g_free(list); + return false; } @@ -493,6 +609,7 @@ static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) s->vram_bit_mask = MACFB_VRAM_SIZE - 1; memory_region_set_coalescing(&s->mem_vram); + macfb_update_mode(s); return true; } diff --git a/hw/display/trace-events b/hw/display/trace-events index 30cb460e4d..3a7a2c957f 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -173,3 +173,4 @@ macfb_ctrl_read(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%"PRIx macfb_ctrl_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%"PRIx64 " value 0x%"PRIx64 " size %u" macfb_sense_read(uint32_t value) "video sense: 0x%"PRIx32 macfb_sense_write(uint32_t value) "video sense: 0x%"PRIx32 +macfb_update_mode(uint32_t width, uint32_t height, uint8_t depth) "setting mode to width %"PRId32 " height %"PRId32 " size %d" diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 5223b880bc..df3fd3711e 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -74,7 +74,7 @@ * is needed by the kernel to have early display and * thus provided by the bootloader */ -#define VIDEO_BASE 0xf9001000 +#define VIDEO_BASE 0xf9000000 #define MAC_CLOCK 3686418 @@ -221,6 +221,7 @@ static void q800_init(MachineState *machine) uint8_t *prom; const int io_slice_nb = (IO_SIZE / IO_SLICE) - 1; int i, checksum; + MacFbMode *macfb_mode; ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; @@ -428,6 +429,8 @@ static void q800_init(MachineState *machine) } qdev_realize_and_unref(dev, BUS(nubus), &error_fatal); + macfb_mode = (NUBUS_MACFB(dev)->macfb).mode; + cs = CPU(cpu); if (linux_boot) { uint64_t high; @@ -450,12 +453,12 @@ static void q800_init(MachineState *machine) BOOTINFO1(cs->as, parameters_base, BI_MAC_MEMSIZE, ram_size >> 20); /* in MB */ BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size); - BOOTINFO1(cs->as, parameters_base, BI_MAC_VADDR, VIDEO_BASE); + BOOTINFO1(cs->as, parameters_base, BI_MAC_VADDR, + VIDEO_BASE + macfb_mode->offset); BOOTINFO1(cs->as, parameters_base, BI_MAC_VDEPTH, graphic_depth); BOOTINFO1(cs->as, parameters_base, BI_MAC_VDIM, (graphic_height << 16) | graphic_width); - BOOTINFO1(cs->as, parameters_base, BI_MAC_VROW, - (graphic_width * graphic_depth + 7) / 8); + BOOTINFO1(cs->as, parameters_base, BI_MAC_VROW, macfb_mode->stride); BOOTINFO1(cs->as, parameters_base, BI_MAC_SCCBASE, SCC_BASE); rom = g_malloc(sizeof(*rom)); diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h index e95a97ebdc..0aff0d84d2 100644 --- a/include/hw/display/macfb.h +++ b/include/hw/display/macfb.h @@ -35,6 +35,19 @@ typedef enum { MACFB_DISPLAY_SVGA = 14, } MacfbDisplayType; +typedef struct MacFbMode { + uint8_t type; + uint8_t depth; + uint32_t mode_ctrl1; + uint32_t mode_ctrl2; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t offset; +} MacFbMode; + +#define MACFB_NUM_REGS 8 + typedef struct MacfbState { MemoryRegion mem_vram; MemoryRegion mem_ctrl; @@ -48,7 +61,8 @@ typedef struct MacfbState { uint8_t depth; uint8_t type; - uint32_t sense; + uint32_t regs[MACFB_NUM_REGS]; + MacFbMode *mode; } MacfbState; #define TYPE_MACFB "sysbus-macfb" From 57eeaf44ce6964e6e86275318f5fc35610c1dc68 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:50 +0100 Subject: [PATCH 0376/1334] macfb: fix up 1-bit pixel encoding The MacOS driver expects the RGB values for the pixel to be in entries 0 and 1 of the colour palette. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-11-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 2759fb5e34..e49c8b6f4b 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -128,7 +128,9 @@ static void macfb_draw_line1(MacfbState *s, uint8_t *d, uint32_t addr, for (x = 0; x < width; x++) { int bit = x & 7; int idx = (macfb_read_byte(s, addr) >> (7 - bit)) & 1; - r = g = b = ((1 - idx) << 7); + r = s->color_palette[idx * 3]; + g = s->color_palette[idx * 3 + 1]; + b = s->color_palette[idx * 3 + 2]; addr += (bit == 7); *(uint32_t *)d = rgb_to_pixel32(r, g, b); From 432d59c56ef8a8eaefd847a40a01439e32317bff Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:51 +0100 Subject: [PATCH 0377/1334] macfb: fix 24-bit RGB pixel encoding According to Apple Technical Note HW26: "Macintosh Quadra Built-In Video" the in-built framebuffer encodes each 24-bit pixel into 4 bytes. Adjust the 24-bit RGB pixel encoding accordingly which agrees with the encoding expected by MacOS when changing into 24-bit colour mode. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-12-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index e49c8b6f4b..3288a71b89 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -224,10 +224,10 @@ static void macfb_draw_line24(MacfbState *s, uint8_t *d, uint32_t addr, int x; for (x = 0; x < width; x++) { - r = macfb_read_byte(s, addr); - g = macfb_read_byte(s, addr + 1); - b = macfb_read_byte(s, addr + 2); - addr += 3; + r = macfb_read_byte(s, addr + 1); + g = macfb_read_byte(s, addr + 2); + b = macfb_read_byte(s, addr + 3); + addr += 4; *(uint32_t *)d = rgb_to_pixel32(r, g, b); d += 4; From c7a2f7ba0c736a119d7b530693de0e1b691cdd5a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:52 +0100 Subject: [PATCH 0378/1334] macfb: add vertical blank interrupt The MacOS driver expects a 60.15Hz vertical blank interrupt to be generated by the framebuffer which in turn schedules the mouse driver via the Vertical Retrace Manager. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-13-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 83 ++++++++++++++++++++++++++++++++++++++ include/hw/display/macfb.h | 8 ++++ 2 files changed, 91 insertions(+) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 3288a71b89..4b352eb89c 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -33,9 +33,16 @@ #define DAFB_MODE_CTRL1 0x8 #define DAFB_MODE_CTRL2 0xc #define DAFB_MODE_SENSE 0x1c +#define DAFB_INTR_MASK 0x104 +#define DAFB_INTR_STAT 0x108 +#define DAFB_INTR_CLEAR 0x10c #define DAFB_RESET 0x200 #define DAFB_LUT 0x213 +#define DAFB_INTR_VBL 0x4 + +/* Vertical Blank period (60.15Hz) */ +#define DAFB_INTR_VBL_PERIOD_NS 16625800 /* * Quadra sense codes taken from Apple Technical Note HW26: @@ -470,6 +477,36 @@ static void macfb_update_display(void *opaque) macfb_draw_graphic(s); } +static void macfb_update_irq(MacfbState *s) +{ + uint32_t irq_state = s->irq_state & s->irq_mask; + + if (irq_state) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static int64_t macfb_next_vbl(void) +{ + return (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + DAFB_INTR_VBL_PERIOD_NS) / + DAFB_INTR_VBL_PERIOD_NS * DAFB_INTR_VBL_PERIOD_NS; +} + +static void macfb_vbl_timer(void *opaque) +{ + MacfbState *s = opaque; + int64_t next_vbl; + + s->irq_state |= DAFB_INTR_VBL; + macfb_update_irq(s); + + /* 60 Hz irq */ + next_vbl = macfb_next_vbl(); + timer_mod(s->vbl_timer, next_vbl); +} + static void macfb_reset(MacfbState *s) { int i; @@ -498,6 +535,9 @@ static uint64_t macfb_ctrl_read(void *opaque, case DAFB_MODE_CTRL2: val = s->regs[addr >> 2]; break; + case DAFB_INTR_STAT: + val = s->irq_state; + break; case DAFB_MODE_SENSE: val = macfb_sense_read(s); break; @@ -513,6 +553,8 @@ static void macfb_ctrl_write(void *opaque, unsigned int size) { MacfbState *s = opaque; + int64_t next_vbl; + switch (addr) { case DAFB_MODE_VADDR1: case DAFB_MODE_VADDR2: @@ -528,8 +570,23 @@ static void macfb_ctrl_write(void *opaque, case DAFB_MODE_SENSE: macfb_sense_write(s, val); break; + case DAFB_INTR_MASK: + s->irq_mask = val; + if (val & DAFB_INTR_VBL) { + next_vbl = macfb_next_vbl(); + timer_mod(s->vbl_timer, next_vbl); + } else { + timer_del(s->vbl_timer); + } + break; + case DAFB_INTR_CLEAR: + s->irq_state &= ~DAFB_INTR_VBL; + macfb_update_irq(s); + break; case DAFB_RESET: s->palette_current = 0; + s->irq_state &= ~DAFB_INTR_VBL; + macfb_update_irq(s); break; case DAFB_LUT: s->color_palette[s->palette_current] = val; @@ -611,6 +668,7 @@ static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) s->vram_bit_mask = MACFB_VRAM_SIZE - 1; memory_region_set_coalescing(&s->mem_vram); + s->vbl_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, macfb_vbl_timer, s); macfb_update_mode(s); return true; } @@ -626,6 +684,16 @@ static void macfb_sysbus_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_ctrl); sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram); + + qdev_init_gpio_out(dev, &ms->irq, 1); +} + +static void macfb_nubus_set_irq(void *opaque, int n, int level) +{ + MacfbNubusState *s = NUBUS_MACFB(opaque); + NubusDevice *nd = NUBUS_DEVICE(s); + + nubus_set_irq(nd, level); } static void macfb_nubus_realize(DeviceState *dev, Error **errp) @@ -646,6 +714,19 @@ static void macfb_nubus_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl); memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram); + + ms->irq = qemu_allocate_irq(macfb_nubus_set_irq, s, 0); +} + +static void macfb_nubus_unrealize(DeviceState *dev) +{ + MacfbNubusState *s = NUBUS_MACFB(dev); + MacfbNubusDeviceClass *ndc = NUBUS_MACFB_GET_CLASS(dev); + MacfbState *ms = &s->macfb; + + ndc->parent_unrealize(dev); + + qemu_free_irq(ms->irq); } static void macfb_sysbus_reset(DeviceState *d) @@ -696,6 +777,8 @@ static void macfb_nubus_class_init(ObjectClass *klass, void *data) device_class_set_parent_realize(dc, macfb_nubus_realize, &ndc->parent_realize); + device_class_set_parent_unrealize(dc, macfb_nubus_unrealize, + &ndc->parent_unrealize); dc->desc = "Nubus Macintosh framebuffer"; dc->reset = macfb_nubus_reset; dc->vmsd = &vmstate_macfb; diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h index 0aff0d84d2..e52775aa21 100644 --- a/include/hw/display/macfb.h +++ b/include/hw/display/macfb.h @@ -14,7 +14,9 @@ #define MACFB_H #include "exec/memory.h" +#include "hw/irq.h" #include "ui/console.h" +#include "qemu/timer.h" #include "qom/object.h" typedef enum { @@ -63,6 +65,11 @@ typedef struct MacfbState { uint32_t regs[MACFB_NUM_REGS]; MacFbMode *mode; + + uint32_t irq_state; + uint32_t irq_mask; + QEMUTimer *vbl_timer; + qemu_irq irq; } MacfbState; #define TYPE_MACFB "sysbus-macfb" @@ -81,6 +88,7 @@ struct MacfbNubusDeviceClass { DeviceClass parent_class; DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; }; From efd0c37edc8efe7dccc2356f4a07f33581bc9e67 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Oct 2021 23:12:53 +0100 Subject: [PATCH 0379/1334] q800: wire macfb IRQ to separate video interrupt on VIA2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whilst the in-built Quadra 800 framebuffer exists within the Nubus address space for slot 9, it has its own dedicated interrupt on VIA2. Force the macfb device to occupy slot 9 in the q800 machine and wire its IRQ to the separate video interrupt since this is what is expected by the MacOS interrupt handler. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20211007221253.29024-14-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index df3fd3711e..fd4855047e 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -407,8 +407,10 @@ static void q800_init(MachineState *machine) MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE); - - for (i = 0; i < VIA2_NUBUS_IRQ_NB; i++) { + qdev_connect_gpio_out(dev, 9, + qdev_get_gpio_in_named(via2_dev, "nubus-irq", + VIA2_NUBUS_IRQ_INTVIDEO)); + for (i = 1; i < VIA2_NUBUS_IRQ_NB; i++) { qdev_connect_gpio_out(dev, 9 + i, qdev_get_gpio_in_named(via2_dev, "nubus-irq", VIA2_NUBUS_IRQ_9 + i)); @@ -419,6 +421,7 @@ static void q800_init(MachineState *machine) /* framebuffer in nubus slot #9 */ dev = qdev_new(TYPE_NUBUS_MACFB); + qdev_prop_set_uint32(dev, "slot", 9); qdev_prop_set_uint32(dev, "width", graphic_width); qdev_prop_set_uint32(dev, "height", graphic_height); qdev_prop_set_uint8(dev, "depth", graphic_depth); From 45a904af3861b5457050e55271e1386a7040f04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:07 +0200 Subject: [PATCH 0380/1334] aspeed/smc: Add watchdog Control/Status Registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Aspeed SoCs have a dual boot function for firmware fail-over recovery. The system auto-reboots from the second flash if the main flash does not boot successfully within a certain amount of time. This function is called alternate boot (ABR) in the FMC controllers. On AST2400/AST2500, ABR is enabled by hardware strapping in SCU70 to enable the 2nd watchdog timer, on AST2600, through register SCU510. If the boot on the the main flash succeeds, the firmware should disable the 2nd watchdog timer. If not, the BMC is reset and the CE0 and CE1 mappings are swapped to restart the BMC from the 2nd flash. On the AST2600, the ABR registers controlling the 2nd watchdog timer were moved from the watchdog register to the FMC controller and the FMC model should be able to control WDT2 through its own register set. This requires more work. For now, add dummy read/write handlers to let the FW disable the 2nd watchdog without error. Reviewed-by: Peter Delevoryas Reported-by: Peter Delevoryas Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 331a2c5446..715f85007d 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -124,6 +124,13 @@ /* SPI dummy cycle data */ #define R_DUMMY_DATA (0x54 / 4) +/* FMC_WDT2 Control/Status Register for Alternate Boot (AST2600) */ +#define R_FMC_WDT2_CTRL (0x64 / 4) +#define FMC_WDT2_CTRL_ALT_BOOT_MODE BIT(6) /* O: 2 chips 1: 1 chip */ +#define FMC_WDT2_CTRL_SINGLE_BOOT_MODE BIT(5) +#define FMC_WDT2_CTRL_BOOT_SOURCE BIT(4) /* O: primary 1: alternate */ +#define FMC_WDT2_CTRL_EN BIT(0) + /* DMA Control/Status Register */ #define R_DMA_CTRL (0x80 / 4) #define DMA_CTRL_REQUEST (1 << 31) @@ -263,12 +270,18 @@ static void aspeed_2600_smc_dma_ctrl(AspeedSMCState *s, uint32_t value); #define ASPEED_SMC_FEATURE_DMA 0x1 #define ASPEED_SMC_FEATURE_DMA_GRANT 0x2 +#define ASPEED_SMC_FEATURE_WDT_CONTROL 0x4 static inline bool aspeed_smc_has_dma(const AspeedSMCState *s) { return !!(s->ctrl->features & ASPEED_SMC_FEATURE_DMA); } +static inline bool aspeed_smc_has_wdt_control(const AspeedSMCState *s) +{ + return !!(s->ctrl->features & ASPEED_SMC_FEATURE_WDT_CONTROL); +} + static const AspeedSMCController controllers[] = { { .name = "aspeed.smc-ast2400", @@ -388,7 +401,8 @@ static const AspeedSMCController controllers[] = { .segments = aspeed_segments_ast2600_fmc, .flash_window_base = ASPEED26_SOC_FMC_FLASH_BASE, .flash_window_size = 0x10000000, - .features = ASPEED_SMC_FEATURE_DMA, + .features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_WDT_CONTROL, .dma_flash_mask = 0x0FFFFFFC, .dma_dram_mask = 0x3FFFFFFC, .nregs = ASPEED_SMC_R_MAX, @@ -1019,6 +1033,7 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) addr == R_CE_CMD_CTRL || addr == R_INTR_CTRL || addr == R_DUMMY_DATA || + (aspeed_smc_has_wdt_control(s) && addr == R_FMC_WDT2_CTRL) || (aspeed_smc_has_dma(s) && addr == R_DMA_CTRL) || (aspeed_smc_has_dma(s) && addr == R_DMA_FLASH_ADDR) || (aspeed_smc_has_dma(s) && addr == R_DMA_DRAM_ADDR) || @@ -1350,6 +1365,8 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, s->regs[addr] = value & 0xff; } else if (addr == R_DUMMY_DATA) { s->regs[addr] = value & 0xff; + } else if (aspeed_smc_has_wdt_control(s) && addr == R_FMC_WDT2_CTRL) { + s->regs[addr] = value & FMC_WDT2_CTRL_EN; } else if (addr == R_INTR_CTRL) { s->regs[addr] = value; } else if (aspeed_smc_has_dma(s) && addr == R_DMA_CTRL) { From 32c54bd0ed622bbd1614ca9d1f5f108823f5cc11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:07 +0200 Subject: [PATCH 0381/1334] aspeed/smc: Introduce aspeed_smc_error() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It unifies the errors reported by the Aspeed SMC model and also removes some use of ctrl->name which will help us for the next patches. Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 97 +++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 52 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 715f85007d..def1cb4c74 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -513,6 +513,9 @@ static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s, } } +#define aspeed_smc_error(fmt, ...) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__) + static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, const AspeedSegments *new, int cs) @@ -529,11 +532,11 @@ static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, 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); + aspeed_smc_error("new segment CS%d [ 0x%" + HWADDR_PRIx" - 0x%"HWADDR_PRIx" ] overlaps with " + "CS%d [ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]", + cs, new->addr, new->addr + new->size, + i, seg.addr, seg.addr + seg.size); return true; } } @@ -568,9 +571,8 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, /* 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); + aspeed_smc_error("Tried to change CS0 start address to 0x%" + HWADDR_PRIx, seg.addr); seg.addr = s->ctrl->flash_window_base; new = s->ctrl->segment_to_reg(s, &seg); } @@ -584,9 +586,8 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, cs == s->ctrl->max_peripherals && 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 + seg.size); + aspeed_smc_error("Tried to change CS%d end address to 0x%" + HWADDR_PRIx, cs, seg.addr + seg.size); seg.size = s->ctrl->segments[cs].addr + s->ctrl->segments[cs].size - seg.addr; new = s->ctrl->segment_to_reg(s, &seg); @@ -596,17 +597,17 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, if (seg.size && (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); + aspeed_smc_error("new segment for CS%d is invalid : " + "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]", + cs, seg.addr, seg.addr + seg.size); return; } /* Check start address vs. alignment */ if (seg.size && !QEMU_IS_ALIGNED(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); + aspeed_smc_error("new segment for CS%d is not " + "aligned : [ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]", + cs, seg.addr, seg.addr + seg.size); } /* And segments should not overlap (in the specs) */ @@ -619,16 +620,15 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr, unsigned size) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u" - PRIx64 "\n", __func__, addr, size); + aspeed_smc_error("To 0x%" HWADDR_PRIx " of size %u" PRIx64, addr, size); return 0; } static void aspeed_smc_flash_default_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u: 0x%" - PRIx64 "\n", __func__, addr, size, data); + aspeed_smc_error("To 0x%" HWADDR_PRIx " of size %u: 0x%" PRIx64, + addr, size, data); } static const MemoryRegionOps aspeed_smc_flash_default_ops = { @@ -671,8 +671,8 @@ static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl) } if (!cmd) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: no command defined for mode %d\n", - __func__, aspeed_smc_flash_mode(fl)); + aspeed_smc_error("no command defined for mode %d", + aspeed_smc_flash_mode(fl)); } return cmd; @@ -716,11 +716,9 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, s->ctrl->reg_to_segment(s, s->regs[R_SEG_ADDR0 + fl->id], &seg); if ((addr % seg.size) != addr) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid address 0x%08x for CS%d segment : " - "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]\n", - s->ctrl->name, addr, fl->id, seg.addr, - seg.addr + seg.size); + aspeed_smc_error("invalid address 0x%08x for CS%d segment : " + "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]", + addr, fl->id, seg.addr, seg.addr + seg.size); addr %= seg.size; } @@ -796,8 +794,7 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) aspeed_smc_flash_unselect(fl); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid flash mode %d\n", - __func__, aspeed_smc_flash_mode(fl)); + aspeed_smc_error("invalid flash mode %d", aspeed_smc_flash_mode(fl)); } trace_aspeed_smc_flash_read(fl->id, addr, size, ret, @@ -914,8 +911,7 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, aspeed_smc_flash_mode(fl)); if (!aspeed_smc_is_writable(fl)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%" - HWADDR_PRIx "\n", __func__, addr); + aspeed_smc_error("flash is not writable at 0x%" HWADDR_PRIx, addr); return; } @@ -940,8 +936,7 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, aspeed_smc_flash_unselect(fl); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid flash mode %d\n", - __func__, aspeed_smc_flash_mode(fl)); + aspeed_smc_error("invalid flash mode %d", aspeed_smc_flash_mode(fl)); } } @@ -1067,7 +1062,7 @@ static uint8_t aspeed_smc_hclk_divisor(uint8_t hclk_mask) } } - qemu_log_mask(LOG_GUEST_ERROR, "invalid HCLK mask %x", hclk_mask); + aspeed_smc_error("invalid HCLK mask %x", hclk_mask); return 0; } @@ -1147,8 +1142,7 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) uint32_t data; if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid direction for DMA checksum\n", __func__); + aspeed_smc_error("invalid direction for DMA checksum"); return; } @@ -1160,8 +1154,8 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) data = address_space_ldl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR], MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash read failed @%08x\n", - __func__, s->regs[R_DMA_FLASH_ADDR]); + aspeed_smc_error("Flash read failed @%08x", + s->regs[R_DMA_FLASH_ADDR]); return; } trace_aspeed_smc_dma_checksum(s->regs[R_DMA_FLASH_ADDR], data); @@ -1196,32 +1190,32 @@ static void aspeed_smc_dma_rw(AspeedSMCState *s) data = address_space_ldl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR], MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM read failed @%08x\n", - __func__, s->regs[R_DMA_DRAM_ADDR]); + aspeed_smc_error("DRAM read failed @%08x", + s->regs[R_DMA_DRAM_ADDR]); return; } address_space_stl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR], data, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash write failed @%08x\n", - __func__, s->regs[R_DMA_FLASH_ADDR]); + aspeed_smc_error("Flash write failed @%08x", + s->regs[R_DMA_FLASH_ADDR]); return; } } else { data = address_space_ldl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR], MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash read failed @%08x\n", - __func__, s->regs[R_DMA_FLASH_ADDR]); + aspeed_smc_error("Flash read failed @%08x", + s->regs[R_DMA_FLASH_ADDR]); return; } address_space_stl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR], data, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM write failed @%08x\n", - __func__, s->regs[R_DMA_DRAM_ADDR]); + aspeed_smc_error("DRAM write failed @%08x", + s->regs[R_DMA_DRAM_ADDR]); return; } } @@ -1281,7 +1275,7 @@ static void aspeed_smc_dma_ctrl(AspeedSMCState *s, uint32_t dma_ctrl) } if (aspeed_smc_dma_in_progress(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA in progress\n", __func__); + aspeed_smc_error("DMA in progress !"); return; } @@ -1303,7 +1297,7 @@ static inline bool aspeed_smc_dma_granted(AspeedSMCState *s) } if (!(s->regs[R_DMA_CTRL] & DMA_CTRL_GRANT)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA not granted\n", __func__); + aspeed_smc_error("DMA not granted"); return false; } @@ -1328,7 +1322,7 @@ static void aspeed_2600_smc_dma_ctrl(AspeedSMCState *s, uint32_t dma_ctrl) } if (!aspeed_smc_dma_granted(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA not granted\n", __func__); + aspeed_smc_error("DMA not granted"); return; } @@ -1434,8 +1428,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) /* Enforce some real HW limits */ if (s->num_cs > s->ctrl->max_peripherals) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: num_cs cannot exceed: %d\n", - __func__, s->ctrl->max_peripherals); + aspeed_smc_error("num_cs cannot exceed: %d", s->ctrl->max_peripherals); s->num_cs = s->ctrl->max_peripherals; } From d0180a3ae48f71d4cb5b9cea89b49451e2dc9a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0382/1334] aspeed/smc: Stop using the model name for the memory regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no real reason to use this name. It's simply nice to have in the monitor output but it's a burden for the following patch which removes the AspeedSMCController structure describing the controller. Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index def1cb4c74..612040493c 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1392,20 +1392,15 @@ static const MemoryRegionOps aspeed_smc_ops = { */ static void aspeed_smc_dma_setup(AspeedSMCState *s, Error **errp) { - char *name; - if (!s->dram_mr) { error_setg(errp, TYPE_ASPEED_SMC ": 'dram' link not set"); return; } - name = g_strdup_printf("%s-dma-flash", s->ctrl->name); - address_space_init(&s->flash_as, &s->mmio_flash, name); - g_free(name); - - name = g_strdup_printf("%s-dma-dram", s->ctrl->name); - address_space_init(&s->dram_as, s->dram_mr, name); - g_free(name); + address_space_init(&s->flash_as, &s->mmio_flash, + TYPE_ASPEED_SMC ".dma-flash"); + address_space_init(&s->dram_as, s->dram_mr, + TYPE_ASPEED_SMC ".dma-dram"); } static void aspeed_smc_realize(DeviceState *dev, Error **errp) @@ -1446,7 +1441,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) /* The memory region for the controller registers */ memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s, - s->ctrl->name, s->ctrl->nregs * 4); + TYPE_ASPEED_SMC, s->ctrl->nregs * 4); sysbus_init_mmio(sbd, &s->mmio); /* @@ -1454,12 +1449,12 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) * 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, + &aspeed_smc_flash_default_ops, s, + TYPE_ASPEED_SMC ".flash", s->ctrl->flash_window_size); - memory_region_init_alias(&s->mmio_flash_alias, OBJECT(s), name, + memory_region_init_alias(&s->mmio_flash_alias, OBJECT(s), + TYPE_ASPEED_SMC ".flash", &s->mmio_flash, 0, s->ctrl->flash_window_size); sysbus_init_mmio(sbd, &s->mmio_flash_alias); @@ -1475,7 +1470,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->ctrl->max_peripherals; ++i) { AspeedSMCFlash *fl = &s->flashes[i]; - snprintf(name, sizeof(name), "%s.%d", s->ctrl->name, i); + snprintf(name, sizeof(name), TYPE_ASPEED_SMC ".flash.%d", i); fl->id = i; fl->controller = s; From 30b6852ce4c3398c54fc6f6c7ff5ccbf8c15cf4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0383/1334] aspeed/smc: Drop AspeedSMCController structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The characteristics of the Aspeed controllers are described in a AspeedSMCController structure which is redundant with the AspeedSMCClass. Move all attributes under the class and adapt the code to use class attributes instead. This is a large change but it is functionally equivalent. Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 4 +- hw/arm/aspeed_soc.c | 4 +- hw/ssi/aspeed_smc.c | 861 ++++++++++++++++++++---------------- include/hw/ssi/aspeed_smc.h | 64 ++- 4 files changed, 511 insertions(+), 422 deletions(-) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 9d70e8e060..c69f27dff6 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -352,7 +352,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, - s->fmc.ctrl->flash_window_base); + ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -367,7 +367,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, - s->spi[i].ctrl->flash_window_base); + ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } /* EHCI */ diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index ed84502e23..4f013dd5cd 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -310,7 +310,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, - s->fmc.ctrl->flash_window_base); + ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -323,7 +323,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, - s->spi[i].ctrl->flash_window_base); + ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } /* EHCI */ diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 612040493c..5466be6317 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -169,11 +169,6 @@ #define ASPEED_SMC_R_SPI_MAX (0x20 / 4) #define ASPEED_SMC_R_SMC_MAX (0x20 / 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 - /* * DMA DRAM addresses should be 4 bytes aligned and the valid address * range is 0x40000000 - 0x5FFFFFFF (AST2400) @@ -186,8 +181,8 @@ * 0: 4 bytes * 0x7FFFFF: 32M bytes */ -#define DMA_DRAM_ADDR(s, val) ((val) & (s)->ctrl->dma_dram_mask) -#define DMA_FLASH_ADDR(s, val) ((val) & (s)->ctrl->dma_flash_mask) +#define DMA_DRAM_ADDR(asc, val) ((val) & (asc)->dma_dram_mask) +#define DMA_FLASH_ADDR(asc, val) ((val) & (asc)->dma_flash_mask) #define DMA_LENGTH(val) ((val) & 0x01FFFFFC) /* Flash opcodes. */ @@ -201,316 +196,25 @@ * controller. These can be changed when board is initialized with the * Segment Address Registers. */ -static const AspeedSegments aspeed_segments_legacy[] = { - { 0x10000000, 32 * 1024 * 1024 }, -}; - -static const AspeedSegments aspeed_segments_fmc[] = { - { 0x20000000, 64 * 1024 * 1024 }, /* start address is readonly */ - { 0x24000000, 32 * 1024 * 1024 }, - { 0x26000000, 32 * 1024 * 1024 }, - { 0x28000000, 32 * 1024 * 1024 }, - { 0x2A000000, 32 * 1024 * 1024 } -}; - -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 uint32_t aspeed_smc_segment_to_reg(const AspeedSMCState *s, - const AspeedSegments *seg); -static void aspeed_smc_reg_to_segment(const AspeedSMCState *s, uint32_t reg, - AspeedSegments *seg); -static void aspeed_smc_dma_ctrl(AspeedSMCState *s, uint32_t value); - -/* - * AST2600 definitions - */ -#define ASPEED26_SOC_FMC_FLASH_BASE 0x20000000 -#define ASPEED26_SOC_SPI_FLASH_BASE 0x30000000 -#define ASPEED26_SOC_SPI2_FLASH_BASE 0x50000000 - -static const AspeedSegments aspeed_segments_ast2600_fmc[] = { - { 0x0, 128 * MiB }, /* start address is readonly */ - { 128 * MiB, 128 * MiB }, /* default is disabled but needed for -kernel */ - { 0x0, 0 }, /* disabled */ -}; - -static const AspeedSegments aspeed_segments_ast2600_spi1[] = { - { 0x0, 128 * MiB }, /* start address is readonly */ - { 0x0, 0 }, /* disabled */ -}; - -static const AspeedSegments aspeed_segments_ast2600_spi2[] = { - { 0x0, 128 * MiB }, /* start address is readonly */ - { 0x0, 0 }, /* disabled */ - { 0x0, 0 }, /* disabled */ -}; - -static uint32_t aspeed_2600_smc_segment_to_reg(const AspeedSMCState *s, - const AspeedSegments *seg); -static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s, - uint32_t reg, AspeedSegments *seg); -static void aspeed_2600_smc_dma_ctrl(AspeedSMCState *s, uint32_t value); +static const AspeedSegments aspeed_2400_fmc_segments[]; +static const AspeedSegments aspeed_2400_spi1_segments[]; +static const AspeedSegments aspeed_2500_fmc_segments[]; +static const AspeedSegments aspeed_2500_spi1_segments[]; +static const AspeedSegments aspeed_2500_spi2_segments[]; +static const AspeedSegments aspeed_2600_fmc_segments[]; #define ASPEED_SMC_FEATURE_DMA 0x1 #define ASPEED_SMC_FEATURE_DMA_GRANT 0x2 #define ASPEED_SMC_FEATURE_WDT_CONTROL 0x4 -static inline bool aspeed_smc_has_dma(const AspeedSMCState *s) +static inline bool aspeed_smc_has_dma(const AspeedSMCClass *asc) { - return !!(s->ctrl->features & ASPEED_SMC_FEATURE_DMA); + return !!(asc->features & ASPEED_SMC_FEATURE_DMA); } -static inline bool aspeed_smc_has_wdt_control(const AspeedSMCState *s) +static inline bool aspeed_smc_has_wdt_control(const AspeedSMCClass *asc) { - return !!(s->ctrl->features & ASPEED_SMC_FEATURE_WDT_CONTROL); -} - -static const AspeedSMCController controllers[] = { - { - .name = "aspeed.smc-ast2400", - .r_conf = R_CONF, - .r_ce_ctrl = R_CE_CTRL, - .r_ctrl0 = R_CTRL0, - .r_timings = R_TIMINGS, - .nregs_timings = 1, - .conf_enable_w0 = CONF_ENABLE_W0, - .max_peripherals = 1, - .segments = aspeed_segments_legacy, - .flash_window_base = ASPEED_SOC_SMC_FLASH_BASE, - .flash_window_size = 0x6000000, - .features = 0x0, - .nregs = ASPEED_SMC_R_SMC_MAX, - .segment_to_reg = aspeed_smc_segment_to_reg, - .reg_to_segment = aspeed_smc_reg_to_segment, - .dma_ctrl = aspeed_smc_dma_ctrl, - }, { - .name = "aspeed.fmc-ast2400", - .r_conf = R_CONF, - .r_ce_ctrl = R_CE_CTRL, - .r_ctrl0 = R_CTRL0, - .r_timings = R_TIMINGS, - .nregs_timings = 1, - .conf_enable_w0 = CONF_ENABLE_W0, - .max_peripherals = 5, - .segments = aspeed_segments_fmc, - .flash_window_base = ASPEED_SOC_FMC_FLASH_BASE, - .flash_window_size = 0x10000000, - .features = ASPEED_SMC_FEATURE_DMA, - .dma_flash_mask = 0x0FFFFFFC, - .dma_dram_mask = 0x1FFFFFFC, - .nregs = ASPEED_SMC_R_MAX, - .segment_to_reg = aspeed_smc_segment_to_reg, - .reg_to_segment = aspeed_smc_reg_to_segment, - .dma_ctrl = aspeed_smc_dma_ctrl, - }, { - .name = "aspeed.spi1-ast2400", - .r_conf = R_SPI_CONF, - .r_ce_ctrl = 0xff, - .r_ctrl0 = R_SPI_CTRL0, - .r_timings = R_SPI_TIMINGS, - .nregs_timings = 1, - .conf_enable_w0 = SPI_CONF_ENABLE_W0, - .max_peripherals = 1, - .segments = aspeed_segments_spi, - .flash_window_base = ASPEED_SOC_SPI_FLASH_BASE, - .flash_window_size = 0x10000000, - .features = 0x0, - .nregs = ASPEED_SMC_R_SPI_MAX, - .segment_to_reg = aspeed_smc_segment_to_reg, - .reg_to_segment = aspeed_smc_reg_to_segment, - .dma_ctrl = aspeed_smc_dma_ctrl, - }, { - .name = "aspeed.fmc-ast2500", - .r_conf = R_CONF, - .r_ce_ctrl = R_CE_CTRL, - .r_ctrl0 = R_CTRL0, - .r_timings = R_TIMINGS, - .nregs_timings = 1, - .conf_enable_w0 = CONF_ENABLE_W0, - .max_peripherals = 3, - .segments = aspeed_segments_ast2500_fmc, - .flash_window_base = ASPEED_SOC_FMC_FLASH_BASE, - .flash_window_size = 0x10000000, - .features = ASPEED_SMC_FEATURE_DMA, - .dma_flash_mask = 0x0FFFFFFC, - .dma_dram_mask = 0x3FFFFFFC, - .nregs = ASPEED_SMC_R_MAX, - .segment_to_reg = aspeed_smc_segment_to_reg, - .reg_to_segment = aspeed_smc_reg_to_segment, - .dma_ctrl = aspeed_smc_dma_ctrl, - }, { - .name = "aspeed.spi1-ast2500", - .r_conf = R_CONF, - .r_ce_ctrl = R_CE_CTRL, - .r_ctrl0 = R_CTRL0, - .r_timings = R_TIMINGS, - .nregs_timings = 1, - .conf_enable_w0 = CONF_ENABLE_W0, - .max_peripherals = 2, - .segments = aspeed_segments_ast2500_spi1, - .flash_window_base = ASPEED_SOC_SPI_FLASH_BASE, - .flash_window_size = 0x8000000, - .features = 0x0, - .nregs = ASPEED_SMC_R_MAX, - .segment_to_reg = aspeed_smc_segment_to_reg, - .reg_to_segment = aspeed_smc_reg_to_segment, - .dma_ctrl = aspeed_smc_dma_ctrl, - }, { - .name = "aspeed.spi2-ast2500", - .r_conf = R_CONF, - .r_ce_ctrl = R_CE_CTRL, - .r_ctrl0 = R_CTRL0, - .r_timings = R_TIMINGS, - .nregs_timings = 1, - .conf_enable_w0 = CONF_ENABLE_W0, - .max_peripherals = 2, - .segments = aspeed_segments_ast2500_spi2, - .flash_window_base = ASPEED_SOC_SPI2_FLASH_BASE, - .flash_window_size = 0x8000000, - .features = 0x0, - .nregs = ASPEED_SMC_R_MAX, - .segment_to_reg = aspeed_smc_segment_to_reg, - .reg_to_segment = aspeed_smc_reg_to_segment, - .dma_ctrl = aspeed_smc_dma_ctrl, - }, { - .name = "aspeed.fmc-ast2600", - .r_conf = R_CONF, - .r_ce_ctrl = R_CE_CTRL, - .r_ctrl0 = R_CTRL0, - .r_timings = R_TIMINGS, - .nregs_timings = 1, - .conf_enable_w0 = CONF_ENABLE_W0, - .max_peripherals = 3, - .segments = aspeed_segments_ast2600_fmc, - .flash_window_base = ASPEED26_SOC_FMC_FLASH_BASE, - .flash_window_size = 0x10000000, - .features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_WDT_CONTROL, - .dma_flash_mask = 0x0FFFFFFC, - .dma_dram_mask = 0x3FFFFFFC, - .nregs = ASPEED_SMC_R_MAX, - .segment_to_reg = aspeed_2600_smc_segment_to_reg, - .reg_to_segment = aspeed_2600_smc_reg_to_segment, - .dma_ctrl = aspeed_2600_smc_dma_ctrl, - }, { - .name = "aspeed.spi1-ast2600", - .r_conf = R_CONF, - .r_ce_ctrl = R_CE_CTRL, - .r_ctrl0 = R_CTRL0, - .r_timings = R_TIMINGS, - .nregs_timings = 2, - .conf_enable_w0 = CONF_ENABLE_W0, - .max_peripherals = 2, - .segments = aspeed_segments_ast2600_spi1, - .flash_window_base = ASPEED26_SOC_SPI_FLASH_BASE, - .flash_window_size = 0x10000000, - .features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_GRANT, - .dma_flash_mask = 0x0FFFFFFC, - .dma_dram_mask = 0x3FFFFFFC, - .nregs = ASPEED_SMC_R_MAX, - .segment_to_reg = aspeed_2600_smc_segment_to_reg, - .reg_to_segment = aspeed_2600_smc_reg_to_segment, - .dma_ctrl = aspeed_2600_smc_dma_ctrl, - }, { - .name = "aspeed.spi2-ast2600", - .r_conf = R_CONF, - .r_ce_ctrl = R_CE_CTRL, - .r_ctrl0 = R_CTRL0, - .r_timings = R_TIMINGS, - .nregs_timings = 3, - .conf_enable_w0 = CONF_ENABLE_W0, - .max_peripherals = 3, - .segments = aspeed_segments_ast2600_spi2, - .flash_window_base = ASPEED26_SOC_SPI2_FLASH_BASE, - .flash_window_size = 0x10000000, - .features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_GRANT, - .dma_flash_mask = 0x0FFFFFFC, - .dma_dram_mask = 0x3FFFFFFC, - .nregs = ASPEED_SMC_R_MAX, - .segment_to_reg = aspeed_2600_smc_segment_to_reg, - .reg_to_segment = aspeed_2600_smc_reg_to_segment, - .dma_ctrl = aspeed_2600_smc_dma_ctrl, - }, -}; - -/* - * The Segment Registers of the AST2400 and AST2500 have a 8MB - * unit. The address range of a flash SPI peripheral is encoded with - * absolute addresses which should be part of the overall controller - * window. - */ -static uint32_t aspeed_smc_segment_to_reg(const AspeedSMCState *s, - 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 void aspeed_smc_reg_to_segment(const AspeedSMCState *s, - 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; -} - -/* - * The Segment Registers of the AST2600 have a 1MB unit. The address - * range of a flash SPI peripheral is encoded with offsets in the overall - * controller window. The previous SoC AST2400 and AST2500 used - * absolute addresses. Only bits [27:20] are relevant and the end - * address is an upper bound limit. - */ -#define AST2600_SEG_ADDR_MASK 0x0ff00000 - -static uint32_t aspeed_2600_smc_segment_to_reg(const AspeedSMCState *s, - const AspeedSegments *seg) -{ - uint32_t reg = 0; - - /* Disabled segments have a nil register */ - if (!seg->size) { - return 0; - } - - reg |= (seg->addr & AST2600_SEG_ADDR_MASK) >> 16; /* start offset */ - reg |= (seg->addr + seg->size - 1) & AST2600_SEG_ADDR_MASK; /* end offset */ - return reg; -} - -static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s, - uint32_t reg, AspeedSegments *seg) -{ - uint32_t start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK; - uint32_t end_offset = reg & AST2600_SEG_ADDR_MASK; - - if (reg) { - seg->addr = s->ctrl->flash_window_base + start_offset; - seg->size = end_offset + MiB - start_offset; - } else { - seg->addr = s->ctrl->flash_window_base; - seg->size = 0; - } + return !!(asc->features & ASPEED_SMC_FEATURE_WDT_CONTROL); } #define aspeed_smc_error(fmt, ...) \ @@ -520,15 +224,16 @@ static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, const AspeedSegments *new, int cs) { + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); AspeedSegments seg; int i; - for (i = 0; i < s->ctrl->max_peripherals; i++) { + for (i = 0; i < asc->max_peripherals; i++) { if (i == cs) { continue; } - s->ctrl->reg_to_segment(s, s->regs[R_SEG_ADDR0 + i], &seg); + asc->reg_to_segment(s, s->regs[R_SEG_ADDR0 + i], &seg); if (new->addr + new->size > seg.addr && new->addr < seg.addr + seg.size) { @@ -546,14 +251,15 @@ static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, static void aspeed_smc_flash_set_segment_region(AspeedSMCState *s, int cs, uint64_t regval) { + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); AspeedSMCFlash *fl = &s->flashes[cs]; AspeedSegments seg; - s->ctrl->reg_to_segment(s, regval, &seg); + asc->reg_to_segment(s, regval, &seg); 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_address(&fl->mmio, seg.addr - asc->flash_window_base); memory_region_set_enabled(&fl->mmio, !!seg.size); memory_region_transaction_commit(); @@ -563,40 +269,41 @@ static void aspeed_smc_flash_set_segment_region(AspeedSMCState *s, int cs, static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, uint64_t new) { + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); AspeedSegments seg; - s->ctrl->reg_to_segment(s, new, &seg); + asc->reg_to_segment(s, new, &seg); trace_aspeed_smc_flash_set_segment(cs, new, seg.addr, seg.addr + seg.size); /* The start address of CS0 is read-only */ - if (cs == 0 && seg.addr != s->ctrl->flash_window_base) { + if (cs == 0 && seg.addr != asc->flash_window_base) { aspeed_smc_error("Tried to change CS0 start address to 0x%" HWADDR_PRIx, seg.addr); - seg.addr = s->ctrl->flash_window_base; - new = s->ctrl->segment_to_reg(s, &seg); + seg.addr = asc->flash_window_base; + new = asc->segment_to_reg(s, &seg); } /* * 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_peripherals && - seg.addr + seg.size != s->ctrl->segments[cs].addr + - s->ctrl->segments[cs].size) { + if ((asc->segments == aspeed_2500_spi1_segments || + asc->segments == aspeed_2500_spi2_segments) && + cs == asc->max_peripherals && + seg.addr + seg.size != asc->segments[cs].addr + + asc->segments[cs].size) { aspeed_smc_error("Tried to change CS%d end address to 0x%" HWADDR_PRIx, cs, seg.addr + seg.size); - seg.size = s->ctrl->segments[cs].addr + s->ctrl->segments[cs].size - + seg.size = asc->segments[cs].addr + asc->segments[cs].size - seg.addr; - new = s->ctrl->segment_to_reg(s, &seg); + new = asc->segment_to_reg(s, &seg); } /* Keep the segment in the overall flash window */ if (seg.size && - (seg.addr + seg.size <= s->ctrl->flash_window_base || - seg.addr > s->ctrl->flash_window_base + s->ctrl->flash_window_size)) { + (seg.addr + seg.size <= asc->flash_window_base || + seg.addr > asc->flash_window_base + asc->flash_window_size)) { aspeed_smc_error("new segment for CS%d is invalid : " "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]", cs, seg.addr, seg.addr + seg.size); @@ -681,8 +388,9 @@ static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl) static inline int aspeed_smc_flash_is_4byte(const AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); - if (s->ctrl->segments == aspeed_segments_spi) { + if (asc->segments == aspeed_2400_spi1_segments) { return s->regs[s->r_ctrl0] & CTRL_AST2400_SPI_4BYTE; } else { return s->regs[s->r_ce_ctrl] & (1 << (CTRL_EXTENDED0 + fl->id)); @@ -712,9 +420,10 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, uint32_t addr) { const AspeedSMCState *s = fl->controller; + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); AspeedSegments seg; - s->ctrl->reg_to_segment(s, s->regs[R_SEG_ADDR0 + fl->id], &seg); + asc->reg_to_segment(s, s->regs[R_SEG_ADDR0 + fl->id], &seg); if ((addr % seg.size) != addr) { aspeed_smc_error("invalid address 0x%08x for CS%d segment : " "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]", @@ -974,6 +683,7 @@ static void aspeed_smc_flash_update_ctrl(AspeedSMCFlash *fl, uint32_t value) static void aspeed_smc_reset(DeviceState *d) { AspeedSMCState *s = ASPEED_SMC(d); + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); int i; memset(s->regs, 0, sizeof s->regs); @@ -985,13 +695,13 @@ static void aspeed_smc_reset(DeviceState *d) } /* setup the default segment register values and regions for all */ - for (i = 0; i < s->ctrl->max_peripherals; ++i) { + for (i = 0; i < asc->max_peripherals; ++i) { aspeed_smc_flash_set_segment_region(s, i, - s->ctrl->segment_to_reg(s, &s->ctrl->segments[i])); + asc->segment_to_reg(s, &asc->segments[i])); } /* HW strapping flash type for the AST2600 controllers */ - if (s->ctrl->segments == aspeed_segments_ast2600_fmc) { + if (asc->segments == aspeed_2600_fmc_segments) { /* flash type is fixed to SPI for all */ s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1); @@ -999,7 +709,7 @@ static void aspeed_smc_reset(DeviceState *d) } /* HW strapping flash type for FMC controllers */ - if (s->ctrl->segments == aspeed_segments_ast2500_fmc) { + if (asc->segments == aspeed_2500_fmc_segments) { /* flash type is fixed to SPI for CE0 and CE1 */ s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1); @@ -1007,7 +717,7 @@ static void aspeed_smc_reset(DeviceState *d) /* HW strapping for AST2400 FMC controllers (SCU70). Let's use the * configuration of the palmetto-bmc machine */ - if (s->ctrl->segments == aspeed_segments_fmc) { + if (asc->segments == aspeed_2400_fmc_segments) { s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); } @@ -1018,25 +728,26 @@ static void aspeed_smc_reset(DeviceState *d) static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) { AspeedSMCState *s = ASPEED_SMC(opaque); + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(opaque); addr >>= 2; if (addr == s->r_conf || (addr >= s->r_timings && - addr < s->r_timings + s->ctrl->nregs_timings) || + addr < s->r_timings + asc->nregs_timings) || addr == s->r_ce_ctrl || addr == R_CE_CMD_CTRL || addr == R_INTR_CTRL || addr == R_DUMMY_DATA || - (aspeed_smc_has_wdt_control(s) && addr == R_FMC_WDT2_CTRL) || - (aspeed_smc_has_dma(s) && addr == R_DMA_CTRL) || - (aspeed_smc_has_dma(s) && addr == R_DMA_FLASH_ADDR) || - (aspeed_smc_has_dma(s) && addr == R_DMA_DRAM_ADDR) || - (aspeed_smc_has_dma(s) && addr == R_DMA_LEN) || - (aspeed_smc_has_dma(s) && addr == R_DMA_CHECKSUM) || + (aspeed_smc_has_wdt_control(asc) && addr == R_FMC_WDT2_CTRL) || + (aspeed_smc_has_dma(asc) && addr == R_DMA_CTRL) || + (aspeed_smc_has_dma(asc) && addr == R_DMA_FLASH_ADDR) || + (aspeed_smc_has_dma(asc) && addr == R_DMA_DRAM_ADDR) || + (aspeed_smc_has_dma(asc) && addr == R_DMA_LEN) || + (aspeed_smc_has_dma(asc) && addr == R_DMA_CHECKSUM) || (addr >= R_SEG_ADDR0 && - addr < R_SEG_ADDR0 + s->ctrl->max_peripherals) || - (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->ctrl->max_peripherals)) { + addr < R_SEG_ADDR0 + asc->max_peripherals) || + (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + asc->max_peripherals)) { trace_aspeed_smc_read(addr, size, s->regs[addr]); @@ -1292,7 +1003,9 @@ static void aspeed_smc_dma_ctrl(AspeedSMCState *s, uint32_t dma_ctrl) static inline bool aspeed_smc_dma_granted(AspeedSMCState *s) { - if (!(s->ctrl->features & ASPEED_SMC_FEATURE_DMA_GRANT)) { + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + + if (!(asc->features & ASPEED_SMC_FEATURE_DMA_GRANT)) { return true; } @@ -1334,6 +1047,7 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { AspeedSMCState *s = ASPEED_SMC(opaque); + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); uint32_t value = data; addr >>= 2; @@ -1342,14 +1056,14 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, if (addr == s->r_conf || (addr >= s->r_timings && - addr < s->r_timings + s->ctrl->nregs_timings) || + addr < s->r_timings + asc->nregs_timings) || addr == s->r_ce_ctrl) { s->regs[addr] = value; } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) { int cs = addr - s->r_ctrl0; aspeed_smc_flash_update_ctrl(&s->flashes[cs], value); } else if (addr >= R_SEG_ADDR0 && - addr < R_SEG_ADDR0 + s->ctrl->max_peripherals) { + addr < R_SEG_ADDR0 + asc->max_peripherals) { int cs = addr - R_SEG_ADDR0; if (value != s->regs[R_SEG_ADDR0 + cs]) { @@ -1359,19 +1073,19 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, s->regs[addr] = value & 0xff; } else if (addr == R_DUMMY_DATA) { s->regs[addr] = value & 0xff; - } else if (aspeed_smc_has_wdt_control(s) && addr == R_FMC_WDT2_CTRL) { + } else if (aspeed_smc_has_wdt_control(asc) && addr == R_FMC_WDT2_CTRL) { s->regs[addr] = value & FMC_WDT2_CTRL_EN; } else if (addr == R_INTR_CTRL) { s->regs[addr] = value; - } else if (aspeed_smc_has_dma(s) && addr == R_DMA_CTRL) { - s->ctrl->dma_ctrl(s, value); - } else if (aspeed_smc_has_dma(s) && addr == R_DMA_DRAM_ADDR && + } else if (aspeed_smc_has_dma(asc) && addr == R_DMA_CTRL) { + asc->dma_ctrl(s, value); + } else if (aspeed_smc_has_dma(asc) && addr == R_DMA_DRAM_ADDR && aspeed_smc_dma_granted(s)) { - s->regs[addr] = DMA_DRAM_ADDR(s, value); - } else if (aspeed_smc_has_dma(s) && addr == R_DMA_FLASH_ADDR && + s->regs[addr] = DMA_DRAM_ADDR(asc, value); + } else if (aspeed_smc_has_dma(asc) && addr == R_DMA_FLASH_ADDR && aspeed_smc_dma_granted(s)) { - s->regs[addr] = DMA_FLASH_ADDR(s, value); - } else if (aspeed_smc_has_dma(s) && addr == R_DMA_LEN && + s->regs[addr] = DMA_FLASH_ADDR(asc, value); + } else if (aspeed_smc_has_dma(asc) && addr == R_DMA_LEN && aspeed_smc_dma_granted(s)) { s->regs[addr] = DMA_LENGTH(value); } else { @@ -1407,24 +1121,22 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedSMCState *s = ASPEED_SMC(dev); - AspeedSMCClass *mc = ASPEED_SMC_GET_CLASS(s); + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); int i; char name[32]; hwaddr offset = 0; - s->ctrl = mc->ctrl; - /* keep a copy under AspeedSMCState to speed up accesses */ - s->r_conf = s->ctrl->r_conf; - s->r_ce_ctrl = s->ctrl->r_ce_ctrl; - s->r_ctrl0 = s->ctrl->r_ctrl0; - s->r_timings = s->ctrl->r_timings; - s->conf_enable_w0 = s->ctrl->conf_enable_w0; + s->r_conf = asc->r_conf; + s->r_ce_ctrl = asc->r_ce_ctrl; + s->r_ctrl0 = asc->r_ctrl0; + s->r_timings = asc->r_timings; + s->conf_enable_w0 = asc->conf_enable_w0; /* Enforce some real HW limits */ - if (s->num_cs > s->ctrl->max_peripherals) { - aspeed_smc_error("num_cs cannot exceed: %d", s->ctrl->max_peripherals); - s->num_cs = s->ctrl->max_peripherals; + if (s->num_cs > asc->max_peripherals) { + aspeed_smc_error("num_cs cannot exceed: %d", asc->max_peripherals); + s->num_cs = asc->max_peripherals; } /* DMA irq. Keep it first for the initialization in the SoC */ @@ -1441,7 +1153,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) /* The memory region for the controller registers */ memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s, - TYPE_ASPEED_SMC, s->ctrl->nregs * 4); + TYPE_ASPEED_SMC, asc->nregs * 4); sysbus_init_mmio(sbd, &s->mmio); /* @@ -1452,13 +1164,13 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->mmio_flash, OBJECT(s), &aspeed_smc_flash_default_ops, s, TYPE_ASPEED_SMC ".flash", - s->ctrl->flash_window_size); + asc->flash_window_size); memory_region_init_alias(&s->mmio_flash_alias, OBJECT(s), TYPE_ASPEED_SMC ".flash", - &s->mmio_flash, 0, s->ctrl->flash_window_size); + &s->mmio_flash, 0, asc->flash_window_size); sysbus_init_mmio(sbd, &s->mmio_flash_alias); - s->flashes = g_new0(AspeedSMCFlash, s->ctrl->max_peripherals); + s->flashes = g_new0(AspeedSMCFlash, asc->max_peripherals); /* * Let's create a sub memory region for each possible peripheral. All @@ -1467,14 +1179,14 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) * module behind to handle the memory accesses. This depends on * the board configuration. */ - for (i = 0; i < s->ctrl->max_peripherals; ++i) { + for (i = 0; i < asc->max_peripherals; ++i) { AspeedSMCFlash *fl = &s->flashes[i]; snprintf(name, sizeof(name), TYPE_ASPEED_SMC ".flash.%d", i); fl->id = i; fl->controller = s; - fl->size = s->ctrl->segments[i].size; + fl->size = asc->segments[i].size; memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops, fl, name, fl->size); memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio); @@ -1482,7 +1194,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) } /* DMA support */ - if (aspeed_smc_has_dma(s)) { + if (aspeed_smc_has_dma(asc)) { aspeed_smc_dma_setup(s, errp); } } @@ -1510,13 +1222,11 @@ static Property aspeed_smc_properties[] = { static void aspeed_smc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - AspeedSMCClass *mc = ASPEED_SMC_CLASS(klass); dc->realize = aspeed_smc_realize; dc->reset = aspeed_smc_reset; device_class_set_props(dc, aspeed_smc_properties); dc->vmsd = &vmstate_aspeed_smc; - mc->ctrl = data; } static const TypeInfo aspeed_smc_info = { @@ -1524,23 +1234,408 @@ static const TypeInfo aspeed_smc_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedSMCState), .class_size = sizeof(AspeedSMCClass), + .class_init = aspeed_smc_class_init, .abstract = true, }; + +/* + * The Segment Registers of the AST2400 and AST2500 have a 8MB + * unit. The address range of a flash SPI peripheral is encoded with + * absolute addresses which should be part of the overall controller + * window. + */ +static uint32_t aspeed_smc_segment_to_reg(const AspeedSMCState *s, + 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 void aspeed_smc_reg_to_segment(const AspeedSMCState *s, + 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 const AspeedSegments aspeed_2400_smc_segments[] = { + { 0x10000000, 32 * MiB }, +}; + +static void aspeed_2400_smc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2400 SMC Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 1; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->max_peripherals = 1; + asc->segments = aspeed_2400_smc_segments; + asc->flash_window_base = 0x10000000; + asc->flash_window_size = 0x6000000; + asc->features = 0x0; + asc->nregs = ASPEED_SMC_R_SMC_MAX; + asc->segment_to_reg = aspeed_smc_segment_to_reg; + asc->reg_to_segment = aspeed_smc_reg_to_segment; + asc->dma_ctrl = aspeed_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2400_smc_info = { + .name = "aspeed.smc-ast2400", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2400_smc_class_init, +}; + +static const AspeedSegments aspeed_2400_fmc_segments[] = { + { 0x20000000, 64 * MiB }, /* start address is readonly */ + { 0x24000000, 32 * MiB }, + { 0x26000000, 32 * MiB }, + { 0x28000000, 32 * MiB }, + { 0x2A000000, 32 * MiB } +}; + +static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2400 FMC Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 1; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->max_peripherals = 5; + asc->segments = aspeed_2400_fmc_segments; + asc->flash_window_base = 0x20000000; + asc->flash_window_size = 0x10000000; + asc->features = ASPEED_SMC_FEATURE_DMA; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0x1FFFFFFC; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_smc_segment_to_reg; + asc->reg_to_segment = aspeed_smc_reg_to_segment; + asc->dma_ctrl = aspeed_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2400_fmc_info = { + .name = "aspeed.fmc-ast2400", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2400_fmc_class_init, +}; + +static const AspeedSegments aspeed_2400_spi1_segments[] = { + { 0x30000000, 64 * MiB }, +}; + +static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2400 SPI1 Controller"; + asc->r_conf = R_SPI_CONF; + asc->r_ce_ctrl = 0xff; + asc->r_ctrl0 = R_SPI_CTRL0; + asc->r_timings = R_SPI_TIMINGS; + asc->nregs_timings = 1; + asc->conf_enable_w0 = SPI_CONF_ENABLE_W0; + asc->max_peripherals = 1; + asc->segments = aspeed_2400_spi1_segments; + asc->flash_window_base = 0x30000000; + asc->flash_window_size = 0x10000000; + asc->features = 0x0; + asc->nregs = ASPEED_SMC_R_SPI_MAX; + asc->segment_to_reg = aspeed_smc_segment_to_reg; + asc->reg_to_segment = aspeed_smc_reg_to_segment; + asc->dma_ctrl = aspeed_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2400_spi1_info = { + .name = "aspeed.spi1-ast2400", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2400_spi1_class_init, +}; + +static const AspeedSegments aspeed_2500_fmc_segments[] = { + { 0x20000000, 128 * MiB }, /* start address is readonly */ + { 0x28000000, 32 * MiB }, + { 0x2A000000, 32 * MiB }, +}; + +static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2600 FMC Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 1; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->max_peripherals = 3; + asc->segments = aspeed_2500_fmc_segments; + asc->flash_window_base = 0x20000000; + asc->flash_window_size = 0x10000000; + asc->features = ASPEED_SMC_FEATURE_DMA; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0x3FFFFFFC; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_smc_segment_to_reg; + asc->reg_to_segment = aspeed_smc_reg_to_segment; + asc->dma_ctrl = aspeed_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2500_fmc_info = { + .name = "aspeed.fmc-ast2500", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2500_fmc_class_init, +}; + +static const AspeedSegments aspeed_2500_spi1_segments[] = { + { 0x30000000, 32 * MiB }, /* start address is readonly */ + { 0x32000000, 96 * MiB }, /* end address is readonly */ +}; + +static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2600 SPI1 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 1; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->max_peripherals = 2; + asc->segments = aspeed_2500_spi1_segments; + asc->flash_window_base = 0x30000000; + asc->flash_window_size = 0x8000000; + asc->features = 0x0; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_smc_segment_to_reg; + asc->reg_to_segment = aspeed_smc_reg_to_segment; + asc->dma_ctrl = aspeed_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2500_spi1_info = { + .name = "aspeed.spi1-ast2500", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2500_spi1_class_init, +}; + +static const AspeedSegments aspeed_2500_spi2_segments[] = { + { 0x38000000, 32 * MiB }, /* start address is readonly */ + { 0x3A000000, 96 * MiB }, /* end address is readonly */ +}; + +static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2600 SPI2 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 1; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->max_peripherals = 2; + asc->segments = aspeed_2500_spi2_segments; + asc->flash_window_base = 0x38000000; + asc->flash_window_size = 0x8000000; + asc->features = 0x0; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_smc_segment_to_reg; + asc->reg_to_segment = aspeed_smc_reg_to_segment; + asc->dma_ctrl = aspeed_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2500_spi2_info = { + .name = "aspeed.spi2-ast2500", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2500_spi2_class_init, +}; + +/* + * The Segment Registers of the AST2600 have a 1MB unit. The address + * range of a flash SPI peripheral is encoded with offsets in the overall + * controller window. The previous SoC AST2400 and AST2500 used + * absolute addresses. Only bits [27:20] are relevant and the end + * address is an upper bound limit. + */ +#define AST2600_SEG_ADDR_MASK 0x0ff00000 + +static uint32_t aspeed_2600_smc_segment_to_reg(const AspeedSMCState *s, + const AspeedSegments *seg) +{ + uint32_t reg = 0; + + /* Disabled segments have a nil register */ + if (!seg->size) { + return 0; + } + + reg |= (seg->addr & AST2600_SEG_ADDR_MASK) >> 16; /* start offset */ + reg |= (seg->addr + seg->size - 1) & AST2600_SEG_ADDR_MASK; /* end offset */ + return reg; +} + +static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s, + uint32_t reg, AspeedSegments *seg) +{ + uint32_t start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK; + uint32_t end_offset = reg & AST2600_SEG_ADDR_MASK; + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + + if (reg) { + seg->addr = asc->flash_window_base + start_offset; + seg->size = end_offset + MiB - start_offset; + } else { + seg->addr = asc->flash_window_base; + seg->size = 0; + } +} + +static const AspeedSegments aspeed_2600_fmc_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 128 * MiB, 128 * MiB }, /* default is disabled but needed for -kernel */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2600 FMC Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 1; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->max_peripherals = 3; + asc->segments = aspeed_2600_fmc_segments; + asc->flash_window_base = 0x20000000; + asc->flash_window_size = 0x10000000; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_WDT_CONTROL; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0x3FFFFFFC; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2600_fmc_info = { + .name = "aspeed.fmc-ast2600", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2600_fmc_class_init, +}; + +static const AspeedSegments aspeed_2600_spi1_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2600_spi1_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2600 SPI1 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->max_peripherals = 2; + asc->segments = aspeed_2600_spi1_segments; + asc->flash_window_base = 0x30000000; + asc->flash_window_size = 0x10000000; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_GRANT; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0x3FFFFFFC; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2600_spi1_info = { + .name = "aspeed.spi1-ast2600", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2600_spi1_class_init, +}; + +static const AspeedSegments aspeed_2600_spi2_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2600_spi2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2600 SPI2 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 3; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->max_peripherals = 3; + asc->segments = aspeed_2600_spi2_segments; + asc->flash_window_base = 0x50000000; + asc->flash_window_size = 0x10000000; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_GRANT; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0x3FFFFFFC; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; +} + +static const TypeInfo aspeed_2600_spi2_info = { + .name = "aspeed.spi2-ast2600", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2600_spi2_class_init, +}; + static void aspeed_smc_register_types(void) { - int i; - type_register_static(&aspeed_smc_info); - for (i = 0; i < ARRAY_SIZE(controllers); ++i) { - TypeInfo ti = { - .name = controllers[i].name, - .parent = TYPE_ASPEED_SMC, - .class_init = aspeed_smc_class_init, - .class_data = (void *)&controllers[i], - }; - type_register(&ti); - } + type_register_static(&aspeed_2400_smc_info); + type_register_static(&aspeed_2400_fmc_info); + type_register_static(&aspeed_2400_spi1_info); + type_register_static(&aspeed_2500_fmc_info); + type_register_static(&aspeed_2500_spi1_info); + type_register_static(&aspeed_2500_spi2_info); + type_register_static(&aspeed_2600_fmc_info); + type_register_static(&aspeed_2600_spi1_info); + type_register_static(&aspeed_2600_spi2_info); } type_init(aspeed_smc_register_types) diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index cdaf165300..0ea536a44c 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -29,35 +29,7 @@ #include "hw/sysbus.h" #include "qom/object.h" -typedef struct AspeedSegments { - hwaddr addr; - uint32_t size; -} AspeedSegments; - struct AspeedSMCState; -typedef struct AspeedSMCController { - const char *name; - uint8_t r_conf; - uint8_t r_ce_ctrl; - uint8_t r_ctrl0; - uint8_t r_timings; - uint8_t nregs_timings; - uint8_t conf_enable_w0; - uint8_t max_peripherals; - const AspeedSegments *segments; - hwaddr flash_window_base; - uint32_t flash_window_size; - uint32_t features; - hwaddr dma_flash_mask; - hwaddr dma_dram_mask; - uint32_t nregs; - uint32_t (*segment_to_reg)(const struct AspeedSMCState *s, - const AspeedSegments *seg); - void (*reg_to_segment)(const struct AspeedSMCState *s, uint32_t reg, - AspeedSegments *seg); - void (*dma_ctrl)(struct AspeedSMCState *s, uint32_t value); -} AspeedSMCController; - typedef struct AspeedSMCFlash { struct AspeedSMCState *controller; @@ -71,18 +43,11 @@ typedef struct AspeedSMCFlash { #define TYPE_ASPEED_SMC "aspeed.smc" OBJECT_DECLARE_TYPE(AspeedSMCState, AspeedSMCClass, ASPEED_SMC) -struct AspeedSMCClass { - SysBusDevice parent_obj; - const AspeedSMCController *ctrl; -}; - #define ASPEED_SMC_R_MAX (0x100 / 4) struct AspeedSMCState { SysBusDevice parent_obj; - const AspeedSMCController *ctrl; - MemoryRegion mmio; MemoryRegion mmio_flash; MemoryRegion mmio_flash_alias; @@ -115,4 +80,33 @@ struct AspeedSMCState { uint8_t snoop_dummies; }; +typedef struct AspeedSegments { + hwaddr addr; + uint32_t size; +} AspeedSegments; + +struct AspeedSMCClass { + SysBusDeviceClass parent_obj; + + uint8_t r_conf; + uint8_t r_ce_ctrl; + uint8_t r_ctrl0; + uint8_t r_timings; + uint8_t nregs_timings; + uint8_t conf_enable_w0; + uint8_t max_peripherals; + const AspeedSegments *segments; + hwaddr flash_window_base; + uint32_t flash_window_size; + uint32_t features; + hwaddr dma_flash_mask; + hwaddr dma_dram_mask; + uint32_t nregs; + uint32_t (*segment_to_reg)(const AspeedSMCState *s, + const AspeedSegments *seg); + void (*reg_to_segment)(const AspeedSMCState *s, uint32_t reg, + AspeedSegments *seg); + void (*dma_ctrl)(AspeedSMCState *s, uint32_t value); +}; + #endif /* ASPEED_SMC_H */ From a7d78beff46f8e5c4343edca4b76675e6e55172e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0384/1334] aspeed/smc: Remove the 'flash' attribute from AspeedSMCFlash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to keep a reference of the flash qdev in the AspeedSMCFlash state: the SPI bus takes ownership and will release its resources. Remove AspeedSMCFlash::flash. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 11 +++++------ include/hw/ssi/aspeed_smc.h | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index ba5f1dc5af..854413594d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -274,18 +274,17 @@ static void aspeed_board_init_flashes(AspeedSMCState *s, int i ; for (i = 0; i < s->num_cs; ++i) { - AspeedSMCFlash *fl = &s->flashes[i]; DriveInfo *dinfo = drive_get_next(IF_MTD); qemu_irq cs_line; + DeviceState *dev; - fl->flash = qdev_new(flashtype); + dev = qdev_new(flashtype); if (dinfo) { - qdev_prop_set_drive(fl->flash, "drive", - blk_by_legacy_dinfo(dinfo)); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo)); } - qdev_realize_and_unref(fl->flash, BUS(s->spi), &error_fatal); + qdev_realize_and_unref(dev, BUS(s->spi), &error_fatal); - cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0); + cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0); sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line); } } diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 0ea536a44c..f32f66f9a8 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -37,7 +37,6 @@ typedef struct AspeedSMCFlash { uint32_t size; MemoryRegion mmio; - DeviceState *flash; } AspeedSMCFlash; #define TYPE_ASPEED_SMC "aspeed.smc" From 6bb55e796740a0b685831faa784efb0c38dd151c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0385/1334] aspeed/smc: Remove the 'size' attribute from AspeedSMCFlash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AspeedSMCFlash::size is only used to compute the initial size of the boot_rom region. Not very useful, so directly call memory_region_size() instead. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 7 ++++--- hw/ssi/aspeed_smc.c | 5 ++--- include/hw/ssi/aspeed_smc.h | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 854413594d..f5916e8126 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -376,6 +376,7 @@ static void aspeed_machine_init(MachineState *machine) if (drive0) { AspeedSMCFlash *fl = &bmc->soc.fmc.flashes[0]; MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + uint64_t size = memory_region_size(&fl->mmio); /* * create a ROM region using the default mapping window size of @@ -385,15 +386,15 @@ static void aspeed_machine_init(MachineState *machine) */ if (ASPEED_MACHINE(machine)->mmio_exec) { memory_region_init_alias(boot_rom, NULL, "aspeed.boot_rom", - &fl->mmio, 0, fl->size); + &fl->mmio, 0, size); memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, boot_rom); } else { memory_region_init_rom(boot_rom, NULL, "aspeed.boot_rom", - fl->size, &error_abort); + size, &error_abort); memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, boot_rom); - write_boot_rom(drive0, FIRMWARE_ADDR, fl->size, &error_abort); + write_boot_rom(drive0, FIRMWARE_ADDR, size, &error_abort); } } diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 5466be6317..3e4221311a 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1186,11 +1186,10 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) fl->id = i; fl->controller = s; - fl->size = asc->segments[i].size; memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops, - fl, name, fl->size); + fl, name, asc->segments[i].size); memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio); - offset += fl->size; + offset += asc->segments[i].size; } /* DMA support */ diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index f32f66f9a8..097bb6aaf5 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -34,7 +34,6 @@ typedef struct AspeedSMCFlash { struct AspeedSMCState *controller; uint8_t id; - uint32_t size; MemoryRegion mmio; } AspeedSMCFlash; From 10f915e4caefeacedf092eb90bfcce56e23e102e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0386/1334] aspeed/smc: Rename AspeedSMCFlash 'id' to 'cs' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'cs' is a more appropriate name to index SPI flash devices. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 30 +++++++++++++++--------------- include/hw/ssi/aspeed_smc.h | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 3e4221311a..643cde8323 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -352,20 +352,20 @@ static inline int aspeed_smc_flash_mode(const AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; - return s->regs[s->r_ctrl0 + fl->id] & CTRL_CMD_MODE_MASK; + return s->regs[s->r_ctrl0 + fl->cs] & CTRL_CMD_MODE_MASK; } static inline bool aspeed_smc_is_writable(const AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; - return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + fl->id)); + return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + fl->cs)); } static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; - int cmd = (s->regs[s->r_ctrl0 + fl->id] >> CTRL_CMD_SHIFT) & CTRL_CMD_MASK; + int cmd = (s->regs[s->r_ctrl0 + fl->cs] >> CTRL_CMD_SHIFT) & CTRL_CMD_MASK; /* * In read mode, the default SPI command is READ (0x3). In other @@ -393,7 +393,7 @@ static inline int aspeed_smc_flash_is_4byte(const AspeedSMCFlash *fl) if (asc->segments == aspeed_2400_spi1_segments) { return s->regs[s->r_ctrl0] & CTRL_AST2400_SPI_4BYTE; } else { - return s->regs[s->r_ce_ctrl] & (1 << (CTRL_EXTENDED0 + fl->id)); + return s->regs[s->r_ce_ctrl] & (1 << (CTRL_EXTENDED0 + fl->cs)); } } @@ -401,9 +401,9 @@ static void aspeed_smc_flash_do_select(AspeedSMCFlash *fl, bool unselect) { AspeedSMCState *s = fl->controller; - trace_aspeed_smc_flash_select(fl->id, unselect ? "un" : ""); + trace_aspeed_smc_flash_select(fl->cs, unselect ? "un" : ""); - qemu_set_irq(s->cs_lines[fl->id], unselect); + qemu_set_irq(s->cs_lines[fl->cs], unselect); } static void aspeed_smc_flash_select(AspeedSMCFlash *fl) @@ -423,11 +423,11 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); AspeedSegments seg; - asc->reg_to_segment(s, s->regs[R_SEG_ADDR0 + fl->id], &seg); + asc->reg_to_segment(s, s->regs[R_SEG_ADDR0 + fl->cs], &seg); if ((addr % seg.size) != addr) { aspeed_smc_error("invalid address 0x%08x for CS%d segment : " "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]", - addr, fl->id, seg.addr, seg.addr + seg.size); + addr, fl->cs, seg.addr, seg.addr + seg.size); addr %= seg.size; } @@ -437,7 +437,7 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, static int aspeed_smc_flash_dummies(const AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; - uint32_t r_ctrl0 = s->regs[s->r_ctrl0 + fl->id]; + uint32_t r_ctrl0 = s->regs[s->r_ctrl0 + fl->cs]; uint32_t dummy_high = (r_ctrl0 >> CTRL_DUMMY_HIGH_SHIFT) & 0x1; uint32_t dummy_low = (r_ctrl0 >> CTRL_DUMMY_LOW_SHIFT) & 0x3; uint32_t dummies = ((dummy_high << 2) | dummy_low) * 8; @@ -506,7 +506,7 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) aspeed_smc_error("invalid flash mode %d", aspeed_smc_flash_mode(fl)); } - trace_aspeed_smc_flash_read(fl->id, addr, size, ret, + trace_aspeed_smc_flash_read(fl->cs, addr, size, ret, aspeed_smc_flash_mode(fl)); return ret; } @@ -563,7 +563,7 @@ static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data, AspeedSMCState *s = fl->controller; uint8_t addr_width = aspeed_smc_flash_is_4byte(fl) ? 4 : 3; - trace_aspeed_smc_do_snoop(fl->id, s->snoop_index, s->snoop_dummies, + trace_aspeed_smc_do_snoop(fl->cs, s->snoop_index, s->snoop_dummies, (uint8_t) data & 0xff); if (s->snoop_index == SNOOP_OFF) { @@ -616,7 +616,7 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, AspeedSMCState *s = fl->controller; int i; - trace_aspeed_smc_flash_write(fl->id, addr, size, data, + trace_aspeed_smc_flash_write(fl->cs, addr, size, data, aspeed_smc_flash_mode(fl)); if (!aspeed_smc_is_writable(fl)) { @@ -668,12 +668,12 @@ static void aspeed_smc_flash_update_ctrl(AspeedSMCFlash *fl, uint32_t value) unselect = (value & CTRL_CMD_MODE_MASK) != CTRL_USERMODE; /* A change of CTRL_CE_STOP_ACTIVE from 0 to 1, unselects the CS */ - if (!(s->regs[s->r_ctrl0 + fl->id] & CTRL_CE_STOP_ACTIVE) && + if (!(s->regs[s->r_ctrl0 + fl->cs] & CTRL_CE_STOP_ACTIVE) && value & CTRL_CE_STOP_ACTIVE) { unselect = true; } - s->regs[s->r_ctrl0 + fl->id] = value; + s->regs[s->r_ctrl0 + fl->cs] = value; s->snoop_index = unselect ? SNOOP_OFF : SNOOP_START; @@ -1184,7 +1184,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) snprintf(name, sizeof(name), TYPE_ASPEED_SMC ".flash.%d", i); - fl->id = i; + fl->cs = i; fl->controller = s; memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops, fl, name, asc->segments[i].size); diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 097bb6aaf5..40b6926b3e 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -33,7 +33,7 @@ struct AspeedSMCState; typedef struct AspeedSMCFlash { struct AspeedSMCState *controller; - uint8_t id; + uint8_t cs; MemoryRegion mmio; } AspeedSMCFlash; From f75b5331178b6771f2a96e92ce140dd2786c8282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0387/1334] aspeed/smc: QOMify AspeedSMCFlash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AspeedSMCFlash is a small structure representing the AHB memory window through which the contents of a flash device can be accessed with MMIOs. Introduce an AspeedSMCFlash SysBusDevice model and attach the associated memory region to the newly instantiated objects. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 76 +++++++++++++++++++++++++++++++++---- include/hw/ssi/aspeed_smc.h | 13 +++++-- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 643cde8323..c534e9bf87 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1101,6 +1101,18 @@ static const MemoryRegionOps aspeed_smc_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void aspeed_smc_instance_init(Object *obj) +{ + AspeedSMCState *s = ASPEED_SMC(obj); + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + int i; + + for (i = 0; i < asc->max_peripherals; i++) { + object_initialize_child(obj, "flash[*]", &s->flashes[i], + TYPE_ASPEED_SMC_FLASH); + } +} + /* * Initialize the custom address spaces for DMAs */ @@ -1123,7 +1135,6 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) AspeedSMCState *s = ASPEED_SMC(dev); AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); int i; - char name[32]; hwaddr offset = 0; /* keep a copy under AspeedSMCState to speed up accesses */ @@ -1170,8 +1181,6 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) &s->mmio_flash, 0, asc->flash_window_size); sysbus_init_mmio(sbd, &s->mmio_flash_alias); - s->flashes = g_new0(AspeedSMCFlash, asc->max_peripherals); - /* * Let's create a sub memory region for each possible peripheral. All * have a configurable memory segment in the overall flash mapping @@ -1182,12 +1191,17 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) for (i = 0; i < asc->max_peripherals; ++i) { AspeedSMCFlash *fl = &s->flashes[i]; - snprintf(name, sizeof(name), TYPE_ASPEED_SMC ".flash.%d", i); + if (!object_property_set_link(OBJECT(fl), "controller", OBJECT(s), + errp)) { + return; + } + if (!object_property_set_uint(OBJECT(fl), "cs", i, errp)) { + return; + } + if (!sysbus_realize(SYS_BUS_DEVICE(fl), errp)) { + return; + } - fl->cs = i; - fl->controller = s; - memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops, - fl, name, asc->segments[i].size); memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio); offset += asc->segments[i].size; } @@ -1231,12 +1245,57 @@ static void aspeed_smc_class_init(ObjectClass *klass, void *data) static const TypeInfo aspeed_smc_info = { .name = TYPE_ASPEED_SMC, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = aspeed_smc_instance_init, .instance_size = sizeof(AspeedSMCState), .class_size = sizeof(AspeedSMCClass), .class_init = aspeed_smc_class_init, .abstract = true, }; +static void aspeed_smc_flash_realize(DeviceState *dev, Error **errp) +{ + AspeedSMCFlash *s = ASPEED_SMC_FLASH(dev); + AspeedSMCClass *asc; + g_autofree char *name = g_strdup_printf(TYPE_ASPEED_SMC_FLASH ".%d", s->cs); + + if (!s->controller) { + error_setg(errp, TYPE_ASPEED_SMC_FLASH ": 'controller' link not set"); + return; + } + + asc = ASPEED_SMC_GET_CLASS(s->controller); + + /* + * Use the default segment value to size the memory region. This + * can be changed by FW at runtime. + */ + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_flash_ops, + s, name, asc->segments[s->cs].size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); +} + +static Property aspeed_smc_flash_properties[] = { + DEFINE_PROP_UINT8("cs", AspeedSMCFlash, cs, 0), + DEFINE_PROP_LINK("controller", AspeedSMCFlash, controller, TYPE_ASPEED_SMC, + AspeedSMCState *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void aspeed_smc_flash_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Aspeed SMC Flash device region"; + dc->realize = aspeed_smc_flash_realize; + device_class_set_props(dc, aspeed_smc_flash_properties); +} + +static const TypeInfo aspeed_smc_flash_info = { + .name = TYPE_ASPEED_SMC_FLASH, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSMCFlash), + .class_init = aspeed_smc_flash_class_init, +}; /* * The Segment Registers of the AST2400 and AST2500 have a 8MB @@ -1625,6 +1684,7 @@ static const TypeInfo aspeed_2600_spi2_info = { static void aspeed_smc_register_types(void) { + type_register_static(&aspeed_smc_flash_info); type_register_static(&aspeed_smc_info); type_register_static(&aspeed_2400_smc_info); type_register_static(&aspeed_2400_fmc_info); diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 40b6926b3e..ee943228b9 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -30,18 +30,23 @@ #include "qom/object.h" struct AspeedSMCState; -typedef struct AspeedSMCFlash { - struct AspeedSMCState *controller; +#define TYPE_ASPEED_SMC_FLASH "aspeed.smc.flash" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedSMCFlash, ASPEED_SMC_FLASH) +struct AspeedSMCFlash { + SysBusDevice parent_obj; + + struct AspeedSMCState *controller; uint8_t cs; MemoryRegion mmio; -} AspeedSMCFlash; +}; #define TYPE_ASPEED_SMC "aspeed.smc" OBJECT_DECLARE_TYPE(AspeedSMCState, AspeedSMCClass, ASPEED_SMC) #define ASPEED_SMC_R_MAX (0x100 / 4) +#define ASPEED_SMC_CS_MAX 5 struct AspeedSMCState { SysBusDevice parent_obj; @@ -72,7 +77,7 @@ struct AspeedSMCState { MemoryRegion *dram_mr; AddressSpace dram_as; - AspeedSMCFlash *flashes; + AspeedSMCFlash flashes[ASPEED_SMC_CS_MAX]; uint8_t snoop_index; uint8_t snoop_dummies; From 71255c48e7b902483b94862f4882ddd050fe8ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0388/1334] aspeed/smc: Add default reset values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies the reset handler and has the benefit to remove some "bad" use of the segments array as an identifier of the controller model. Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 52 +++++++++++++++++++------------------ include/hw/ssi/aspeed_smc.h | 1 + 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index c534e9bf87..8cc7ccf455 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -196,12 +196,9 @@ * controller. These can be changed when board is initialized with the * Segment Address Registers. */ -static const AspeedSegments aspeed_2400_fmc_segments[]; static const AspeedSegments aspeed_2400_spi1_segments[]; -static const AspeedSegments aspeed_2500_fmc_segments[]; static const AspeedSegments aspeed_2500_spi1_segments[]; static const AspeedSegments aspeed_2500_spi2_segments[]; -static const AspeedSegments aspeed_2600_fmc_segments[]; #define ASPEED_SMC_FEATURE_DMA 0x1 #define ASPEED_SMC_FEATURE_DMA_GRANT 0x2 @@ -686,7 +683,11 @@ static void aspeed_smc_reset(DeviceState *d) AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); int i; - memset(s->regs, 0, sizeof s->regs); + if (asc->resets) { + memcpy(s->regs, asc->resets, sizeof s->regs); + } else { + memset(s->regs, 0, sizeof s->regs); + } /* Unselect all peripherals */ for (i = 0; i < s->num_cs; ++i) { @@ -700,27 +701,6 @@ static void aspeed_smc_reset(DeviceState *d) asc->segment_to_reg(s, &asc->segments[i])); } - /* HW strapping flash type for the AST2600 controllers */ - if (asc->segments == aspeed_2600_fmc_segments) { - /* flash type is fixed to SPI for all */ - s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); - s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1); - s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE2); - } - - /* HW strapping flash type for FMC controllers */ - if (asc->segments == aspeed_2500_fmc_segments) { - /* flash type is fixed to SPI for CE0 and CE1 */ - s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); - s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1); - } - - /* HW strapping for AST2400 FMC controllers (SCU70). Let's use the - * configuration of the palmetto-bmc machine */ - if (asc->segments == aspeed_2400_fmc_segments) { - s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); - } - s->snoop_index = SNOOP_OFF; s->snoop_dummies = 0; } @@ -1352,6 +1332,14 @@ static const TypeInfo aspeed_2400_smc_info = { .class_init = aspeed_2400_smc_class_init, }; +static const uint32_t aspeed_2400_fmc_resets[ASPEED_SMC_R_MAX] = { + /* + * CE0 and CE1 types are HW strapped in SCU70. Do it here to + * simplify the model. + */ + [R_CONF] = CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0, +}; + static const AspeedSegments aspeed_2400_fmc_segments[] = { { 0x20000000, 64 * MiB }, /* start address is readonly */ { 0x24000000, 32 * MiB }, @@ -1374,6 +1362,7 @@ static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data) asc->conf_enable_w0 = CONF_ENABLE_W0; asc->max_peripherals = 5; asc->segments = aspeed_2400_fmc_segments; + asc->resets = aspeed_2400_fmc_resets; asc->flash_window_base = 0x20000000; asc->flash_window_size = 0x10000000; asc->features = ASPEED_SMC_FEATURE_DMA; @@ -1424,6 +1413,11 @@ static const TypeInfo aspeed_2400_spi1_info = { .class_init = aspeed_2400_spi1_class_init, }; +static const uint32_t aspeed_2500_fmc_resets[ASPEED_SMC_R_MAX] = { + [R_CONF] = (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0 | + CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1), +}; + static const AspeedSegments aspeed_2500_fmc_segments[] = { { 0x20000000, 128 * MiB }, /* start address is readonly */ { 0x28000000, 32 * MiB }, @@ -1444,6 +1438,7 @@ static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) asc->conf_enable_w0 = CONF_ENABLE_W0; asc->max_peripherals = 3; asc->segments = aspeed_2500_fmc_segments; + asc->resets = aspeed_2500_fmc_resets; asc->flash_window_base = 0x20000000; asc->flash_window_size = 0x10000000; asc->features = ASPEED_SMC_FEATURE_DMA; @@ -1569,6 +1564,12 @@ static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s, } } +static const uint32_t aspeed_2600_fmc_resets[ASPEED_SMC_R_MAX] = { + [R_CONF] = (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0 | + CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1 | + CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE2), +}; + static const AspeedSegments aspeed_2600_fmc_segments[] = { { 0x0, 128 * MiB }, /* start address is readonly */ { 128 * MiB, 128 * MiB }, /* default is disabled but needed for -kernel */ @@ -1589,6 +1590,7 @@ static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data) asc->conf_enable_w0 = CONF_ENABLE_W0; asc->max_peripherals = 3; asc->segments = aspeed_2600_fmc_segments; + asc->resets = aspeed_2600_fmc_resets; asc->flash_window_base = 0x20000000; asc->flash_window_size = 0x10000000; asc->features = ASPEED_SMC_FEATURE_DMA | diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index ee943228b9..a1ca0e65c4 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -98,6 +98,7 @@ struct AspeedSMCClass { uint8_t nregs_timings; uint8_t conf_enable_w0; uint8_t max_peripherals; + const uint32_t *resets; const AspeedSegments *segments; hwaddr flash_window_base; uint32_t flash_window_size; From a779e37c681f17f83e1d31a68c030731ebc8c998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0389/1334] aspeed/smc: Introduce a new addr_width() class handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2400 SPI controller has a transitional HW interface and it stores the address width currently in use in a different register than all the other SMC controllers. It needs special handling when working in 4B mode. Make it clear through a class handler. This also removes another use of the segments array. Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 19 ++++++++++++------- include/hw/ssi/aspeed_smc.h | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 8cc7ccf455..7129341c12 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -196,7 +196,6 @@ * controller. These can be changed when board is initialized with the * Segment Address Registers. */ -static const AspeedSegments aspeed_2400_spi1_segments[]; static const AspeedSegments aspeed_2500_spi1_segments[]; static const AspeedSegments aspeed_2500_spi2_segments[]; @@ -382,15 +381,15 @@ static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl) return cmd; } -static inline int aspeed_smc_flash_is_4byte(const AspeedSMCFlash *fl) +static inline int aspeed_smc_flash_addr_width(const AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); - if (asc->segments == aspeed_2400_spi1_segments) { - return s->regs[s->r_ctrl0] & CTRL_AST2400_SPI_4BYTE; + if (asc->addr_width) { + return asc->addr_width(s); } else { - return s->regs[s->r_ce_ctrl] & (1 << (CTRL_EXTENDED0 + fl->cs)); + return s->regs[s->r_ce_ctrl] & (1 << (CTRL_EXTENDED0 + fl->cs)) ? 4 : 3; } } @@ -450,7 +449,7 @@ static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr) { const AspeedSMCState *s = fl->controller; uint8_t cmd = aspeed_smc_flash_cmd(fl); - int i = aspeed_smc_flash_is_4byte(fl) ? 4 : 3; + int i = aspeed_smc_flash_addr_width(fl); /* Flash access can not exceed CS segment */ addr = aspeed_smc_check_segment_addr(fl, addr); @@ -558,7 +557,7 @@ static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data, unsigned size) { AspeedSMCState *s = fl->controller; - uint8_t addr_width = aspeed_smc_flash_is_4byte(fl) ? 4 : 3; + uint8_t addr_width = aspeed_smc_flash_addr_width(fl); trace_aspeed_smc_do_snoop(fl->cs, s->snoop_index, s->snoop_dummies, (uint8_t) data & 0xff); @@ -1384,6 +1383,11 @@ static const AspeedSegments aspeed_2400_spi1_segments[] = { { 0x30000000, 64 * MiB }, }; +static int aspeed_2400_spi1_addr_width(const AspeedSMCState *s) +{ + return s->regs[R_SPI_CTRL0] & CTRL_AST2400_SPI_4BYTE ? 4 : 3; +} + static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1405,6 +1409,7 @@ static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->addr_width = aspeed_2400_spi1_addr_width; } static const TypeInfo aspeed_2400_spi1_info = { diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index a1ca0e65c4..8dc8129498 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -111,6 +111,7 @@ struct AspeedSMCClass { void (*reg_to_segment)(const AspeedSMCState *s, uint32_t reg, AspeedSegments *seg); void (*dma_ctrl)(AspeedSMCState *s, uint32_t value); + int (*addr_width)(const AspeedSMCState *s); }; #endif /* ASPEED_SMC_H */ From 33456a8870f761ae4aba223a65082985d870bfc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0390/1334] aspeed/smc: Remove unused attribute 'irqline' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater --- include/hw/ssi/aspeed_smc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 8dc8129498..75bc793bd2 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -56,7 +56,6 @@ struct AspeedSMCState { MemoryRegion mmio_flash_alias; qemu_irq irq; - int irqline; uint32_t num_cs; qemu_irq *cs_lines; From 602610383f1be927830b903275ee02fcd368d6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0391/1334] aspeed/i2c: QOMify AspeedI2CBus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce an AspeedI2CBus SysBusDevice model and attach the associated memory region and IRQ to the newly instantiated objects. Before this change, the I2C bus IRQs were all attached to the SysBusDevice model of the I2C controller. Adapt the AST2600 SoC realize routine to take into account this change. Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 7 +-- hw/i2c/aspeed_i2c.c | 101 +++++++++++++++++++++++++++++------- include/hw/i2c/aspeed_i2c.h | 8 ++- 3 files changed, 91 insertions(+), 25 deletions(-) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index c69f27dff6..a70e4c48a7 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -337,11 +337,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[ASPEED_DEV_I2C] + i); - /* - * The AST2600 SoC has one IRQ per I2C bus. Skip the common - * IRQ (AST2400 and AST2500) and connect all bussses. - */ - sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), i + 1, irq); + /* The AST2600 I2C controller has one IRQ per bus. */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c.busses[i]), 0, irq); } /* FMC, The number of CS is set at the board level */ diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 8d276d9ed3..03a4f5a910 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -740,20 +740,20 @@ static const VMStateDescription aspeed_i2c_vmstate = { static void aspeed_i2c_reset(DeviceState *dev) { - int i; AspeedI2CState *s = ASPEED_I2C(dev); - AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); s->intr_status = 0; +} + +static void aspeed_i2c_instance_init(Object *obj) +{ + AspeedI2CState *s = ASPEED_I2C(obj); + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); + int i; for (i = 0; i < aic->num_busses; i++) { - s->busses[i].intr_ctrl = 0; - s->busses[i].intr_status = 0; - s->busses[i].cmd = 0; - s->busses[i].buf = 0; - s->busses[i].dma_addr = 0; - s->busses[i].dma_len = 0; - i2c_end_transfer(s->busses[i].bus); + object_initialize_child(obj, "bus[*]", &s->busses[i], + TYPE_ASPEED_I2C_BUS); } } @@ -791,17 +791,21 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); for (i = 0; i < aic->num_busses; i++) { - char name[32]; + Object *bus = OBJECT(&s->busses[i]); int offset = i < aic->gap ? 1 : 5; - sysbus_init_irq(sbd, &s->busses[i].irq); - snprintf(name, sizeof(name), "aspeed.i2c.%d", i); - s->busses[i].controller = s; - s->busses[i].id = i; - s->busses[i].bus = i2c_init_bus(dev, name); - memory_region_init_io(&s->busses[i].mr, OBJECT(dev), - &aspeed_i2c_bus_ops, &s->busses[i], name, - aic->reg_size); + if (!object_property_set_link(bus, "controller", OBJECT(s), errp)) { + return; + } + + if (!object_property_set_uint(bus, "bus-id", i, errp)) { + return; + } + + if (!sysbus_realize(SYS_BUS_DEVICE(bus), errp)) { + return; + } + memory_region_add_subregion(&s->iomem, aic->reg_size * (i + offset), &s->busses[i].mr); } @@ -841,12 +845,72 @@ static void aspeed_i2c_class_init(ObjectClass *klass, void *data) static const TypeInfo aspeed_i2c_info = { .name = TYPE_ASPEED_I2C, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = aspeed_i2c_instance_init, .instance_size = sizeof(AspeedI2CState), .class_init = aspeed_i2c_class_init, .class_size = sizeof(AspeedI2CClass), .abstract = true, }; +static void aspeed_i2c_bus_reset(DeviceState *dev) +{ + AspeedI2CBus *s = ASPEED_I2C_BUS(dev); + + s->intr_ctrl = 0; + s->intr_status = 0; + s->cmd = 0; + s->buf = 0; + s->dma_addr = 0; + s->dma_len = 0; + i2c_end_transfer(s->bus); +} + +static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) +{ + AspeedI2CBus *s = ASPEED_I2C_BUS(dev); + AspeedI2CClass *aic; + g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I2C_BUS ".%d", s->id); + + if (!s->controller) { + error_setg(errp, TYPE_ASPEED_I2C_BUS ": 'controller' link not set"); + return; + } + + aic = ASPEED_I2C_GET_CLASS(s->controller); + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + + s->bus = i2c_init_bus(dev, name); + + memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i2c_bus_ops, + s, name, aic->reg_size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); +} + +static Property aspeed_i2c_bus_properties[] = { + DEFINE_PROP_UINT8("bus-id", AspeedI2CBus, id, 0), + DEFINE_PROP_LINK("controller", AspeedI2CBus, controller, TYPE_ASPEED_I2C, + AspeedI2CState *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void aspeed_i2c_bus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Aspeed I2C Bus"; + dc->realize = aspeed_i2c_bus_realize; + dc->reset = aspeed_i2c_bus_reset; + device_class_set_props(dc, aspeed_i2c_bus_properties); +} + +static const TypeInfo aspeed_i2c_bus_info = { + .name = TYPE_ASPEED_I2C_BUS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedI2CBus), + .class_init = aspeed_i2c_bus_class_init, +}; + static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus) { return bus->controller->irq; @@ -951,6 +1015,7 @@ static const TypeInfo aspeed_2600_i2c_info = { static void aspeed_i2c_register_types(void) { + type_register_static(&aspeed_i2c_bus_info); type_register_static(&aspeed_i2c_info); type_register_static(&aspeed_2400_i2c_info); type_register_static(&aspeed_2500_i2c_info); diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 565f833066..4b9be09274 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -36,7 +36,11 @@ OBJECT_DECLARE_TYPE(AspeedI2CState, AspeedI2CClass, ASPEED_I2C) struct AspeedI2CState; -typedef struct AspeedI2CBus { +#define TYPE_ASPEED_I2C_BUS "aspeed.i2c.bus" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedI2CBus, ASPEED_I2C_BUS) +struct AspeedI2CBus { + SysBusDevice parent_obj; + struct AspeedI2CState *controller; MemoryRegion mr; @@ -54,7 +58,7 @@ typedef struct AspeedI2CBus { uint32_t pool_ctrl; uint32_t dma_addr; uint32_t dma_len; -} AspeedI2CBus; +}; struct AspeedI2CState { SysBusDevice parent_obj; From 9fffe140a9424e80af5f30a149c9f0d67424434f Mon Sep 17 00:00:00 2001 From: Peter Delevoryas Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0392/1334] hw: aspeed_gpio: Fix pin I/O type declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the pin declarations in the Aspeed GPIO module were incorrect, probably because of confusion over which bits in the input and output uint32_t's correspond to which groups in the label array. Since the uint32_t literals are in big endian, it's sort of the opposite of what would be intuitive. The least significant bit in ast2500_set_props[6] corresponds to GPIOY0, not GPIOAB7. GPIOxx indicates input and output capabilities, GPIxx indicates only input, GPOxx indicates only output. AST2500: - Previously had GPIW0..GPIW7 and GPIX0..GPIX7, that's correct. - Previously had GPIOY0..GPIOY3, should have been GPIOY0..GPIOY7. - Previously had GPIOAB0..GPIOAB3 and GPIAB4..GPIAB7, should only have been GPIOAB0..GPIOAB3. AST2600: - GPIOT0..GPIOT7 should have been GPIT0..GPIT7. - GPIOU0..GPIOU7 should have been GPIU0..GPIU7. - GPIW0..GPIW7 should have been GPIOW0..GPIOW7. - GPIOY0..GPIOY7 and GPIOZ0...GPIOZ7 were disabled. Fixes: 4b7f956862dc2db4c5c ("hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500") Fixes: 36d737ee82b2972167e ("hw/gpio: Add in AST2600 specific implementation") Signed-off-by: Peter Delevoryas Reviewed-by: Damien Hedde Reviewed-by: Rashmica Gupta Message-Id: <20210928032456.3192603-2-pdel@fb.com> Signed-off-by: Cédric Le Goater --- hw/gpio/aspeed_gpio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index dfa6d6cb40..33a40a624a 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -796,7 +796,7 @@ static const GPIOSetProperties ast2500_set_props[] = { [3] = {0xffffffff, 0xffffffff, {"M", "N", "O", "P"} }, [4] = {0xffffffff, 0xffffffff, {"Q", "R", "S", "T"} }, [5] = {0xffffffff, 0x0000ffff, {"U", "V", "W", "X"} }, - [6] = {0xffffff0f, 0x0fffff0f, {"Y", "Z", "AA", "AB"} }, + [6] = {0x0fffffff, 0x0fffffff, {"Y", "Z", "AA", "AB"} }, [7] = {0x000000ff, 0x000000ff, {"AC"} }, }; @@ -805,9 +805,9 @@ static GPIOSetProperties ast2600_3_3v_set_props[] = { [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} }, [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} }, [3] = {0xffffffff, 0xffffffff, {"M", "N", "O", "P"} }, - [4] = {0xffffffff, 0xffffffff, {"Q", "R", "S", "T"} }, - [5] = {0xffffffff, 0x0000ffff, {"U", "V", "W", "X"} }, - [6] = {0xffff0000, 0x0fff0000, {"Y", "Z", "", ""} }, + [4] = {0xffffffff, 0x00ffffff, {"Q", "R", "S", "T"} }, + [5] = {0xffffffff, 0xffffff00, {"U", "V", "W", "X"} }, + [6] = {0x0000ffff, 0x0000ffff, {"Y", "Z"} }, }; static GPIOSetProperties ast2600_1_8v_set_props[] = { From 87bd33e8b0d2e08a6030ffced9433e5927360de5 Mon Sep 17 00:00:00 2001 From: Peter Delevoryas Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0393/1334] hw: aspeed_gpio: Fix GPIO array indexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gpio array is declared as a dense array: qemu_irq gpios[ASPEED_GPIO_NR_PINS]; (AST2500 has 228, AST2400 has 216, AST2600 has 208) However, this array is used like a matrix of GPIO sets (e.g. gpio[NR_SETS][NR_PINS_PER_SET] = gpio[8][32]) size_t offset = set * GPIOS_PER_SET + gpio; qemu_set_irq(s->gpios[offset], !!(new & mask)); This can result in an out-of-bounds access to "s->gpios" because the gpio sets do _not_ have the same length. Some of the groups (e.g. GPIOAB) only have 4 pins. 228 != 8 * 32 == 256. To fix this, I converted the gpio array from dense to sparse, to that match both the hardware layout and this existing indexing code. Fixes: 4b7f956862dc2db4c5c ("hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500") Signed-off-by: Peter Delevoryas Message-Id: <20211008033501.934729-2-pdel@fb.com> Signed-off-by: Cédric Le Goater --- hw/gpio/aspeed_gpio.c | 78 ++++++++++++++--------------------- include/hw/gpio/aspeed_gpio.h | 5 +-- 2 files changed, 34 insertions(+), 49 deletions(-) diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 33a40a624a..911d21c8cf 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -16,11 +16,7 @@ #include "hw/irq.h" #include "migration/vmstate.h" -#define GPIOS_PER_REG 32 -#define GPIOS_PER_SET GPIOS_PER_REG -#define GPIO_PIN_GAP_SIZE 4 #define GPIOS_PER_GROUP 8 -#define GPIO_GROUP_SHIFT 3 /* GPIO Source Types */ #define ASPEED_CMD_SRC_MASK 0x01010101 @@ -259,7 +255,7 @@ static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs, diff = old ^ new; if (diff) { - for (gpio = 0; gpio < GPIOS_PER_REG; gpio++) { + for (gpio = 0; gpio < ASPEED_GPIOS_PER_SET; gpio++) { uint32_t mask = 1 << gpio; /* If the gpio needs to be updated... */ @@ -283,8 +279,7 @@ static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs, if (direction & mask) { /* ...trigger the line-state IRQ */ ptrdiff_t set = aspeed_gpio_set_idx(s, regs); - size_t offset = set * GPIOS_PER_SET + gpio; - qemu_set_irq(s->gpios[offset], !!(new & mask)); + qemu_set_irq(s->gpios[set][gpio], !!(new & mask)); } else { /* ...otherwise if we meet the line's current IRQ policy... */ if (aspeed_evaluate_irq(regs, old & mask, gpio)) { @@ -297,21 +292,6 @@ static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs, qemu_set_irq(s->irq, !!(s->pending)); } -static uint32_t aspeed_adjust_pin(AspeedGPIOState *s, uint32_t pin) -{ - AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); - /* - * The 2500 has a 4 pin gap in group AB and the 2400 has a 4 pin - * gap in group Y (and only four pins in AB but this is the last group so - * it doesn't matter). - */ - if (agc->gap && pin >= agc->gap) { - pin += GPIO_PIN_GAP_SIZE; - } - - return pin; -} - static bool aspeed_gpio_get_pin_level(AspeedGPIOState *s, uint32_t set_idx, uint32_t pin) { @@ -367,7 +347,7 @@ static uint32_t update_value_control_source(GPIOSets *regs, uint32_t old_value, uint32_t new_value = 0; /* for each group in set */ - for (i = 0; i < GPIOS_PER_REG; i += GPIOS_PER_GROUP) { + for (i = 0; i < ASPEED_GPIOS_PER_SET; i += GPIOS_PER_GROUP) { cmd_source = extract32(regs->cmd_source_0, i, 1) | (extract32(regs->cmd_source_1, i, 1) << 1); @@ -637,7 +617,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, * bidirectional | 1 | 1 | data * input only | 1 | 0 | 0 * output only | 0 | 1 | 1 - * no pin / gap | 0 | 0 | 0 + * no pin | 0 | 0 | 0 * * which is captured by: * data = ( data | ~input) & output; @@ -779,7 +759,7 @@ static void aspeed_gpio_set_pin(Object *obj, Visitor *v, const char *name, } /****************** Setup functions ******************/ -static const GPIOSetProperties ast2400_set_props[] = { +static const GPIOSetProperties ast2400_set_props[ASPEED_GPIO_MAX_NR_SETS] = { [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} }, [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} }, [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} }, @@ -789,7 +769,7 @@ static const GPIOSetProperties ast2400_set_props[] = { [6] = {0x0000000f, 0x0fffff0f, {"Y", "Z", "AA", "AB"} }, }; -static const GPIOSetProperties ast2500_set_props[] = { +static const GPIOSetProperties ast2500_set_props[ASPEED_GPIO_MAX_NR_SETS] = { [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} }, [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} }, [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} }, @@ -800,7 +780,7 @@ static const GPIOSetProperties ast2500_set_props[] = { [7] = {0x000000ff, 0x000000ff, {"AC"} }, }; -static GPIOSetProperties ast2600_3_3v_set_props[] = { +static GPIOSetProperties ast2600_3_3v_set_props[ASPEED_GPIO_MAX_NR_SETS] = { [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} }, [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} }, [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} }, @@ -810,7 +790,7 @@ static GPIOSetProperties ast2600_3_3v_set_props[] = { [6] = {0x0000ffff, 0x0000ffff, {"Y", "Z"} }, }; -static GPIOSetProperties ast2600_1_8v_set_props[] = { +static GPIOSetProperties ast2600_1_8v_set_props[ASPEED_GPIO_MAX_NR_SETS] = { [0] = {0xffffffff, 0xffffffff, {"18A", "18B", "18C", "18D"} }, [1] = {0x0000000f, 0x0000000f, {"18E"} }, }; @@ -836,14 +816,20 @@ static void aspeed_gpio_realize(DeviceState *dev, Error **errp) AspeedGPIOState *s = ASPEED_GPIO(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); - int pin; /* Interrupt parent line */ sysbus_init_irq(sbd, &s->irq); /* Individual GPIOs */ - for (pin = 0; pin < agc->nr_gpio_pins; pin++) { - sysbus_init_irq(sbd, &s->gpios[pin]); + for (int i = 0; i < ASPEED_GPIO_MAX_NR_SETS; i++) { + const GPIOSetProperties *props = &agc->props[i]; + uint32_t skip = ~(props->input | props->output); + for (int j = 0; j < ASPEED_GPIOS_PER_SET; j++) { + if (skip >> j & 1) { + continue; + } + sysbus_init_irq(sbd, &s->gpios[i][j]); + } } memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_gpio_ops, s, @@ -856,20 +842,22 @@ static void aspeed_gpio_init(Object *obj) { AspeedGPIOState *s = ASPEED_GPIO(obj); AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); - int pin; - for (pin = 0; pin < agc->nr_gpio_pins; pin++) { - char *name; - int set_idx = pin / GPIOS_PER_SET; - int pin_idx = aspeed_adjust_pin(s, pin) - (set_idx * GPIOS_PER_SET); - int group_idx = pin_idx >> GPIO_GROUP_SHIFT; - const GPIOSetProperties *props = &agc->props[set_idx]; - - name = g_strdup_printf("gpio%s%d", props->group_label[group_idx], - pin_idx % GPIOS_PER_GROUP); - object_property_add(obj, name, "bool", aspeed_gpio_get_pin, - aspeed_gpio_set_pin, NULL, NULL); - g_free(name); + for (int i = 0; i < ASPEED_GPIO_MAX_NR_SETS; i++) { + const GPIOSetProperties *props = &agc->props[i]; + uint32_t skip = ~(props->input | props->output); + for (int j = 0; j < ASPEED_GPIOS_PER_SET; j++) { + if (skip >> j & 1) { + continue; + } + int group_idx = j / GPIOS_PER_GROUP; + int pin_idx = j % GPIOS_PER_GROUP; + const char *group = &props->group_label[group_idx][0]; + char *name = g_strdup_printf("gpio%s%d", group, pin_idx); + object_property_add(obj, name, "bool", aspeed_gpio_get_pin, + aspeed_gpio_set_pin, NULL, NULL); + g_free(name); + } } } @@ -926,7 +914,6 @@ static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data) agc->props = ast2400_set_props; agc->nr_gpio_pins = 216; agc->nr_gpio_sets = 7; - agc->gap = 196; agc->reg_table = aspeed_3_3v_gpios; } @@ -937,7 +924,6 @@ static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data) agc->props = ast2500_set_props; agc->nr_gpio_pins = 228; agc->nr_gpio_sets = 8; - agc->gap = 220; agc->reg_table = aspeed_3_3v_gpios; } diff --git a/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h index e1636ce7fe..801846befb 100644 --- a/include/hw/gpio/aspeed_gpio.h +++ b/include/hw/gpio/aspeed_gpio.h @@ -17,9 +17,9 @@ OBJECT_DECLARE_TYPE(AspeedGPIOState, AspeedGPIOClass, ASPEED_GPIO) #define ASPEED_GPIO_MAX_NR_SETS 8 +#define ASPEED_GPIOS_PER_SET 32 #define ASPEED_REGS_PER_BANK 14 #define ASPEED_GPIO_MAX_NR_REGS (ASPEED_REGS_PER_BANK * ASPEED_GPIO_MAX_NR_SETS) -#define ASPEED_GPIO_NR_PINS 228 #define ASPEED_GROUPS_PER_SET 4 #define ASPEED_GPIO_NR_DEBOUNCE_REGS 3 #define ASPEED_CHARS_PER_GROUP_LABEL 4 @@ -60,7 +60,6 @@ struct AspeedGPIOClass { const GPIOSetProperties *props; uint32_t nr_gpio_pins; uint32_t nr_gpio_sets; - uint32_t gap; const AspeedGPIOReg *reg_table; }; @@ -72,7 +71,7 @@ struct AspeedGPIOState { MemoryRegion iomem; int pending; qemu_irq irq; - qemu_irq gpios[ASPEED_GPIO_NR_PINS]; + qemu_irq gpios[ASPEED_GPIO_MAX_NR_SETS][ASPEED_GPIOS_PER_SET]; /* Parallel GPIO Registers */ uint32_t debounce_regs[ASPEED_GPIO_NR_DEBOUNCE_REGS]; From 5857974d5d1133455e3c33e7c740786722418588 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0394/1334] hw/adc: Add basic Aspeed ADC model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This model implements enough behaviour to do basic functionality tests such as device initialisation and read out of dummy sample values. The sample value generation strategy is similar to the STM ADC already in the tree. Signed-off-by: Andrew Jeffery [clg : support for multiple engines (AST2600) ] Signed-off-by: Cédric Le Goater [pdel : refactored engine register struct fields to regs[] array field] [pdel : added guest-error checking for upper-8 channel regs in AST2600] [pdel : allow 16-bit reads of the channel data registers] Signed-off-by: Peter Delevoryas Message-Id: <20211005052604.1674891-2-pdel@fb.com> Signed-off-by: Cédric Le Goater --- hw/adc/aspeed_adc.c | 427 ++++++++++++++++++++++++++++++++++++ hw/adc/meson.build | 1 + hw/adc/trace-events | 3 + include/hw/adc/aspeed_adc.h | 55 +++++ 4 files changed, 486 insertions(+) create mode 100644 hw/adc/aspeed_adc.c create mode 100644 include/hw/adc/aspeed_adc.h diff --git a/hw/adc/aspeed_adc.c b/hw/adc/aspeed_adc.c new file mode 100644 index 0000000000..c5fcae29f6 --- /dev/null +++ b/hw/adc/aspeed_adc.c @@ -0,0 +1,427 @@ +/* + * Aspeed ADC + * + * Copyright 2017-2021 IBM Corp. + * + * Andrew Jeffery + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "hw/adc/aspeed_adc.h" +#include "trace.h" + +#define ASPEED_ADC_MEMORY_REGION_SIZE 0x1000 +#define ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE 0x100 +#define ASPEED_ADC_ENGINE_CH_EN_MASK 0xffff0000 +#define ASPEED_ADC_ENGINE_CH_EN(x) ((BIT(x)) << 16) +#define ASPEED_ADC_ENGINE_INIT BIT(8) +#define ASPEED_ADC_ENGINE_AUTO_COMP BIT(5) +#define ASPEED_ADC_ENGINE_COMP BIT(4) +#define ASPEED_ADC_ENGINE_MODE_MASK 0x0000000e +#define ASPEED_ADC_ENGINE_MODE_OFF (0b000 << 1) +#define ASPEED_ADC_ENGINE_MODE_STANDBY (0b001 << 1) +#define ASPEED_ADC_ENGINE_MODE_NORMAL (0b111 << 1) +#define ASPEED_ADC_ENGINE_EN BIT(0) +#define ASPEED_ADC_HYST_EN BIT(31) + +#define ASPEED_ADC_L_MASK ((1 << 10) - 1) +#define ASPEED_ADC_L(x) ((x) & ASPEED_ADC_L_MASK) +#define ASPEED_ADC_H(x) (((x) >> 16) & ASPEED_ADC_L_MASK) +#define ASPEED_ADC_LH_MASK (ASPEED_ADC_L_MASK << 16 | ASPEED_ADC_L_MASK) +#define LOWER_CHANNEL_MASK ((1 << 10) - 1) +#define LOWER_CHANNEL_DATA(x) ((x) & LOWER_CHANNEL_MASK) +#define UPPER_CHANNEL_DATA(x) (((x) >> 16) & LOWER_CHANNEL_MASK) + +#define TO_REG(addr) (addr >> 2) + +#define ENGINE_CONTROL TO_REG(0x00) +#define INTERRUPT_CONTROL TO_REG(0x04) +#define VGA_DETECT_CONTROL TO_REG(0x08) +#define CLOCK_CONTROL TO_REG(0x0C) +#define DATA_CHANNEL_1_AND_0 TO_REG(0x10) +#define DATA_CHANNEL_7_AND_6 TO_REG(0x1C) +#define DATA_CHANNEL_9_AND_8 TO_REG(0x20) +#define DATA_CHANNEL_15_AND_14 TO_REG(0x2C) +#define BOUNDS_CHANNEL_0 TO_REG(0x30) +#define BOUNDS_CHANNEL_7 TO_REG(0x4C) +#define BOUNDS_CHANNEL_8 TO_REG(0x50) +#define BOUNDS_CHANNEL_15 TO_REG(0x6C) +#define HYSTERESIS_CHANNEL_0 TO_REG(0x70) +#define HYSTERESIS_CHANNEL_7 TO_REG(0x8C) +#define HYSTERESIS_CHANNEL_8 TO_REG(0x90) +#define HYSTERESIS_CHANNEL_15 TO_REG(0xAC) +#define INTERRUPT_SOURCE TO_REG(0xC0) +#define COMPENSATING_AND_TRIMMING TO_REG(0xC4) + +static inline uint32_t update_channels(uint32_t current) +{ + return ((((current >> 16) & ASPEED_ADC_L_MASK) + 7) << 16) | + ((current + 5) & ASPEED_ADC_L_MASK); +} + +static bool breaks_threshold(AspeedADCEngineState *s, int reg) +{ + assert(reg >= DATA_CHANNEL_1_AND_0 && + reg < DATA_CHANNEL_1_AND_0 + s->nr_channels / 2); + + int a_bounds_reg = BOUNDS_CHANNEL_0 + (reg - DATA_CHANNEL_1_AND_0) * 2; + int b_bounds_reg = a_bounds_reg + 1; + uint32_t a_and_b = s->regs[reg]; + uint32_t a_bounds = s->regs[a_bounds_reg]; + uint32_t b_bounds = s->regs[b_bounds_reg]; + uint32_t a = ASPEED_ADC_L(a_and_b); + uint32_t b = ASPEED_ADC_H(a_and_b); + uint32_t a_lower = ASPEED_ADC_L(a_bounds); + uint32_t a_upper = ASPEED_ADC_H(a_bounds); + uint32_t b_lower = ASPEED_ADC_L(b_bounds); + uint32_t b_upper = ASPEED_ADC_H(b_bounds); + + return (a < a_lower || a > a_upper) || + (b < b_lower || b > b_upper); +} + +static uint32_t read_channel_sample(AspeedADCEngineState *s, int reg) +{ + assert(reg >= DATA_CHANNEL_1_AND_0 && + reg < DATA_CHANNEL_1_AND_0 + s->nr_channels / 2); + + /* Poor man's sampling */ + uint32_t value = s->regs[reg]; + s->regs[reg] = update_channels(s->regs[reg]); + + if (breaks_threshold(s, reg)) { + s->regs[INTERRUPT_CONTROL] |= BIT(reg - DATA_CHANNEL_1_AND_0); + qemu_irq_raise(s->irq); + } + + return value; +} + +static uint64_t aspeed_adc_engine_read(void *opaque, hwaddr addr, + unsigned int size) +{ + AspeedADCEngineState *s = ASPEED_ADC_ENGINE(opaque); + int reg = TO_REG(addr); + uint32_t value = 0; + + switch (reg) { + case BOUNDS_CHANNEL_8 ... BOUNDS_CHANNEL_15: + if (s->nr_channels <= 8) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: " + "bounds register %u invalid, only 0...7 valid\n", + __func__, s->engine_id, reg - BOUNDS_CHANNEL_0); + break; + } + /* fallthrough */ + case HYSTERESIS_CHANNEL_8 ... HYSTERESIS_CHANNEL_15: + if (s->nr_channels <= 8) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: " + "hysteresis register %u invalid, only 0...7 valid\n", + __func__, s->engine_id, reg - HYSTERESIS_CHANNEL_0); + break; + } + /* fallthrough */ + case BOUNDS_CHANNEL_0 ... BOUNDS_CHANNEL_7: + case HYSTERESIS_CHANNEL_0 ... HYSTERESIS_CHANNEL_7: + case ENGINE_CONTROL: + case INTERRUPT_CONTROL: + case VGA_DETECT_CONTROL: + case CLOCK_CONTROL: + case INTERRUPT_SOURCE: + case COMPENSATING_AND_TRIMMING: + value = s->regs[reg]; + break; + case DATA_CHANNEL_9_AND_8 ... DATA_CHANNEL_15_AND_14: + if (s->nr_channels <= 8) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: " + "data register %u invalid, only 0...3 valid\n", + __func__, s->engine_id, reg - DATA_CHANNEL_1_AND_0); + break; + } + /* fallthrough */ + case DATA_CHANNEL_1_AND_0 ... DATA_CHANNEL_7_AND_6: + value = read_channel_sample(s, reg); + /* Allow 16-bit reads of the data registers */ + if (addr & 0x2) { + assert(size == 2); + value >>= 16; + } + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: engine[%u]: 0x%" HWADDR_PRIx "\n", + __func__, s->engine_id, addr); + break; + } + + trace_aspeed_adc_engine_read(s->engine_id, addr, value); + return value; +} + +static void aspeed_adc_engine_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + AspeedADCEngineState *s = ASPEED_ADC_ENGINE(opaque); + int reg = TO_REG(addr); + uint32_t init = 0; + + trace_aspeed_adc_engine_write(s->engine_id, addr, value); + + switch (reg) { + case ENGINE_CONTROL: + init = !!(value & ASPEED_ADC_ENGINE_EN); + init *= ASPEED_ADC_ENGINE_INIT; + + value &= ~ASPEED_ADC_ENGINE_INIT; + value |= init; + + value &= ~ASPEED_ADC_ENGINE_AUTO_COMP; + break; + case INTERRUPT_CONTROL: + case VGA_DETECT_CONTROL: + case CLOCK_CONTROL: + break; + case DATA_CHANNEL_9_AND_8 ... DATA_CHANNEL_15_AND_14: + if (s->nr_channels <= 8) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: " + "data register %u invalid, only 0...3 valid\n", + __func__, s->engine_id, reg - DATA_CHANNEL_1_AND_0); + return; + } + /* fallthrough */ + case BOUNDS_CHANNEL_8 ... BOUNDS_CHANNEL_15: + if (s->nr_channels <= 8) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: " + "bounds register %u invalid, only 0...7 valid\n", + __func__, s->engine_id, reg - BOUNDS_CHANNEL_0); + return; + } + /* fallthrough */ + case DATA_CHANNEL_1_AND_0 ... DATA_CHANNEL_7_AND_6: + case BOUNDS_CHANNEL_0 ... BOUNDS_CHANNEL_7: + value &= ASPEED_ADC_LH_MASK; + break; + case HYSTERESIS_CHANNEL_8 ... HYSTERESIS_CHANNEL_15: + if (s->nr_channels <= 8) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: " + "hysteresis register %u invalid, only 0...7 valid\n", + __func__, s->engine_id, reg - HYSTERESIS_CHANNEL_0); + return; + } + /* fallthrough */ + case HYSTERESIS_CHANNEL_0 ... HYSTERESIS_CHANNEL_7: + value &= (ASPEED_ADC_HYST_EN | ASPEED_ADC_LH_MASK); + break; + case INTERRUPT_SOURCE: + value &= 0xffff; + break; + case COMPENSATING_AND_TRIMMING: + value &= 0xf; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: engine[%u]: " + "0x%" HWADDR_PRIx " 0x%" PRIx64 "\n", + __func__, s->engine_id, addr, value); + break; + } + + s->regs[reg] = value; +} + +static const MemoryRegionOps aspeed_adc_engine_ops = { + .read = aspeed_adc_engine_read, + .write = aspeed_adc_engine_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 2, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const uint32_t aspeed_adc_resets[ASPEED_ADC_NR_REGS] = { + [ENGINE_CONTROL] = 0x00000000, + [INTERRUPT_CONTROL] = 0x00000000, + [VGA_DETECT_CONTROL] = 0x0000000f, + [CLOCK_CONTROL] = 0x0000000f, +}; + +static void aspeed_adc_engine_reset(DeviceState *dev) +{ + AspeedADCEngineState *s = ASPEED_ADC_ENGINE(dev); + + memcpy(s->regs, aspeed_adc_resets, sizeof(aspeed_adc_resets)); +} + +static void aspeed_adc_engine_realize(DeviceState *dev, Error **errp) +{ + AspeedADCEngineState *s = ASPEED_ADC_ENGINE(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + g_autofree char *name = g_strdup_printf(TYPE_ASPEED_ADC_ENGINE ".%d", + s->engine_id); + + assert(s->engine_id < 2); + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_adc_engine_ops, s, name, + ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE); + + sysbus_init_mmio(sbd, &s->mmio); +} + +static const VMStateDescription vmstate_aspeed_adc_engine = { + .name = TYPE_ASPEED_ADC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AspeedADCEngineState, ASPEED_ADC_NR_REGS), + VMSTATE_END_OF_LIST(), + } +}; + +static Property aspeed_adc_engine_properties[] = { + DEFINE_PROP_UINT32("engine-id", AspeedADCEngineState, engine_id, 0), + DEFINE_PROP_UINT32("nr-channels", AspeedADCEngineState, nr_channels, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void aspeed_adc_engine_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_adc_engine_realize; + dc->reset = aspeed_adc_engine_reset; + device_class_set_props(dc, aspeed_adc_engine_properties); + dc->desc = "Aspeed Analog-to-Digital Engine"; + dc->vmsd = &vmstate_aspeed_adc_engine; +} + +static const TypeInfo aspeed_adc_engine_info = { + .name = TYPE_ASPEED_ADC_ENGINE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedADCEngineState), + .class_init = aspeed_adc_engine_class_init, +}; + +static void aspeed_adc_instance_init(Object *obj) +{ + AspeedADCState *s = ASPEED_ADC(obj); + AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(obj); + uint32_t nr_channels = ASPEED_ADC_NR_CHANNELS / aac->nr_engines; + + for (int i = 0; i < aac->nr_engines; i++) { + AspeedADCEngineState *engine = &s->engines[i]; + object_initialize_child(obj, "engine[*]", engine, + TYPE_ASPEED_ADC_ENGINE); + qdev_prop_set_uint32(DEVICE(engine), "engine-id", i); + qdev_prop_set_uint32(DEVICE(engine), "nr-channels", nr_channels); + } +} + +static void aspeed_adc_set_irq(void *opaque, int n, int level) +{ + AspeedADCState *s = opaque; + AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(s); + uint32_t pending = 0; + + /* TODO: update Global IRQ status register on AST2600 (Need specs) */ + for (int i = 0; i < aac->nr_engines; i++) { + uint32_t irq_status = s->engines[i].regs[INTERRUPT_CONTROL] & 0xFF; + pending |= irq_status << (i * 8); + } + + qemu_set_irq(s->irq, !!pending); +} + +static void aspeed_adc_realize(DeviceState *dev, Error **errp) +{ + AspeedADCState *s = ASPEED_ADC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(dev); + + qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_adc_set_irq, + s, NULL, aac->nr_engines); + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init(&s->mmio, OBJECT(s), TYPE_ASPEED_ADC, + ASPEED_ADC_MEMORY_REGION_SIZE); + + sysbus_init_mmio(sbd, &s->mmio); + + for (int i = 0; i < aac->nr_engines; i++) { + Object *eng = OBJECT(&s->engines[i]); + + if (!sysbus_realize(SYS_BUS_DEVICE(eng), errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(eng), 0, + qdev_get_gpio_in(DEVICE(sbd), i)); + memory_region_add_subregion(&s->mmio, + i * ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE, + &s->engines[i].mmio); + } +} + +static void aspeed_adc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); + + dc->realize = aspeed_adc_realize; + dc->desc = "Aspeed Analog-to-Digital Converter"; + aac->nr_engines = 1; +} + +static void aspeed_2600_adc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); + + dc->desc = "ASPEED 2600 ADC Controller"; + aac->nr_engines = 2; +} + +static const TypeInfo aspeed_adc_info = { + .name = TYPE_ASPEED_ADC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = aspeed_adc_instance_init, + .instance_size = sizeof(AspeedADCState), + .class_init = aspeed_adc_class_init, + .class_size = sizeof(AspeedADCClass), + .abstract = true, +}; + +static const TypeInfo aspeed_2400_adc_info = { + .name = TYPE_ASPEED_2400_ADC, + .parent = TYPE_ASPEED_ADC, +}; + +static const TypeInfo aspeed_2500_adc_info = { + .name = TYPE_ASPEED_2500_ADC, + .parent = TYPE_ASPEED_ADC, +}; + +static const TypeInfo aspeed_2600_adc_info = { + .name = TYPE_ASPEED_2600_ADC, + .parent = TYPE_ASPEED_ADC, + .class_init = aspeed_2600_adc_class_init, +}; + +static void aspeed_adc_register_types(void) +{ + type_register_static(&aspeed_adc_engine_info); + type_register_static(&aspeed_adc_info); + type_register_static(&aspeed_2400_adc_info); + type_register_static(&aspeed_2500_adc_info); + type_register_static(&aspeed_2600_adc_info); +} + +type_init(aspeed_adc_register_types); diff --git a/hw/adc/meson.build b/hw/adc/meson.build index ac4f093fea..b29ac7ccdf 100644 --- a/hw/adc/meson.build +++ b/hw/adc/meson.build @@ -1,4 +1,5 @@ softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) +softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c')) softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) softmmu_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c')) diff --git a/hw/adc/trace-events b/hw/adc/trace-events index 456f21c8f4..5a4c444d77 100644 --- a/hw/adc/trace-events +++ b/hw/adc/trace-events @@ -3,3 +3,6 @@ # npcm7xx_adc.c npcm7xx_adc_read(const char *id, uint64_t offset, uint32_t value) " %s offset: 0x%04" PRIx64 " value 0x%04" PRIx32 npcm7xx_adc_write(const char *id, uint64_t offset, uint32_t value) "%s offset: 0x%04" PRIx64 " value 0x%04" PRIx32 + +aspeed_adc_engine_read(uint32_t engine_id, uint64_t addr, uint64_t value) "engine[%u] 0x%" PRIx64 " 0x%" PRIx64 +aspeed_adc_engine_write(uint32_t engine_id, uint64_t addr, uint64_t value) "engine[%u] 0x%" PRIx64 " 0x%" PRIx64 diff --git a/include/hw/adc/aspeed_adc.h b/include/hw/adc/aspeed_adc.h new file mode 100644 index 0000000000..2f166e8be1 --- /dev/null +++ b/include/hw/adc/aspeed_adc.h @@ -0,0 +1,55 @@ +/* + * Aspeed ADC + * + * Copyright 2017-2021 IBM Corp. + * + * Andrew Jeffery + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_ADC_ASPEED_ADC_H +#define HW_ADC_ASPEED_ADC_H + +#include "hw/sysbus.h" + +#define TYPE_ASPEED_ADC "aspeed.adc" +#define TYPE_ASPEED_2400_ADC TYPE_ASPEED_ADC "-ast2400" +#define TYPE_ASPEED_2500_ADC TYPE_ASPEED_ADC "-ast2500" +#define TYPE_ASPEED_2600_ADC TYPE_ASPEED_ADC "-ast2600" +OBJECT_DECLARE_TYPE(AspeedADCState, AspeedADCClass, ASPEED_ADC) + +#define TYPE_ASPEED_ADC_ENGINE "aspeed.adc.engine" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedADCEngineState, ASPEED_ADC_ENGINE) + +#define ASPEED_ADC_NR_CHANNELS 16 +#define ASPEED_ADC_NR_REGS (0xD0 >> 2) + +struct AspeedADCEngineState { + /* */ + SysBusDevice parent; + + MemoryRegion mmio; + qemu_irq irq; + uint32_t engine_id; + uint32_t nr_channels; + uint32_t regs[ASPEED_ADC_NR_REGS]; +}; + +struct AspeedADCState { + /* */ + SysBusDevice parent; + + MemoryRegion mmio; + qemu_irq irq; + + AspeedADCEngineState engines[2]; +}; + +struct AspeedADCClass { + SysBusDeviceClass parent_class; + + uint32_t nr_engines; +}; + +#endif /* HW_ADC_ASPEED_ADC_H */ From 199fd6230cf9e774991b60e1ee8a0ab35b6c5f9a Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0395/1334] hw/arm: Integrate ADC model into Aspeed SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andrew Jeffery Signed-off-by: Cédric Le Goater Signed-off-by: Peter Delevoryas Message-Id: <20211005052604.1674891-3-pdel@fb.com> Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 11 +++++++++++ hw/arm/aspeed_soc.c | 11 +++++++++++ include/hw/arm/aspeed_soc.h | 2 ++ 3 files changed, 24 insertions(+) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index a70e4c48a7..0384357a95 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -148,6 +148,9 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); + snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); + object_initialize_child(obj, "adc", &s->adc, typename); + snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); object_initialize_child(obj, "i2c", &s->i2c, typename); @@ -322,6 +325,14 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } + /* ADC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); + /* UART - attach an 8250 to the IO space as our UART */ serial_mm_init(get_system_memory(), sc->memmap[s->uart_default], 2, aspeed_soc_get_irq(s, s->uart_default), 38400, diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 4f013dd5cd..7d53cf2f51 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -162,6 +162,9 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); + snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); + object_initialize_child(obj, "adc", &s->adc, typename); + snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); object_initialize_child(obj, "i2c", &s->i2c, typename); @@ -287,6 +290,14 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } + /* ADC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); + /* UART - attach an 8250 to the IO space as our UART */ serial_mm_init(get_system_memory(), sc->memmap[s->uart_default], 2, aspeed_soc_get_irq(s, s->uart_default), 38400, diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 87d76c9259..8139358549 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -15,6 +15,7 @@ #include "hw/cpu/a15mpcore.h" #include "hw/intc/aspeed_vic.h" #include "hw/misc/aspeed_scu.h" +#include "hw/adc/aspeed_adc.h" #include "hw/misc/aspeed_sdmc.h" #include "hw/misc/aspeed_xdma.h" #include "hw/timer/aspeed_timer.h" @@ -53,6 +54,7 @@ struct AspeedSoCState { AspeedSCUState scu; AspeedHACEState hace; AspeedXDMAState xdma; + AspeedADCState adc; AspeedSMCState fmc; AspeedSMCState spi[ASPEED_SPIS_NUM]; EHCISysBusState ehci[ASPEED_EHCIS_NUM]; From a8eb9a43337b3243204c0a5d270165ca2ac03910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0396/1334] aspeed/wdt: Add trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Francisco Iglesias Signed-off-by: Cédric Le Goater --- hw/watchdog/trace-events | 4 ++++ hw/watchdog/wdt_aspeed.c | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index c3bafbffa9..e7523e22aa 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -5,3 +5,7 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK AP cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 + +# wdt-aspeed.c +aspeed_wdt_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" +aspeed_wdt_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 69c37af9a6..146ffcd713 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -19,6 +19,7 @@ #include "hw/sysbus.h" #include "hw/watchdog/wdt_aspeed.h" #include "migration/vmstate.h" +#include "trace.h" #define WDT_STATUS (0x00 / 4) #define WDT_RELOAD_VALUE (0x04 / 4) @@ -60,6 +61,8 @@ static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) { AspeedWDTState *s = ASPEED_WDT(opaque); + trace_aspeed_wdt_read(offset, size); + offset >>= 2; switch (offset) { @@ -140,6 +143,8 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(s); bool enable; + trace_aspeed_wdt_write(offset, size, data); + offset >>= 2; switch (offset) { From e2804a1ec97ceede14b69a2a6e9a8b5dfa0b15c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Oct 2021 08:20:08 +0200 Subject: [PATCH 0397/1334] aspeed/smc: Dump address offset in trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The register index is currently printed and this is confusing. Reviewed-by: Francisco Iglesias Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 7129341c12..8a988c1676 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -728,7 +728,7 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) addr < R_SEG_ADDR0 + asc->max_peripherals) || (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + asc->max_peripherals)) { - trace_aspeed_smc_read(addr, size, s->regs[addr]); + trace_aspeed_smc_read(addr << 2, size, s->regs[addr]); return s->regs[addr]; } else { @@ -1029,10 +1029,10 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); uint32_t value = data; - addr >>= 2; - trace_aspeed_smc_write(addr, size, data); + addr >>= 2; + if (addr == s->r_conf || (addr >= s->r_timings && addr < s->r_timings + asc->nregs_timings) || From 9557af9ce94d434440d6397fb0d80748e4714e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 17 Sep 2021 17:23:22 +0100 Subject: [PATCH 0398/1334] configure: don't override the selected host test compiler if defined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are not many cases you would want to do this but one is if you want to use a test friendly compiler like gcc instead of a system compiler like clang. Either way we should honour the users choice if they have made it. Signed-off-by: Alex Bennée Cc: Warner Losh Reviewed-by: Warner Losh Message-Id: <20210917162332.3511179-2-alex.bennee@linaro.org> --- configure | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 877bf3d76a..e2750810e2 100755 --- a/configure +++ b/configure @@ -1686,8 +1686,10 @@ case "$cpu" in # No special flags required for other host CPUs esac -eval "cross_cc_${cpu}=\$cc" -cross_cc_vars="$cross_cc_vars cross_cc_${cpu}" +if eval test -z "\${cross_cc_$cpu}"; then + eval "cross_cc_${cpu}=\$cc" + cross_cc_vars="$cross_cc_vars cross_cc_${cpu}" +fi # For user-mode emulation the host arch has to be one we explicitly # support, even if we're using TCI. From 4f0ebed41809531e4e84658b8ac1742c4ba6966a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 17 Sep 2021 17:23:23 +0100 Subject: [PATCH 0399/1334] tests/tcg/sha1: remove endian include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This doesn't exist in BSD world and doesn't seem to be needed by either. Signed-off-by: Alex Bennée Reviewed-by: Warner Losh Message-Id: <20210917162332.3511179-3-alex.bennee@linaro.org> --- tests/tcg/multiarch/sha1.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tcg/multiarch/sha1.c b/tests/tcg/multiarch/sha1.c index 87bfbcdf52..0081bd7657 100644 --- a/tests/tcg/multiarch/sha1.c +++ b/tests/tcg/multiarch/sha1.c @@ -43,7 +43,6 @@ void SHA1Init(SHA1_CTX* context); void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); void SHA1Final(unsigned char digest[20], SHA1_CTX* context); /* ================ end of sha1.h ================ */ -#include #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) From 5343a837cdb0e10db05310e0da5a89843539b400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 17 Sep 2021 17:23:24 +0100 Subject: [PATCH 0400/1334] tests/tcg: move some multiarch files and make conditional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We had some messy code to filter out stuff we can't build. Lets junk that and simplify the logic by pushing some stuff into subdirs. In particular we move: float_helpers into libs - not a standalone test linux-test into linux - so we only build on Linux hosts This allows for at least some of the tests to be nominally usable by *BSD user builds. Signed-off-by: Alex Bennée Cc: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Warner Losh Message-Id: <20210917162332.3511179-4-alex.bennee@linaro.org> --- tests/tcg/multiarch/Makefile.target | 15 ++++++++++----- tests/tcg/multiarch/{ => libs}/float_helpers.c | 2 +- tests/tcg/multiarch/{ => linux}/linux-test.c | 0 tests/tcg/x86_64/Makefile.target | 4 ++++ 4 files changed, 15 insertions(+), 6 deletions(-) rename tests/tcg/multiarch/{ => libs}/float_helpers.c (99%) rename tests/tcg/multiarch/{ => linux}/linux-test.c (100%) diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 3f283eabe6..6ccb592aac 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -8,18 +8,23 @@ MULTIARCH_SRC=$(SRC_PATH)/tests/tcg/multiarch # Set search path for all sources -VPATH += $(MULTIARCH_SRC) -MULTIARCH_SRCS =$(notdir $(wildcard $(MULTIARCH_SRC)/*.c)) -MULTIARCH_TESTS =$(filter-out float_helpers, $(MULTIARCH_SRCS:.c=)) +VPATH += $(MULTIARCH_SRC) +MULTIARCH_SRCS = $(notdir $(wildcard $(MULTIARCH_SRC)/*.c)) +ifneq ($(CONFIG_LINUX),) +VPATH += $(MULTIARCH_SRC)/linux +MULTIARCH_SRCS += $(notdir $(wildcard $(MULTIARCH_SRC)/linux/*.c)) +endif +MULTIARCH_TESTS = $(MULTIARCH_SRCS:.c=) +$(info SRCS=${MULTIARCH_SRCS} and ${MULTIARCH_TESTS}) # # The following are any additional rules needed to build things # float_%: LDFLAGS+=-lm -float_%: float_%.c float_helpers.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< $(MULTIARCH_SRC)/float_helpers.c -o $@ $(LDFLAGS) +float_%: float_%.c libs/float_helpers.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< $(MULTIARCH_SRC)/libs/float_helpers.c -o $@ $(LDFLAGS) run-float_%: float_% $(call run-test,$<, $(QEMU) $(QEMU_OPTS) $<,"$< on $(TARGET_NAME)") diff --git a/tests/tcg/multiarch/float_helpers.c b/tests/tcg/multiarch/libs/float_helpers.c similarity index 99% rename from tests/tcg/multiarch/float_helpers.c rename to tests/tcg/multiarch/libs/float_helpers.c index bc530e5732..4e68d2b659 100644 --- a/tests/tcg/multiarch/float_helpers.c +++ b/tests/tcg/multiarch/libs/float_helpers.c @@ -22,7 +22,7 @@ #include #include -#include "float_helpers.h" +#include "../float_helpers.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) diff --git a/tests/tcg/multiarch/linux-test.c b/tests/tcg/multiarch/linux/linux-test.c similarity index 100% rename from tests/tcg/multiarch/linux-test.c rename to tests/tcg/multiarch/linux/linux-test.c diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index 2151ea6302..d7a7385583 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -8,8 +8,12 @@ include $(SRC_PATH)/tests/tcg/i386/Makefile.target +ifneq ($(CONFIG_LINUX),) X86_64_TESTS += vsyscall TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 +else +TESTS=$(MULTIARCH_TESTS) +endif QEMU_OPTS += -cpu max test-x86_64: LDFLAGS+=-lm -lc From 5c24acf320a0ac259447788162a3b17505ad4fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 17 Sep 2021 17:23:25 +0100 Subject: [PATCH 0401/1334] tests/docker: promote debian-riscv64-cross to a full image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be able to cross build QEMU itself we need to include a few more libraries. These are only available in Debian's unstable ports repo for now so we need to base the riscv64 image on sid with the the minimal libs needed to build QEMU (glib/pixman). The result works but is not as clean as using build-dep to bring in more dependencies. However sid is by definition a shifting pile of sand and by keeping the list of libs minimal we reduce the chance of having an image we can't build. It's good enough for a basic cross build testing of TCG. Cc: "Daniel P. Berrangé" Signed-off-by: Richard Henderson Message-Id: <20210914185830.1378771-2-richard.henderson@linaro.org> [AJB: tweak allow_failure] Signed-off-by: Alex Bennée Message-Id: <20210917162332.3511179-5-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 3 +- tests/docker/Makefile.include | 2 - .../dockerfiles/debian-riscv64-cross.docker | 46 +++++++++++++++++-- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 0fcebe363a..a3b5b90552 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -134,7 +134,8 @@ ppc64el-debian-cross-container: riscv64-debian-cross-container: extends: .container_job_template stage: containers-layer2 - needs: ['amd64-debian10-container'] + # as we are currently based on 'sid/unstable' we may break so... + allow_failure: true variables: NAME: debian-riscv64-cross diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 0806c6f726..450c76a3ca 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -141,7 +141,6 @@ docker-image-debian-mips64-cross: docker-image-debian10 docker-image-debian-mips64el-cross: docker-image-debian10 docker-image-debian-mipsel-cross: docker-image-debian10 docker-image-debian-ppc64el-cross: docker-image-debian10 -docker-image-debian-riscv64-cross: docker-image-debian10 docker-image-debian-s390x-cross: docker-image-debian10 docker-image-debian-sh4-cross: docker-image-debian10 docker-image-debian-sparc64-cross: docker-image-debian10 @@ -180,7 +179,6 @@ DOCKER_PARTIAL_IMAGES += debian-arm64-test-cross DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross DOCKER_PARTIAL_IMAGES += debian-hppa-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross -DOCKER_PARTIAL_IMAGES += debian-riscv64-cross DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-tricore-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 2bbff19772..594d97982c 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -1,12 +1,48 @@ # -# Docker cross-compiler target +# Docker cross-compiler target for riscv64 # -# This docker target builds on the debian Buster base image. +# Currently the only distro that gets close to cross compiling riscv64 +# images is Debian Sid (with unofficial ports). As this is a moving +# target we keep the library list minimal and are aiming to migrate +# from this hack as soon as we are able. # -FROM qemu/debian10 +FROM docker.io/library/debian:sid-slim + +# Add ports +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ + DEBIAN_FRONTEND=noninteractive eatmydata apt update -yy && \ + DEBIAN_FRONTEND=noninteractive eatmydata apt upgrade -yy + +# Install common build utilities +RUN DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy \ + bc \ + build-essential \ + ca-certificates \ + debian-ports-archive-keyring \ + dpkg-dev \ + gettext \ + git \ + ninja-build \ + pkg-config \ + python3 + +# Add ports and riscv64 architecture +RUN echo "deb http://ftp.ports.debian.org/debian-ports/ sid main" >> /etc/apt/sources.list +RUN dpkg --add-architecture riscv64 + +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list RUN apt update && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ - gcc-riscv64-linux-gnu \ - libc6-dev-riscv64-cross + gcc-riscv64-linux-gnu \ + libc6-dev-riscv64-cross \ + libffi-dev:riscv64 \ + libglib2.0-dev:riscv64 \ + libpixman-1-dev:riscv64 + +# Specify the cross prefix for this image (see tests/docker/common.rc) +ENV QEMU_CONFIGURE_OPTS --cross-prefix=riscv64-linux-gnu- +ENV DEF_TARGET_LIST riscv64-softmmu,riscv64-linux-user From 9f62025141c67c5dbe5c5a04becb12add2de12cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 17:23:26 +0100 Subject: [PATCH 0402/1334] gitlab: Add cross-riscv64-system, cross-riscv64-user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Message-Id: <20210914185830.1378771-3-richard.henderson@linaro.org> [AJB: add allow_failure] Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Willian Rampazzo Message-Id: <20210917162332.3511179-6-alex.bennee@linaro.org> --- .gitlab-ci.d/crossbuilds.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index f10168db2e..17d6cb3e45 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -124,6 +124,25 @@ cross-ppc64el-user: variables: IMAGE: debian-ppc64el-cross +# The riscv64 cross-builds currently use a 'sid' container to get +# compilers and libraries. Until something more stable is found we +# allow_failure so as not to block CI. +cross-riscv64-system: + extends: .cross_system_build_job + allow_failure: true + needs: + job: riscv64-debian-cross-container + variables: + IMAGE: debian-riscv64-cross + +cross-riscv64-user: + extends: .cross_user_build_job + allow_failure: true + needs: + job: riscv64-debian-cross-container + variables: + IMAGE: debian-riscv64-cross + cross-s390x-system: extends: .cross_system_build_job needs: From 9d03f5abede84a7b05877f7adeeea967d55566a0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 17 Sep 2021 17:23:28 +0100 Subject: [PATCH 0403/1334] travis.yml: Remove the "Release tarball" job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a leftover from the days when we were using Travis excessively, but since x86 jobs are not really usable there anymore, this job has likely never been used since many months. Let's simply remove it now. Signed-off-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Message-Id: <20210917094826.466047-1-thuth@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20210917162332.3511179-8-alex.bennee@linaro.org> --- .travis.yml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0faddf7b4e..41010ebe6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -305,26 +305,3 @@ jobs: - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools --host-cc=clang --cxx=clang++" - UNRELIABLE=true - - # Release builds - # The make-release script expect a QEMU version, so our tag must start with a 'v'. - # This is the case when release candidate tags are created. - - name: "Release tarball" - if: tag IS present AND tag =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/ - env: - # We want to build from the release tarball - - BUILD_DIR="release/build/dir" SRC_DIR="../../.." - - BASE_CONFIG="--prefix=$PWD/dist" - - CONFIG="--target-list=x86_64-softmmu,aarch64-softmmu,armeb-linux-user,ppc-linux-user" - - TEST_CMD="make install -j${JOBS}" - - QEMU_VERSION="${TRAVIS_TAG:1}" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - script: - - make -C ${SRC_DIR} qemu-${QEMU_VERSION}.tar.bz2 - - ls -l ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 - - tar -xf ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 && cd qemu-${QEMU_VERSION} - - mkdir -p release-build && cd release-build - - ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log meson-logs/meson-log.txt && exit 1; } - - make install - allow_failures: - - env: UNRELIABLE=true From dcbad7a6ed57f2885307b1c2236ca51796e638ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 17 Sep 2021 17:23:29 +0100 Subject: [PATCH 0404/1334] gitlab: skip the check-patch job on the upstream repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The check-patch job is intended to be used by contributors or subsystem maintainers to see if there are style mistakes. The false positive rate is too high to be used in a gating scenario so should not run it on the upstream repo ever. Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Acked-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20210915125452.1704899-2-berrange@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20210917162332.3511179-9-alex.bennee@linaro.org> --- .gitlab-ci.d/static_checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/static_checks.yml b/.gitlab-ci.d/static_checks.yml index 96dbd9e310..902843f8b3 100644 --- a/.gitlab-ci.d/static_checks.yml +++ b/.gitlab-ci.d/static_checks.yml @@ -8,7 +8,7 @@ check-patch: variables: GIT_DEPTH: 1000 rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + - if: '$CI_PROJECT_NAMESPACE == "qemu-project"' when: never - when: on_success allow_failure: true From f13abca0a3c55e2da35d2a0e6d6e0a75bb012885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 17 Sep 2021 17:23:30 +0100 Subject: [PATCH 0405/1334] gitlab: fix passing of TEST_TARGETS env to cirrus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A typo meant the substitution would not work, and the placeholder in the target file didn't even exist. The result was that tests were never run on the FreeBSD and macOS jobs, only a basic build. Signed-off-by: Daniel P. Berrangé Acked-by: Thomas Huth Reviewed-by: Willian Rampazzo Reviewed-by: Richard Henderson Message-Id: <20210915125452.1704899-3-berrange@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20210917162332.3511179-10-alex.bennee@linaro.org> --- .gitlab-ci.d/cirrus.yml | 2 +- .gitlab-ci.d/cirrus/build.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index 675db69622..e7b25e7427 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -35,7 +35,7 @@ -e "s|[@]PIP3@|$PIP3|g" -e "s|[@]PYPI_PKGS@|$PYPI_PKGS|g" -e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g" - -e "s|[@]TEST_TARGETSS@|$TEST_TARGETSS|g" + -e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g" <.gitlab-ci.d/cirrus/build.yml >.gitlab-ci.d/cirrus/$NAME.yml - cat .gitlab-ci.d/cirrus/$NAME.yml - cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml diff --git a/.gitlab-ci.d/cirrus/build.yml b/.gitlab-ci.d/cirrus/build.yml index 857bdc5536..c555f5d36e 100644 --- a/.gitlab-ci.d/cirrus/build.yml +++ b/.gitlab-ci.d/cirrus/build.yml @@ -13,6 +13,7 @@ env: PYTHON: "@PYTHON@" MAKE: "@MAKE@" CONFIGURE_ARGS: "@CONFIGURE_ARGS@" + TEST_TARGETS: "@TEST_TARGETS@" build_task: install_script: From 6a78a987c6826d44c9e70eddb63613e455f691e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20J=C3=BCnger?= Date: Fri, 17 Sep 2021 17:23:31 +0100 Subject: [PATCH 0406/1334] plugins/: Add missing functions to symbol list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some functions of the plugin API were missing in the symbol list. However, they are all used by the contributed example plugins. QEMU fails to load the plugin if the function symbol is not exported. Signed-off-by: Lukas Jünger Message-Id: <20210905140939.638928-2-lukas.junger@greensocs.com> Signed-off-by: Alex Bennée Message-Id: <20210917162332.3511179-11-alex.bennee@linaro.org> --- plugins/qemu-plugins.symbols | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 67b309ea2a..4834756ba3 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -1,11 +1,14 @@ { qemu_plugin_bool_parse; qemu_plugin_get_hwaddr; + qemu_plugin_hwaddr_device_name; qemu_plugin_hwaddr_is_io; + qemu_plugin_hwaddr_phys_addr; qemu_plugin_insn_data; qemu_plugin_insn_disas; qemu_plugin_insn_haddr; qemu_plugin_insn_size; + qemu_plugin_insn_symbol; qemu_plugin_insn_vaddr; qemu_plugin_mem_is_big_endian; qemu_plugin_mem_is_sign_extended; From 5d23d530235eaf352b1067854ad22681b1ab5584 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 11 Oct 2021 11:53:32 -0700 Subject: [PATCH 0407/1334] target/s390x: move tcg_gen_insn_start to s390x_tr_insn_start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use INDEX_op_insn_start to make the start of instruction boundaries. If we don't do it in the .insn_start hook things get confused especially now plugins want to use that marking to identify the start of instructions and will bomb out if it sees instrumented ops before the first instruction boundary. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Message-Id: <20211011185332.166763-1-richard.henderson@linaro.org> Signed-off-by: Alex Bennée --- target/s390x/tcg/translate.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index f284870cd2..a2d6fa5cca 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -138,6 +138,7 @@ struct DisasFields { struct DisasContext { DisasContextBase base; const DisasInsn *insn; + TCGOp *insn_start; DisasFields fields; uint64_t ex_value; /* @@ -6380,8 +6381,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* Search for the insn in the table. */ insn = extract_insn(env, s); - /* Emit insn_start now that we know the ILEN. */ - tcg_gen_insn_start(s->base.pc_next, s->cc_op, s->ilen); + /* Update insn_start now that we know the ILEN. */ + tcg_set_insn_start_param(s->insn_start, 2, s->ilen); /* Not found means unimplemented/illegal opcode. */ if (insn == NULL) { @@ -6552,6 +6553,11 @@ static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { + DisasContext *dc = container_of(dcbase, DisasContext, base); + + /* Delay the set of ilen until we've read the insn. */ + tcg_gen_insn_start(dc->base.pc_next, dc->cc_op, 0); + dc->insn_start = tcg_last_op(); } static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) From 453d50ce75b16d1b00a0783279779471e079f489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 17 Sep 2021 17:23:32 +0100 Subject: [PATCH 0408/1334] accel/tcg: re-factor plugin_inject_cb so we can assert insn_idx is valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity doesn't know enough about how we have arranged our plugin TCG ops to know we will always have incremented insn_idx before injecting the callback. Let us assert it for the benefit of Coverity and protect ourselves from accidentally breaking the assumption and triggering harder to grok errors deeper in the code if we attempt a negative indexed array lookup. However to get to this point we re-factor the code and remove the second hand instruction boundary detection in favour of scanning the full set of ops and using the existing INDEX_op_insn_start to cleanly detect when the instruction has started. As we no longer need the plugin specific list of ops we delete that. My initial benchmarks shows no discernible impact of dropping the plugin specific ops list. Fixes: Coverity 1459509 Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Cc: Peter Maydell Message-Id: <20210917162332.3511179-12-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 155 ++++++++++++++++++++++------------------- include/tcg/tcg.h | 6 -- 2 files changed, 84 insertions(+), 77 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index f5fd5f279c..61be64b78c 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -162,11 +162,7 @@ static void gen_empty_mem_helper(void) static void gen_plugin_cb_start(enum plugin_gen_from from, enum plugin_gen_cb type, unsigned wr) { - TCGOp *op; - tcg_gen_plugin_cb_start(from, type, wr); - op = tcg_last_op(); - QSIMPLEQ_INSERT_TAIL(&tcg_ctx->plugin_ops, op, plugin_link); } static void gen_wrapped(enum plugin_gen_from from, @@ -706,62 +702,6 @@ static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb, inject_mem_disable_helper(insn, begin_op); } -static void plugin_inject_cb(const struct qemu_plugin_tb *ptb, TCGOp *begin_op, - int insn_idx) -{ - enum plugin_gen_from from = begin_op->args[0]; - enum plugin_gen_cb type = begin_op->args[1]; - - switch (from) { - case PLUGIN_GEN_FROM_TB: - switch (type) { - case PLUGIN_GEN_CB_UDATA: - plugin_gen_tb_udata(ptb, begin_op); - return; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_tb_inline(ptb, begin_op); - return; - default: - g_assert_not_reached(); - } - case PLUGIN_GEN_FROM_INSN: - switch (type) { - case PLUGIN_GEN_CB_UDATA: - plugin_gen_insn_udata(ptb, begin_op, insn_idx); - return; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_insn_inline(ptb, begin_op, insn_idx); - return; - case PLUGIN_GEN_ENABLE_MEM_HELPER: - plugin_gen_enable_mem_helper(ptb, begin_op, insn_idx); - return; - default: - g_assert_not_reached(); - } - case PLUGIN_GEN_FROM_MEM: - switch (type) { - case PLUGIN_GEN_CB_MEM: - plugin_gen_mem_regular(ptb, begin_op, insn_idx); - return; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_mem_inline(ptb, begin_op, insn_idx); - return; - default: - g_assert_not_reached(); - } - case PLUGIN_GEN_AFTER_INSN: - switch (type) { - case PLUGIN_GEN_DISABLE_MEM_HELPER: - plugin_gen_disable_mem_helper(ptb, begin_op, insn_idx); - return; - default: - g_assert_not_reached(); - } - default: - g_assert_not_reached(); - } -} - /* #define DEBUG_PLUGIN_GEN_OPS */ static void pr_ops(void) { @@ -819,21 +759,95 @@ static void pr_ops(void) static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) { TCGOp *op; - int insn_idx; + int insn_idx = -1; pr_ops(); - insn_idx = -1; - QSIMPLEQ_FOREACH(op, &tcg_ctx->plugin_ops, plugin_link) { - enum plugin_gen_from from = op->args[0]; - enum plugin_gen_cb type = op->args[1]; - tcg_debug_assert(op->opc == INDEX_op_plugin_cb_start); - /* ENABLE_MEM_HELPER is the first callback of an instruction */ - if (from == PLUGIN_GEN_FROM_INSN && - type == PLUGIN_GEN_ENABLE_MEM_HELPER) { + QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { + switch (op->opc) { + case INDEX_op_insn_start: insn_idx++; + break; + case INDEX_op_plugin_cb_start: + { + enum plugin_gen_from from = op->args[0]; + enum plugin_gen_cb type = op->args[1]; + + switch (from) { + case PLUGIN_GEN_FROM_TB: + { + g_assert(insn_idx == -1); + + switch (type) { + case PLUGIN_GEN_CB_UDATA: + plugin_gen_tb_udata(plugin_tb, op); + break; + case PLUGIN_GEN_CB_INLINE: + plugin_gen_tb_inline(plugin_tb, op); + break; + default: + g_assert_not_reached(); + } + break; + } + case PLUGIN_GEN_FROM_INSN: + { + g_assert(insn_idx >= 0); + + switch (type) { + case PLUGIN_GEN_CB_UDATA: + plugin_gen_insn_udata(plugin_tb, op, insn_idx); + break; + case PLUGIN_GEN_CB_INLINE: + plugin_gen_insn_inline(plugin_tb, op, insn_idx); + break; + case PLUGIN_GEN_ENABLE_MEM_HELPER: + plugin_gen_enable_mem_helper(plugin_tb, op, insn_idx); + break; + default: + g_assert_not_reached(); + } + break; + } + case PLUGIN_GEN_FROM_MEM: + { + g_assert(insn_idx >= 0); + + switch (type) { + case PLUGIN_GEN_CB_MEM: + plugin_gen_mem_regular(plugin_tb, op, insn_idx); + break; + case PLUGIN_GEN_CB_INLINE: + plugin_gen_mem_inline(plugin_tb, op, insn_idx); + break; + default: + g_assert_not_reached(); + } + + break; + } + case PLUGIN_GEN_AFTER_INSN: + { + g_assert(insn_idx >= 0); + + switch (type) { + case PLUGIN_GEN_DISABLE_MEM_HELPER: + plugin_gen_disable_mem_helper(plugin_tb, op, insn_idx); + break; + default: + g_assert_not_reached(); + } + break; + } + default: + g_assert_not_reached(); + } + break; + } + default: + /* plugins don't care about any other ops */ + break; } - plugin_inject_cb(plugin_tb, op, insn_idx); } pr_ops(); } @@ -846,7 +860,6 @@ bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_onl if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) { ret = true; - QSIMPLEQ_INIT(&tcg_ctx->plugin_ops); ptb->vaddr = tb->pc; ptb->vaddr2 = -1; get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1); diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index ba13ab1151..9f398b9afe 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -493,9 +493,6 @@ typedef struct TCGOp { /* Next and previous opcodes. */ QTAILQ_ENTRY(TCGOp) link; -#ifdef CONFIG_PLUGIN - QSIMPLEQ_ENTRY(TCGOp) plugin_link; -#endif /* Arguments for the opcode. */ TCGArg args[MAX_OPC_PARAM]; @@ -605,9 +602,6 @@ struct TCGContext { /* descriptor of the instruction being translated */ struct qemu_plugin_insn *plugin_insn; - - /* list to quickly access the injected ops */ - QSIMPLEQ_HEAD(, TCGOp) plugin_ops; #endif GHashTable *const_table[TCG_TYPE_COUNT]; From 9b89cdb2a5064a87b8a7172fa1748d46aa37a9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 4 Oct 2021 16:43:08 +0100 Subject: [PATCH 0409/1334] .github: move repo lockdown to the v2 configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I was getting prompted by GitHub for new permissions but it turns out per https://github.com/dessant/repo-lockdown/issues/6: Repo Lockdown has been rewritten for GitHub Actions, offering new features and better control over your automation presets. The legacy GitHub App has been deprecated, and the public instance of the app has been shut down. So this is what I've done. As the issues tab is disabled I've removed the handling for issues from the new version. Signed-off-by: Alex Bennée Reviewed-by: Willian Rampazzo Message-Id: <20211004154308.2114870-1-alex.bennee@linaro.org> --- .github/lockdown.yml | 34 ---------------------------------- .github/workflows/lockdown.yml | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 34 deletions(-) delete mode 100644 .github/lockdown.yml create mode 100644 .github/workflows/lockdown.yml diff --git a/.github/lockdown.yml b/.github/lockdown.yml deleted file mode 100644 index d3546bd2bc..0000000000 --- a/.github/lockdown.yml +++ /dev/null @@ -1,34 +0,0 @@ -# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown - -# Close issues and pull requests -close: true - -# Lock issues and pull requests -lock: true - -issues: - comment: | - Thank you for your interest in the QEMU project. - - This repository is a read-only mirror of the project's repostories hosted - at https://gitlab.com/qemu-project/qemu.git. - The project does not process issues filed on GitHub. - - The project issues are tracked on GitLab: - https://gitlab.com/qemu-project/qemu/-/issues - - QEMU welcomes bug report contributions. You can file new ones on: - https://gitlab.com/qemu-project/qemu/-/issues/new - -pulls: - comment: | - Thank you for your interest in the QEMU project. - - This repository is a read-only mirror of the project's repostories hosted - on https://gitlab.com/qemu-project/qemu.git. - The project does not process merge requests filed on GitHub. - - QEMU welcomes contributions of code (either fixing bugs or adding new - functionality). However, we get a lot of patches, and so we have some - guidelines about contributing on the project website: - https://www.qemu.org/contribute/ diff --git a/.github/workflows/lockdown.yml b/.github/workflows/lockdown.yml new file mode 100644 index 0000000000..ad8b8f7e30 --- /dev/null +++ b/.github/workflows/lockdown.yml @@ -0,0 +1,30 @@ +# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown + +name: 'Repo Lockdown' + +on: + pull_request_target: + types: opened + +permissions: + pull-requests: write + +jobs: + action: + runs-on: ubuntu-latest + steps: + - uses: dessant/repo-lockdown@v2 + with: + pull-comment: | + Thank you for your interest in the QEMU project. + + This repository is a read-only mirror of the project's repostories hosted + on https://gitlab.com/qemu-project/qemu.git. + The project does not process merge requests filed on GitHub. + + QEMU welcomes contributions of code (either fixing bugs or adding new + functionality). However, we get a lot of patches, and so we have some + guidelines about contributing on the project website: + https://www.qemu.org/contribute/ + lock-pull: true + close-pull: true From 17888749ba0fb06694de5efe0b941f16a0fba6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 22 Sep 2021 16:15:27 +0100 Subject: [PATCH 0410/1334] tests/docker: add a debian-native image and make available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This image is intended for building whatever the native versions of QEMU are for the host architecture. This will hopefully be an aid for 3rd parties who want to be able to build QEMU themselves without redoing all the dependencies themselves. We disable the registry because we currently don't have multi-arch support there. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Tested-by: Anders Roxell Acked-by: Willian Rampazzo Message-Id: <20210922151528.2192966-1-alex.bennee@linaro.org> --- tests/docker/Makefile.include | 4 ++ tests/docker/common.rc | 10 +++- tests/docker/dockerfiles/debian-native.docker | 49 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/docker/dockerfiles/debian-native.docker diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 450c76a3ca..b9d4094c2e 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -145,6 +145,10 @@ docker-image-debian-s390x-cross: docker-image-debian10 docker-image-debian-sh4-cross: docker-image-debian10 docker-image-debian-sparc64-cross: docker-image-debian10 +# The native build should never use the registry +docker-image-debian-native: DOCKER_REGISTRY= + + # # The build rule for hexagon-cross is special in so far for most of # the time we don't want to build it. While dockers caching does avoid diff --git a/tests/docker/common.rc b/tests/docker/common.rc index c5cc33d366..e6f8cee0d6 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -12,8 +12,14 @@ # the top-level directory. # This might be set by ENV of a docker container... it is always -# overriden by TARGET_LIST if the user sets it. -DEF_TARGET_LIST=${DEF_TARGET_LIST:-"x86_64-softmmu,aarch64-softmmu"} +# overriden by TARGET_LIST if the user sets it. We special case +# "none" to allow for other options like --disable-tcg to restrict the +# builds we eventually do. +if test "$DEF_TARGET_LIST" = "none"; then + DEF_TARGET_LIST="" +else + DEF_TARGET_LIST=${DEF_TARGET_LIST:-"x86_64-softmmu,aarch64-softmmu"} +fi requires_binary() { diff --git a/tests/docker/dockerfiles/debian-native.docker b/tests/docker/dockerfiles/debian-native.docker new file mode 100644 index 0000000000..efd55cb6e0 --- /dev/null +++ b/tests/docker/dockerfiles/debian-native.docker @@ -0,0 +1,49 @@ +# +# Docker Debian Native +# +# This this intended to build QEMU on native host systems. Debian is +# chosen due to the broadest range on supported host systems for QEMU. +# +# This docker target is based on the docker.io Debian Bullseye base +# image rather than QEMU's base because we would otherwise confuse the +# build grabbing stuff from the registry built for other +# architectures. +# +FROM docker.io/library/debian:bullseye-slim +MAINTAINER Alex Bennée + +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list + +# Install common build utilities +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata + +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt build-dep -yy --arch-only qemu + +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + cscope \ + genisoimage \ + exuberant-ctags \ + global \ + libbz2-dev \ + liblzo2-dev \ + libgcrypt20-dev \ + libfdt-dev \ + librdmacm-dev \ + libsasl2-dev \ + libsnappy-dev \ + libvte-dev \ + netcat-openbsd \ + ninja-build \ + openssh-client \ + python3-numpy \ + python3-opencv \ + python3-venv + +ENV QEMU_CONFIGURE_OPTS $QEMU_CONFIGURE_OPTS +ENV DEF_TARGET_LIST "none" From 0257209a09793bfaf1b662faad5558e7011e748a Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:22 -0400 Subject: [PATCH 0411/1334] python/aqmp: add greeting property to QMPClient Expose the greeting as a read-only property of QMPClient so it can be retrieved at-will. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-2-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/qmp_client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/qemu/aqmp/qmp_client.py b/python/qemu/aqmp/qmp_client.py index 82e9dab124..d2ad7459f9 100644 --- a/python/qemu/aqmp/qmp_client.py +++ b/python/qemu/aqmp/qmp_client.py @@ -224,6 +224,11 @@ class QMPClient(AsyncProtocol[Message], Events): 'asyncio.Queue[QMPClient._PendingT]' ] = {} + @property + def greeting(self) -> Optional[Greeting]: + """The `Greeting` from the QMP server, if any.""" + return self._greeting + @upper_half async def _establish_session(self) -> None: """ From 16cce725ed87e3ed5901c93e61514840c989216a Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:23 -0400 Subject: [PATCH 0412/1334] python/aqmp: add .empty() method to EventListener Synchronous clients may want to know if they're about to block waiting for an event or not. A method such as this is necessary to implement a compatible interface for the old QEMUMonitorProtocol using the new async internals. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-3-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/events.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/qemu/aqmp/events.py b/python/qemu/aqmp/events.py index fb81d21610..271899f6b8 100644 --- a/python/qemu/aqmp/events.py +++ b/python/qemu/aqmp/events.py @@ -556,6 +556,12 @@ class EventListener: """ return await self._queue.get() + def empty(self) -> bool: + """ + Return `True` if there are no pending events. + """ + return self._queue.empty() + def clear(self) -> None: """ Clear this listener of all pending events. From 6bfebc7306e42910cd33553d9ed385ef475d8196 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:24 -0400 Subject: [PATCH 0413/1334] python/aqmp: Return cleared events from EventListener.clear() This serves two purposes: (1) It is now possible to discern whether or not clear() removed any event(s) from the queue with absolute certainty, and (2) It is now very easy to get a List of all pending events in one chunk, which is useful for the sync bridge. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-4-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/events.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/python/qemu/aqmp/events.py b/python/qemu/aqmp/events.py index 271899f6b8..5f7150c78d 100644 --- a/python/qemu/aqmp/events.py +++ b/python/qemu/aqmp/events.py @@ -562,7 +562,7 @@ class EventListener: """ return self._queue.empty() - def clear(self) -> None: + def clear(self) -> List[Message]: """ Clear this listener of all pending events. @@ -570,17 +570,22 @@ class EventListener: pending FIFO queue synchronously. It can be also be used to manually clear any pending events, if desired. + :return: The cleared events, if any. + .. warning:: Take care when discarding events. Cleared events will be silently tossed on the floor. All events that were ever accepted by this listener are visible in `history()`. """ + events = [] while True: try: - self._queue.get_nowait() + events.append(self._queue.get_nowait()) except asyncio.QueueEmpty: break + return events + def __aiter__(self) -> AsyncIterator[Message]: return self From 6e2f6ec5615f50c38e21542bb66bb690a289cbfc Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:25 -0400 Subject: [PATCH 0414/1334] python/aqmp: add send_fd_scm Add an implementation for send_fd_scm to the async QMP implementation. Like socket_scm_helper mentions, a non-empty payload is required for QEMU to process the ancillary data. A space is most useful because it does not disturb the parsing of subsequent JSON objects. A note on "voiding the warranty": Python 3.11 removes support for calling sendmsg directly from a transport's socket. There is no other interface for doing this, our use case is, I suspect, "quite unique". As far as I can tell, this is safe to do -- send_fd_scm is a synchronous function and we can be guaranteed that the async coroutines will *not* be running when it is invoked. In testing, it works correctly. I investigated quite thoroughly the possibility of creating my own asyncio Transport (The class that ultimately manages the raw socket object) so that I could manage the socket myself, but this is so wildly invasive and unportable I scrapped the idea. It would involve a lot of copy-pasting of various python utilities and classes just to re-create the same infrastructure, and for extremely little benefit. Nah. Just boldly void the warranty instead, while I try to follow up on https://bugs.python.org/issue43232 Signed-off-by: John Snow Reviewed-by: Paolo Bonzini Reviewed-by: Eric Blake Message-id: 20210923004938.3999963-5-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/qmp_client.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/python/qemu/aqmp/qmp_client.py b/python/qemu/aqmp/qmp_client.py index d2ad7459f9..f987da02eb 100644 --- a/python/qemu/aqmp/qmp_client.py +++ b/python/qemu/aqmp/qmp_client.py @@ -9,6 +9,8 @@ accept an incoming connection from that server. import asyncio import logging +import socket +import struct from typing import ( Dict, List, @@ -624,3 +626,23 @@ class QMPClient(AsyncProtocol[Message], Events): """ msg = self.make_execute_msg(cmd, arguments, oob=oob) return await self.execute_msg(msg) + + @upper_half + @require(Runstate.RUNNING) + def send_fd_scm(self, fd: int) -> None: + """ + Send a file descriptor to the remote via SCM_RIGHTS. + """ + assert self._writer is not None + sock = self._writer.transport.get_extra_info('socket') + + if sock.family != socket.AF_UNIX: + raise AQMPError("Sending file descriptors requires a UNIX socket.") + + # Void the warranty sticker. + # Access to sendmsg in asyncio is scheduled for removal in Python 3.11. + sock = sock._sock # pylint: disable=protected-access + sock.sendmsg( + [b' '], + [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))] + ) From 58026b11f36f9a19fd35fdfbfa50be7bd78e70a5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:26 -0400 Subject: [PATCH 0415/1334] python/aqmp: Add dict conversion method to Greeting object The iotests interface expects to return the greeting as a dict; AQMP offers it as a rich object. Signed-off-by: John Snow Reviewed-by: Paolo Bonzini Reviewed-by: Eric Blake Message-id: 20210923004938.3999963-6-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/models.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/python/qemu/aqmp/models.py b/python/qemu/aqmp/models.py index 24c94123ac..de87f87804 100644 --- a/python/qemu/aqmp/models.py +++ b/python/qemu/aqmp/models.py @@ -8,8 +8,10 @@ data to make sure it conforms to spec. # pylint: disable=too-few-public-methods from collections import abc +import copy from typing import ( Any, + Dict, Mapping, Optional, Sequence, @@ -66,6 +68,17 @@ class Greeting(Model): self._check_member('QMP', abc.Mapping, "JSON object") self.QMP = QMPGreeting(self._raw['QMP']) + def _asdict(self) -> Dict[str, object]: + """ + For compatibility with the iotests sync QMP wrapper. + + The legacy QMP interface needs Greetings as a garden-variety Dict. + + This interface is private in the hopes that it will be able to + be dropped again in the near-future. Caller beware! + """ + return dict(copy.deepcopy(self._raw)) + class QMPGreeting(Model): """ From 3e55dc35b8ed8cea53ef2604874d2fe882fbadc1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:27 -0400 Subject: [PATCH 0416/1334] python/aqmp: Reduce severity of EOFError-caused loop terminations When we encounter an EOFError, we don't know if it's an "error" in the perspective of the user of the library yet. Therefore, we should not log it as an error. Reduce the severity of this logging message to "INFO" to indicate that it's something that we expect to occur during the normal operation of the library. Signed-off-by: John Snow Reviewed-by: Paolo Bonzini Reviewed-by: Eric Blake Message-id: 20210923004938.3999963-7-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/protocol.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/qemu/aqmp/protocol.py b/python/qemu/aqmp/protocol.py index 32e78749c1..ae1df24026 100644 --- a/python/qemu/aqmp/protocol.py +++ b/python/qemu/aqmp/protocol.py @@ -721,8 +721,11 @@ class AsyncProtocol(Generic[T]): self.logger.debug("Task.%s: cancelled.", name) return except BaseException as err: - self.logger.error("Task.%s: %s", - name, exception_summary(err)) + self.logger.log( + logging.INFO if isinstance(err, EOFError) else logging.ERROR, + "Task.%s: %s", + name, exception_summary(err) + ) self.logger.debug("Task.%s: failure:\n%s\n", name, pretty_traceback()) self._schedule_disconnect() From 3a3d84f5ec0bca2586a1a83691182576a9447130 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:28 -0400 Subject: [PATCH 0417/1334] python/aqmp: Disable logging messages by default AQMP is a library, and ideally it should not print error diagnostics unless a user opts into seeing them. By default, Python will print all WARNING, ERROR or CRITICAL messages to screen if no logging configuration has been created by a client application. In AQMP's case, ERROR logging statements are used to report additional detail about runtime failures that will also eventually be reported to the client library via an Exception, so these messages should not be rendered by default. (Why bother to have them at all, then? In async contexts, there may be multiple Exceptions and we are only able to report one of them back to the client application. It is not reasonably easy to predict ahead of time if one or more of these Exceptions will be squelched. Therefore, it's useful to log intermediate failures to help make sense of the ultimate, resulting failure.) Add a NullHandler that will suppress these messages until a client application opts into logging via logging.basicConfig or similar. Note that upon calling basicConfig(), this handler will *not* suppress these messages from being displayed by the client's configuration. Signed-off-by: John Snow Reviewed-by: Paolo Bonzini Reviewed-by: Eric Blake Message-id: 20210923004938.3999963-8-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/qemu/aqmp/__init__.py b/python/qemu/aqmp/__init__.py index ab1782999c..d1b0e4dc3d 100644 --- a/python/qemu/aqmp/__init__.py +++ b/python/qemu/aqmp/__init__.py @@ -21,6 +21,7 @@ managing QMP events. # This work is licensed under the terms of the GNU GPL, version 2. See # the COPYING file in the top-level directory. +import logging import warnings from .error import AQMPError @@ -41,6 +42,9 @@ Proceed with caution! warnings.warn(_WMSG, FutureWarning) +# Suppress logging unless an application engages it. +logging.getLogger('qemu.aqmp').addHandler(logging.NullHandler()) + # The order of these fields impact the Sphinx documentation order. __all__ = ( From d911accf0a88070120f7cc71c065fb797484d9b7 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:29 -0400 Subject: [PATCH 0418/1334] python/qmp: clear events on get_events() call All callers in the tree *already* clear the events after a call to get_events(). Do it automatically instead and update callsites to remove the manual clear call. These semantics are quite a bit easier to emulate with async QMP, and nobody appears to be abusing some emergent properties of what happens if you decide not to clear them, so let's dial down to the dumber, simpler thing. Specifically: callers of clear() right after a call to get_events() are more likely expressing their desire to not see any events they just retrieved, whereas callers of clear_events() not in relation to a recent call to pull_event/get_events are likely expressing their desire to simply drop *all* pending events straight onto the floor. In the sync world, this is safe enough; in the async world it's nearly impossible to promise that nothing happens between getting and clearing the events. Making the retrieval also clear the queue is vastly simpler. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-9-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 1 - python/qemu/qmp/__init__.py | 6 ++++-- python/qemu/qmp/qmp_shell.py | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 34131884a5..ae945ca3c9 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -631,7 +631,6 @@ class QEMUMachine: events = self._qmp.get_events(wait=wait) events.extend(self._events) del self._events[:] - self._qmp.clear_events() return events @staticmethod diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py index 269516a79b..c27594b66a 100644 --- a/python/qemu/qmp/__init__.py +++ b/python/qemu/qmp/__init__.py @@ -361,7 +361,7 @@ class QEMUMonitorProtocol: def get_events(self, wait: bool = False) -> List[QMPMessage]: """ - Get a list of available QMP events. + Get a list of available QMP events and clear all pending events. @param wait (bool): block until an event is available. @param wait (float): If wait is a float, treat it as a timeout value. @@ -374,7 +374,9 @@ class QEMUMonitorProtocol: @return The list of available QMP events. """ self.__get_events(wait) - return self.__events + events = self.__events + self.__events = [] + return events def clear_events(self) -> None: """ diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index 337acfce2d..e7d7eb18f1 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -381,7 +381,6 @@ class QMPShell(qmp.QEMUMonitorProtocol): if cmdline == '': for event in self.get_events(): print(event) - self.clear_events() return True return self._execute_cmd(cmdline) From 514d00df5f44f220d0b97cc71323275067d3e60e Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:30 -0400 Subject: [PATCH 0419/1334] python/qmp: add send_fd_scm directly to QEMUMonitorProtocol It turns out you can do this directly from Python ... and because of this, you don't need to worry about setting the inheritability of the fds or spawning another process. Doing this is helpful because it allows QEMUMonitorProtocol to keep its file descriptor and socket object as private implementation details. /that/ is helpful in turn because it allows me to write a compatible, alternative implementation. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-10-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 44 +++++++--------------------------- python/qemu/qmp/__init__.py | 23 ++++++++---------- 2 files changed, 19 insertions(+), 48 deletions(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index ae945ca3c9..1c6532a3d6 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -213,48 +213,22 @@ class QEMUMachine: def send_fd_scm(self, fd: Optional[int] = None, file_path: Optional[str] = None) -> int: """ - Send an fd or file_path to socket_scm_helper. + Send an fd or file_path to the remote via SCM_RIGHTS. - Exactly one of fd and file_path must be given. - If it is file_path, the helper will open that file and pass its own fd. + Exactly one of fd and file_path must be given. If it is + file_path, the file will be opened read-only and the new file + descriptor will be sent to the remote. """ - # In iotest.py, the qmp should always use unix socket. - assert self._qmp.is_scm_available() - if self._socket_scm_helper is None: - raise QEMUMachineError("No path to socket_scm_helper set") - if not os.path.exists(self._socket_scm_helper): - raise QEMUMachineError("%s does not exist" % - self._socket_scm_helper) - - # This did not exist before 3.4, but since then it is - # mandatory for our purpose - if hasattr(os, 'set_inheritable'): - os.set_inheritable(self._qmp.get_sock_fd(), True) - if fd is not None: - os.set_inheritable(fd, True) - - fd_param = ["%s" % self._socket_scm_helper, - "%d" % self._qmp.get_sock_fd()] - if file_path is not None: assert fd is None - fd_param.append(file_path) + with open(file_path, "rb") as passfile: + fd = passfile.fileno() + self._qmp.send_fd_scm(fd) else: assert fd is not None - fd_param.append(str(fd)) + self._qmp.send_fd_scm(fd) - proc = subprocess.run( - fd_param, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - check=False, - close_fds=False, - ) - if proc.stdout: - LOG.debug(proc.stdout) - - return proc.returncode + return 0 @staticmethod def _remove_if_exists(path: str) -> None: diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py index c27594b66a..358c0971d0 100644 --- a/python/qemu/qmp/__init__.py +++ b/python/qemu/qmp/__init__.py @@ -21,6 +21,7 @@ import errno import json import logging import socket +import struct from types import TracebackType from typing import ( Any, @@ -408,18 +409,14 @@ class QEMUMonitorProtocol: raise ValueError(msg) self.__sock.settimeout(timeout) - def get_sock_fd(self) -> int: + def send_fd_scm(self, fd: int) -> None: """ - Get the socket file descriptor. + Send a file descriptor to the remote via SCM_RIGHTS. + """ + if self.__sock.family != socket.AF_UNIX: + raise RuntimeError("Can't use SCM_RIGHTS on non-AF_UNIX socket.") - @return The file descriptor number. - """ - return self.__sock.fileno() - - def is_scm_available(self) -> bool: - """ - Check if the socket allows for SCM_RIGHTS. - - @return True if SCM_RIGHTS is available, otherwise False. - """ - return self.__sock.family == socket.AF_UNIX + self.__sock.sendmsg( + [b' '], + [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))] + ) From c163c723ef92d0f629d015902396f2c67328b2e5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:31 -0400 Subject: [PATCH 0420/1334] python, iotests: remove socket_scm_helper It's not used anymore, now. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-11-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 3 - python/qemu/machine/qtest.py | 2 - tests/Makefile.include | 1 - tests/meson.build | 4 - tests/qemu-iotests/iotests.py | 3 - tests/qemu-iotests/meson.build | 5 - tests/qemu-iotests/socket_scm_helper.c | 136 ------------------------- tests/qemu-iotests/testenv.py | 8 +- 8 files changed, 1 insertion(+), 161 deletions(-) delete mode 100644 tests/qemu-iotests/meson.build delete mode 100644 tests/qemu-iotests/socket_scm_helper.c diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 1c6532a3d6..056d340e35 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -98,7 +98,6 @@ class QEMUMachine: name: Optional[str] = None, base_temp_dir: str = "/var/tmp", monitor_address: Optional[SocketAddrT] = None, - socket_scm_helper: Optional[str] = None, sock_dir: Optional[str] = None, drain_console: bool = False, console_log: Optional[str] = None, @@ -113,7 +112,6 @@ class QEMUMachine: @param name: prefix for socket and log file names (default: qemu-PID) @param base_temp_dir: default location where temp files are created @param monitor_address: address for QMP monitor - @param socket_scm_helper: helper program, required for send_fd_scm() @param sock_dir: where to create socket (defaults to base_temp_dir) @param drain_console: (optional) True to drain console socket to buffer @param console_log: (optional) path to console log file @@ -134,7 +132,6 @@ class QEMUMachine: self._base_temp_dir = base_temp_dir self._sock_dir = sock_dir or self._base_temp_dir self._log_dir = log_dir - self._socket_scm_helper = socket_scm_helper if monitor_address is not None: self._monitor_address = monitor_address diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py index 395cc8fbfe..f2f9aaa5e5 100644 --- a/python/qemu/machine/qtest.py +++ b/python/qemu/machine/qtest.py @@ -115,7 +115,6 @@ class QEMUQtestMachine(QEMUMachine): wrapper: Sequence[str] = (), name: Optional[str] = None, base_temp_dir: str = "/var/tmp", - socket_scm_helper: Optional[str] = None, sock_dir: Optional[str] = None, qmp_timer: Optional[float] = None): # pylint: disable=too-many-arguments @@ -126,7 +125,6 @@ class QEMUQtestMachine(QEMUMachine): sock_dir = base_temp_dir super().__init__(binary, args, wrapper=wrapper, name=name, base_temp_dir=base_temp_dir, - socket_scm_helper=socket_scm_helper, sock_dir=sock_dir, qmp_timer=qmp_timer) self._qtest: Optional[QEMUQtestProtocol] = None self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") diff --git a/tests/Makefile.include b/tests/Makefile.include index 7426522bbe..7bb8961515 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -148,7 +148,6 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images check: ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy) -QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF) check: check-block export PYTHON check-block: $(SRC_PATH)/tests/check-block.sh qemu-img$(EXESUF) \ diff --git a/tests/meson.build b/tests/meson.build index 55a7b08275..3f3882748a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -67,10 +67,6 @@ if have_tools and 'CONFIG_VHOST_USER' in config_host and 'CONFIG_LINUX' in confi dependencies: [qemuutil, vhost_user]) endif -if have_system and 'CONFIG_POSIX' in config_host - subdir('qemu-iotests') -endif - test('decodetree', sh, args: [ files('decode/check.sh'), config_host['PYTHON'], files('../scripts/decodetree.py') ], workdir: meson.current_source_dir() / 'decode', diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index b06ad76e0c..e5fff6ddcf 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -107,8 +107,6 @@ if os.environ.get('VALGRIND_QEMU') == "y" and \ qemu_valgrind = ['valgrind', valgrind_logfile, '--error-exitcode=99'] -socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper') - luks_default_secret_object = 'secret,id=keysec0,data=' + \ os.environ.get('IMGKEYSECRET', '') luks_default_key_secret_opt = 'key-secret=keysec0' @@ -598,7 +596,6 @@ class VM(qtest.QEMUQtestMachine): super().__init__(qemu_prog, qemu_opts, wrapper=wrapper, name=name, base_temp_dir=test_dir, - socket_scm_helper=socket_scm_helper, sock_dir=sock_dir, qmp_timer=timer) self._num_drives = 0 diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build deleted file mode 100644 index 67aed1e492..0000000000 --- a/tests/qemu-iotests/meson.build +++ /dev/null @@ -1,5 +0,0 @@ -if 'CONFIG_LINUX' in config_host - socket_scm_helper = executable('socket_scm_helper', 'socket_scm_helper.c') -else - socket_scm_helper = [] -endif diff --git a/tests/qemu-iotests/socket_scm_helper.c b/tests/qemu-iotests/socket_scm_helper.c deleted file mode 100644 index eb76d31aa9..0000000000 --- a/tests/qemu-iotests/socket_scm_helper.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * SCM_RIGHTS with unix socket help program for test - * - * Copyright IBM, Inc. 2013 - * - * Authors: - * Wenchao Xia - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include -#include - -/* #define SOCKET_SCM_DEBUG */ - -/* - * @fd and @fd_to_send will not be checked for validation in this function, - * a blank will be sent as iov data to notify qemu. - */ -static int send_fd(int fd, int fd_to_send) -{ - struct msghdr msg; - struct iovec iov[1]; - int ret; - char control[CMSG_SPACE(sizeof(int))]; - struct cmsghdr *cmsg; - - memset(&msg, 0, sizeof(msg)); - memset(control, 0, sizeof(control)); - - /* Send a blank to notify qemu */ - iov[0].iov_base = (void *)" "; - iov[0].iov_len = 1; - - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&msg); - - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int)); - - do { - ret = sendmsg(fd, &msg, 0); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) { - fprintf(stderr, "Failed to send msg, reason: %s\n", strerror(errno)); - } - - return ret; -} - -/* Convert string to fd number. */ -static int get_fd_num(const char *fd_str, bool silent) -{ - int sock; - char *err; - - errno = 0; - sock = strtol(fd_str, &err, 10); - if (errno) { - if (!silent) { - fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n", - strerror(errno)); - } - return -1; - } - if (!*fd_str || *err || sock < 0) { - if (!silent) { - fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str); - } - return -1; - } - - return sock; -} - -/* - * To make things simple, the caller needs to specify: - * 1. socket fd. - * 2. path of the file to be sent. - */ -int main(int argc, char **argv, char **envp) -{ - int sock, fd, ret; - -#ifdef SOCKET_SCM_DEBUG - int i; - for (i = 0; i < argc; i++) { - fprintf(stderr, "Parameter %d: %s\n", i, argv[i]); - } -#endif - - if (argc != 3) { - fprintf(stderr, - "Usage: %s < socket-fd > < file-path >\n", - argv[0]); - return EXIT_FAILURE; - } - - - sock = get_fd_num(argv[1], false); - if (sock < 0) { - return EXIT_FAILURE; - } - - fd = get_fd_num(argv[2], true); - if (fd < 0) { - /* Now only open a file in readonly mode for test purpose. If more - precise control is needed, use python script in file operation, which - is supposed to fork and exec this program. */ - fd = open(argv[2], O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Failed to open file '%s'\n", argv[2]); - return EXIT_FAILURE; - } - } - - ret = send_fd(sock, fd); - if (ret < 0) { - close(fd); - return EXIT_FAILURE; - } - - close(fd); - return EXIT_SUCCESS; -} diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 99a57a69f3..c33454fa68 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -68,7 +68,7 @@ class TestEnv(ContextManager['TestEnv']): env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR', 'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG', 'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_PROG', - 'SOCKET_SCM_HELPER', 'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS', + 'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS', 'QEMU_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT', 'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO', 'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU', @@ -140,7 +140,6 @@ class TestEnv(ContextManager['TestEnv']): """Init binary path variables: PYTHON (for bash tests) QEMU_PROG, QEMU_IMG_PROG, QEMU_IO_PROG, QEMU_NBD_PROG, QSD_PROG - SOCKET_SCM_HELPER """ self.python = sys.executable @@ -174,10 +173,6 @@ class TestEnv(ContextManager['TestEnv']): if not isxfile(b): sys.exit('Not executable: ' + b) - helper_path = os.path.join(self.build_iotests, 'socket_scm_helper') - if isxfile(helper_path): - self.socket_scm_helper = helper_path # SOCKET_SCM_HELPER - def __init__(self, imgfmt: str, imgproto: str, aiomode: str, cachemode: Optional[str] = None, imgopts: Optional[str] = None, @@ -303,7 +298,6 @@ IMGPROTO -- {IMGPROTO} PLATFORM -- {platform} TEST_DIR -- {TEST_DIR} SOCK_DIR -- {SOCK_DIR} -SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER} GDB_OPTIONS -- {GDB_OPTIONS} VALGRIND_QEMU -- {VALGRIND_QEMU} PRINT_QEMU_OUTPUT -- {PRINT_QEMU} From e770b8cf76083cc51497b854e73f0a9bb92d1bc7 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 24 Sep 2021 21:53:27 +0200 Subject: [PATCH 0421/1334] pc-bios: Update hppa-firmware.img Update SeaBIOS to seabios-hppa-v2 Changes in seabios-hppa: * Include all latest upstream SeaBIOS patches * add support for the qemu "bootindex" parameter * add support for the qemu "-boot order=g-m" parameter to choose SCSI ID Signed-off-by: Helge Deller Message-Id: Signed-off-by: Richard Henderson --- pc-bios/hppa-firmware.img | Bin 785696 -> 757144 bytes roms/seabios-hppa | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img index 4ba8c7f8b8d4187271e958f5b1f6d7f2a099e0d8..a34a8c84f5d96419bbe7baf0b2fed1842a450cdd 100644 GIT binary patch literal 757144 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 LeC;v+Q@sBJ=?t(~ literal 785696 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

icH@sH_�CzC1trKciCB>Uszu*nUaG66 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 literal 262144 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�>deN4-FwmV*T>c^L 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⪚Qa88!U&4n=ao{|x|}$}Y%N3)eJ`@Y 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 oK9QC@{bsMo^;k@fXaE2J diff --git a/pc-bios/bios-microvm.bin b/pc-bios/bios-microvm.bin index eefd32197e8d637be0266e3410551f711728c6d0..ca6375d65c9d0b77dda0da82f6a0575d7c800f00 100644 GIT binary patch literal 131072 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 avRgZ+^+)p?RddF~^T%IsepVJl;=cjWyF7*f literal 131072 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

!qQ5oyYLc0&_R@xX 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 z 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%0AoPsctQkB;v;F3xth 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

cPNZswyEXOX@_Z>_w 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 JC@Vzwe**{)a0~zd diff --git a/pc-bios/vgabios-bochs-display.bin b/pc-bios/vgabios-bochs-display.bin index d79124b76010580a220fc6a92df7c83350ac5a58..76e076f0293629875daebf7a68d155ca5e325bb9 100644 GIT binary patch delta 14088 zcmZvD3w%>my6@geo1~?5hf+#~0tHI|M>;L?ss=2rX%Q$yYw>|NjOa=o!Q;RV)uVN% zlk|`@*rO+UuQNY-53l?Cc^uEcsM7~}AZeMFhhiUIMR2ep1Bt0nTBI*%?*Cg!+Tx7( z%ie44wZ8SO@BMvi)z&H6Iz^@Ay`_Ow#w_6<{(FIX9?UBeGTkYEdZDsi7Oym_1XiDm z?rN`LPlMp~$1r=RNWCS@^>_9tThEG_tC_hzw=j2+&QfJcV7^OwCe@u~alI`2Ms>4+ zxuz$GwgFKdZyQj{33oY8$nl5n2x<-`233cC!5TPtA6seI=MbuW%+=4N+E#-rrBd+B zTsZm7)A3K_v~8aW_~m-$8r;q7jiDL~lMa?7FtNkJ#FB0c>)V)MVfJ#pAkVM%hEZW| zYd3R!yX$QAncf4{XDuvLq_;3N4Rl-aBU-zIr##mtO#F{YTIFItvv)0Jt}j`3h*ey` zhC@s`(5I)(SeR>J27X@6h{3Y8c(AxZ09ly9Do%$&f`D&wMk=c~DR?AmJd%+{Gy$a9 zw*g|VAe91%bIPVY|(`nt{(I4c=tC*%O|J zQqz=VX788ex%kyfZ+ijK%UmtHS=(i1Z_(5+R}59@D$&qO%pR5<4OSfKelBi9mMcl8POs9_DIw ze$6^M*-9GR#asid;{rN?vzJ(;1_Pvn%`BH4QPc3>v%XKV#^5{YQd_koy}`TEt=I0T66bbwV?n8+cb!)1CGy=BDbx?e$x51wb@DWW6< zt*S7=sFoZ_Dr1b~Xiyndl4Gy9tChK~5RX101d9$a-*;-v^)g>)3==`xvu6JS%Pk9Q zF&zL2ARTT^co|#ye@gNkmhH9Rjra(rbpk(4jr4Yt=bo5(N*mz%h>otJ_rnBt`iQ`* z&{MJpkV@WXZ4g9P$y;F}ea(sVF6#hU?=-!?n?3JU*D$e_{T>QZ{ywey4yKvjB_{AO zd%|M~)*iqb8M_BUr0}6*Ac3K#w<54urB`~}$12ZBZ#O#Hnb3%0FtW_0ykKb%3Np6{;HwCG^mMG`)CyAy`ErfhSIzn zra@h^s~y~KXTA$D%;m>?FXoHTuxosB<`444-j+kQD@hx>TnWP7i+o`P8O&v1S+w@feIAg#mKzkl4!b zV90SJ>pM>rd|!~~MhNCAImEH8fGmX|YGN-IMxWTji5G+oIJRkwev$R}Bx$E9TD4Il zIXcwC$EC{i>P$;jo+hekU6{IBkDq+#k_&q7&QJ9DxoQAA(stL$wjY3JJ2gxE+<=K+ zk+7F{TdIl^Y=h9XUy8OtDBC!dZ4lb_x?uZGl;bR{!qhFK3qlc$WPK<^yXg|GOS#=Q zbsH>IdT138>0w8;WOiVd=E1G>u#Q1yzaUzikEoMiZ0!9y>-T{<>c~7#mo`heqPl&F z2<%))7s2H4ELAigfFgjQk#S!};e}D+Aj_fsz{aT!yrZJY+3l99H4~GV>ol`pQnTkz z4nuGgS;r+7*Uj>neNbmr&a=a`rRv6i>jdq}G2NvNJfQ+XAbotB6S;kD>#QLg&9R7U z}9!vZN&1zD?)1Kq;)9 L~8~oK#yK$*Q2@;xUb*y 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 delta 14346 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

DGO{|7!KbT9w_ diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin index 37bf5e7fe72d5e9f4ecc2747cd5eb9d1eb8d9f41..0f16ad199db5f5e9041fc3e63e19a5ae8683cde2 100644 GIT binary patch literal 39424 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 apS2IocwyBq92slYVuHGeUH+9;@qYmgIlZ?4 diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin index 0ff8ff21f00582e1738b052e0bf959be3aeafcae..420f9ed3319025db97dd895ae574c0ee8aa41433 100644 GIT binary patch literal 39424 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 literal 39424 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 diff --git a/pc-bios/vgabios-ramfb.bin b/pc-bios/vgabios-ramfb.bin index df5e9d615a2b2f706bcc4a7be1cc4050d6b4ceeb..b9b2e230a5d568112dab419a67cfb8e32d9742d5 100644 GIT binary patch delta 14192 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 diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin index 70f60941533d7e8567a0d107f6e671c8b5635ca8..b59256572d3df9c467f45c1ee338846e15ae4bcc 100644 GIT binary patch literal 39424 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�lF2^ zZ5G{%B%3mH-*;&(kt~ue)l&!j#;!1h2s66ntVs6$0Y(S~ z;bY)|G0V&TpxE)1sx%KH6*qWv7=%WP_!On*-Y7kY1`|CQ-mlOeYSN&dqozqd_ysY+ z3;Y7di_bx)ZpK(44D1Wc<~0JVEe^}l)(y#Jr(UywB1%&DeAFh^M@&;q?6T8+?}ND0 z)b85YvFR8NNp}zk;pkE;vVF7ImCB$sxEoL~aeuj+(Q#!A>Ky1i9K6fHY^>sf91!rt zILg6sJIM~=pEH5NPn~yit3(zCdmDm#1<2U6UlIk3UhKgt%1XM8HVr669UFI1>OLgu zP!a==b_4=ZaH~-{=bhb}7$1R(bp>}{#fg`9-Z#2Ygy|$2T;o(f{HhY69|%7CV@zS3pQF<@MshZ0IIUXIG>s~TunCd; z6>=%TT7|IFKOe^t7aB62E-#x&YPf}r^7|!6>t{nd@CtYx6xs=R0aAdeSt8?{*g&yy z((>YV*c#Xk((6nn>Pos^(}M#fQkCj8U8=eC@rWZ7@D3n)7j16NhHJiLQ4bmh;{e1ZQWM8Xh z(7^Qp9kQ*48AL(+SrsrBP9l)dY|HfSLUgmJYdA)=sOvAe9IWaw?qrp?#EOsDYFKfR zPPcJgS8&A@EJ1pW!0c=>*l3)2G$?@jl{+;6(4K;waVu7;HQ&Jz*=GY;B85^oiCZyT zo#XMfMzCS(y(x4f3@F-XBx~ZcOVmzna&l-Cc^av3)`Cv6mpl_=G4u64Gw4G01z=!; z;1J=Z@a+go1(UtEk{y%o9j(^ks0i-X%4nK=@VQusMjlBOd*mq^$PR?(*mon%r%Hb^^s)E3Pk;ezpDKE+qHR8x4ej+&7lRiE`JAsf~iD(bm zG=_2~bggs(VXo55CXloPVL5PUB^(7lgh&TAc}HouBzebvfMX-7F@$ZVFHPd`mk_?r zjB+?V2MTa-lC35zU;mDVx}Zlt;4_$V50J~wZ@V#8+-g3jB$2E-I@je2zVR6t#blVtpz|=KHQ6v8vy;de0apSv&x4ecjGWWRX;p#)i?#lT=`yf@BNFSn*zQf|*mYMpc~f9p zBv8JSZbVjazS8~N4izS6u7 zoLp|e{D0>mz{B4^t-b}?FHe$&r+}8(X;=sl8YWCZ+S%-v;K+j`#T%&o(P~hP@0603 z+Fpb>P(w-36})mV8hFDIRaa0P+0>Zx&Qki|NZ;O^^G+?^6qP*?fzE31t~BTESCGB! zO3vGLc;;;JL+UzT%6S`4o-W99`{`I7`h;^o-AMaKK0-J_!P`r;jR$ZT!YX!_zK*dY zrz@isOQd+G3a4YUJa<%Iwc_mdc9p)yum?oO=LGA|vY5hmMG^fN z2B;;Pf+-x27U$OygTN6+auxoP%CeE$v_g9m)mNNgeYNu(zOz~=R&05L@~(2;ug)Ro zNwK1vy`hYSB*m|t!TJ_c6rcnU|%KIdc58xElTYO%M+0%WA- zF9_)QKBT_EhiTPsOl$Jq8q4oj`9G%o%#E$QoR0Clv8`EITA2S(6v`W-O}o9Ua1aro zjq+W5W^X6t5xcFlS1hyr2fvvf*yhp2ifktQvv0%?L;J9#%Ho~>rr@bP$uNvd-;=US zFvLlb{wxXFHp_!99?u!jy`;)4+c*`zT|8~8Y76R_f}^uT7Akz4~>zZotp zIPB|8BR`zg9vx$Mq1}(krJm27KL(YxFB?%ygAB0;v~(K0#@9}gr8{L*6^7&7Osp*hoqy~1p?Su9qp;r5=P_AFu-pWk2sh++iADg*rE zGplMXAdEbqvT3NJQ(`Cd<$%o2xav~-%LS8HN%HH6-qT{Qe7)a$TKD}59&y7DlS#{^ z0_{oCCbZr22P_F>A}1jNx#0J#3FC5>d_ucx(-xgtb037e84|vgEa*U*ItN7c^KkA{^WirNIJ% zWGVPCW$&RvA{z?{V!!0|(4nhZ(nerFopPe_+0{YsGWH)f>?co}gMD3q*m+vEeVL%o z;2nEPv=&h?;x&Rqboms$e=XlTv22{uytgwK`DmmWk>b1y(LeJn~9T8}5k$t1lR(Mzb&@Ruh|s9H-4h&6`kjKqcV5 zl}VOR-vJawh^~doiG#wi;awRCP!W>b<^mi}pfT2Ki~HG*pb6&|&(80@)9u?uV{}6W z&QoLkMX>b9S^jP{LQWsV(&{~`bj8ThH)!Xu@f2j@yN??((GG;PSiMug3*%m`8;AI? z+l`~WVngpZ*ZlcD>Pi70`8X{U-2v3XoGIyEtE0$n1vBuWCPKSk*BsDmcS7Sc z9!vqzFgJ}x2uBh~36nLqfTq;Dm{M5BXmwbzm8VztS<-a05bbfGe_}7OLVW*m@k~S% zV>2Z3U&bMpx6j(!$9#~CC1bu>s_MZv+~Ejnbv~i4Etx=AJxLC{4^PRt)Nh5M1yw; z5j5;M6<}|v=sXUB*L;&waT|RoJ1?c;20A7FmWe)<{bMo+niU$I0i|$0S+D(VO6aK+ zde78rtmzda=rv8RS)EER@MOoNh0dqbyH&4=q|wU=y=G~8=vX?k4O6_tU}%6+sU%t!eETG3^S+L2vS1 zjd(I?*7|LzeYcxuV7T(KrqW0X4Gl= zaw2p`8gz0OO&2vVIL14vA**1G?~3VPQmcD(B+}Vt5Hb&rCru2z7N!yr`F6`=LS8!q*X#b3td$I#!M@Wh0P6F>Fj5LNX z4#%w=p#ixn7NikHBcsW5O5SKB=cZzF5%Igac?d}Jk)*R!KAOaiUdBL-ARWdTwbj)wE@H|)#mb!SbUN%Sy#rqe_7&Q<8qU%A6IUE8MDj(~e`8#odgp1O^xu#4U7 zhVQ(ea{E$5<2g(KtnF;PO^$@^XdMMQ&s z1#@BKroxowWD`zr`%Vp^_t)`(`?CT3Ou_u>D-Q2H+j#76qv|%Qo*vGOp%Q!PyzReO zx21>2ioK+`{v~r;O8AC22+34@JugM6a4b>gz%CD-^KKzpZ>8IJFfk}+9PL=s%Xqx- z5axg{I=(m|8G3O7_JjH5PLhdSjL13?kr$_^>h}d&*lFTnsl*p@x6H9W z8oE`@54mv2LjN4nO>DAmVzh5{(hOy^q2EC^)Kqq{o}6pN5g5ohPwLAp-H=PYc7wSZ z-{QaWcQ}~8y_o|o83?|dI741l-7+&)kN<@z4oyf9gZ9F2GuF6uE@Z9oj)b6vJ^a?jc$HMpEME;Z?#i* zY98(n^xB_R!m-~KYf zdw+rbQF92t0K)X@ulxjuZM;hOFzgF)60v?ioxSjw1_{}ILp=9gO{HD8FTcP~jQFl% z_x3>(!flR0z%d*+=6@gK8#g#UU#G)|?$Wp8*rxFXXgOz5$Hq(#Q>%sU!q-;+RFUSg@ zRWO~l>;k_!;c+V>LVMxglc=zSdQ$saJ&y2`leQH62y9z0Y0JzX^#{BUNf0z_Hih^X zB&2Mu&c=zzG-LC5+{S}6-zt;cn+lS!7O;D`s~YVZoFrdD|*dM zxPt3>z3L{$Xr?gEhixG;>^%%YLY5+C#{Q%w}hnUR#OdNH~a8Uucb^ zIQ08tP8fQ(nVrM*nxCeGiY?C3dd+=9(A*7rO;e_GlwR|3X2@xA+Vz^VL!6K5wYfNM zmhioyz;^-qcASQg$MMO2^PPPuI@0xUuOS3j^$wcc)i2t$7tGa%s<%0W?@XoFT74yz z!a%fo;S_2hAL<*3dg4jkdX|Q>&=#LJ`a_(p8io#yK!>tJ2jdL#t^aVbX2mX7C7nNUkHB@cN^akhsHZL_(IPNao0a(#}^vP;K1+WySeXUX*$s1qJ#9cM10}PM~zN~q1X0PUGf0&Lo~)DlN5U> zKtl&BQsD{Ugebz|FfLrd(ob+!RNuZYjO80dqS}rJbf&iK$`{O~`Xv#eL*uZP6-5&;+j0`dj6?q;D@C7TTj>rq|wqWr}}@ zfPSOMhu^{AvsR&bCYsvK8co1IYM9uyCM=3{+$8jl=CtWGZ<2<9_qE96yiu=NVWKJd zXDxKxM6hMY0FJ|IkW z$zv4auVv5hrMxVdZ4D(uJ0=M9vAu+zzGICJ6>)CIQ#MiT3YP5+VJp7Ac$oWAq1A*4 zIewM{;0M*m6}*iUPwX5JhmAkAy%c!ACV(JVcV#l_!FfBS%i~0$F$1U79`5AzsCe)L z@q^GqX)Nt>3pwOWGa7m7{Xj4c?l@YNN0lCWpBFi_Tl1;c90(?1CRsxVU@)PFB&ItO zwegL6#jbrU)s=8MLWfgX-mo4_Fjd@7a`p!jsG1~REdCqIT9Mf}2$ThU+wn2v0y+?6 zoCf+a(6nm2Dwry!lc+^B==$2M|FD`XIAJGisGal!H=$e;4R+FTCeJM^JgTc-$2n&&_dL!@`pc+}9`XPupeR$&ZQ`#;LoU z-_dJknJT6@Z{gW~D}K<-JS_J$lVblXI$G^COef{EG0d3I7coDHwpabw zFU~@-{}6Gd*iOF=HHLn*AqNWPKD4(An?}`S4fyZ{t?y7ePS8@O%%GpYU)iwE@3VK~VP4O(z$Ij1g*_D8Tb(>8DDA810OWymXi+OD?1$)e{q^Ky8; zIu%CNrnvm6ID86V(72s{Cc&16!@>)gJ@_TL%k3z^X&mNU6FA4lB7xF2a`+B@c}2t7 z(_O#f0s5a{7yO}JuXE~mNb!1e==biHLi_O0R=6-VT=YJT>>h$j38h{x9G$iPe0fxnR&Um>{A;L1}HjZx1xt-%dbMDxNXkgu( z$<(LO;WDDVn3rB&2DcYdjJca~%WdLvOAmq=-Z{>sio$5@!f_NHbmd)$taWQRKtsU` z#Ugw7X0dApbEpU$8ZaC;9*G+qxKG%3aA99|0X7&)5Z`v1!sF~;smc=^AFmj zO|w67^iFp3CHOSvj6}S2~ufXSsY* z)$)}JL_L?Rmg#c!T&k+9q%=@C1V1yia*ub3V|@92Ib2Z|iC9xQe} zC>s`wmoAUCd8b1xE-PF4yxXy4@zQ4h)=&$6}d#lUFPy3es>nQ)BHol!Wrc-(O%D)5I%N+9`$kY}re{R)UY9ZQ}vQ-t&Eq_j%>G3Ro zZncMs&@RYWwREuu)x3@+FL;)hgMP=_m5Uv#pL@=+6r}`2>;)BfgCWG7at{%0)RYV) zUM*kDEJ7D16yENbk~?{FG;h}Owad#^tp@L+S=7^+%NDN&&p_OBb(MwjAvhFE4}8I3NTb z&{ysl9BO76OB%R3Yi0TBvc)ermXbgX4*Z%N`lr!@r>QI2+9k`Mo}7EPSh!Go&3MmU zti85k@$<{ds0ano_sNTq60x$$QwFB*nwnUC*F6d8$phdCdmO&pB?HoTYmn2YSFbAf z01WnBtjX3b$mZBxgvNS3~N|p^ZrD_GHR!wJ|St@1al&(@XPFazvPUNeHC zn^8t7S#OL=$$Dc|O4ci&6gc7F-p)jd#zCeY;8dqd6(U9blV!7zqCUt}5v4$}6~Ur;Ft`FAQMAz$QFKtWcil!RQTQWA1KQd?4L6y!@PB_XR-N0Nyt}_s!rRcaMr1mg!HSFgsewO%PdHhtv9HYgxsi7 z67naMN@-7$kegIWLcXd}67n^qs?+jR>pxW~3He`DN<#iUQU$37mtI#X3HdXXl8`?~ zO3U1mB3u6lm6DKesFZ|+8ex5soRUhv!KP_L;ehGT892LPP8&A-hQU<~*CC+4U4P@i z-#G9$4*ZP+f8)U4IPm`r2WZJ9OcS)5wB!t|#XAv_!E9C`K*Pwx5-vQpo@GQU|;k7E* zWCd%CaXjpu3U(O7b^;QCfdyu%f&^wS21HSpd5mqU6Mk`MG@8>(wP zz(ssGfoCiJ-pAiFD2Vxu*W%p*7z6K1OPBhd9lfSoD+LlJP_iGiBYkHoZEb;OD#EA$XIH;mA+?4~L9Z zogp){0$JzljbBXA&L+$DxQ*m(pWrog*M>6z^wjV);lF6R3Wu+D(FA?z zZpINU3jiJ$(WL!+7ngx1nA>zE_Q?wKIeWy&8>zjCw@rBMyVR!u15*8Lqke`_-s;T{w4XFPp8R)WoUGYR>94hPHn0O`ht1;R((cZ-TG%4D1by@$05jQ3! zr>4bdH^*p?)hF8DrurFXEom^?#u~JtoLjYK#f^7tEG+07^JYsz-?EMR>8L-L3&J?> z(C*N*JMN$x9wX>=1YP;~Z$y4R-S~l5z^A07fUiS_49UvM8a8ZLc6K({Yq#4GFC8~- z+=K}eCQh6ykP9u{M*Lne{XF5 zy<_w9>EFHiW51U__O|@Qg1Fql3nos?pFA;t>csr}Cg$hUzx(nhPR*Y+pDeN?vHPX9*Pvu*b5k@m!bxZJ@DpaGLLYr1B&XjUuzvsg8#%4BHE z_=33H!3z*!$xOFe(ydwPR(Q|W^ek(-#hRXJO)QAZ9lT)35UXW~b?6YQKE!IJf4X(Z zQ0owjHL)Nrcklu^W3O{hlz*B7mVm2n^cn=UWX)fN9bw5VA|C29mY}sE#vIj)d zujRw~4gYP!h7aD{uwlI{TOTiGI2w9yoo8SE4gEyO1`IxLxQ!3kVRWhvR6u6u= zG$7OYcjf2eTHW0#(=RQBjka{D`|O#3%&^&xJI^|gEBNX_XLnD3Pj_danv0WCv&PKm zpD`vYRj%it6{v0N4T3dl4i}yh-Al)hAAfN)U7W6JDa|WuX(`LIv{Xp|z7xx2GKx>} zQ>MuRQswj--yqm!>9?p-hRH4MFU^(E@*W5T6n0LJ6A>{kR&LG9tEw`ZM>YnpDlayC zF+hI9*f`O?v>G8UA|4e%l$G_D@gF5@wyKtq+!rj%cd8({ge;frdza%~pAY_T2+XT+(v%^={8(H5ua29jeH=tGEsSFA=- zD$%FL8}ifKeEfKGvl2MXmRAW}xYYPmwMg(5g%jyiXKHGyF}xPLv#hNjB#)6T_G2*m z;>z<%k#51I#GxPcWuGN{6&1ym)BAeVw@9Yf7sAa{ma0qED3{Jy~U84}{C9kE%VCm*!bgqxB=l-%OyF zQ1hkmGbtoqY6f%q`00_~QIgO^oJIEavb8{70@x_&vs_1;xI!jB5+Yo{%NL332z~G-9EtLUW&2Iv# zYQZEh%Ja&WmGT%V{-S&>fwxszScLM{_zTg@V}`2!rKW0Gm=3t0=202&QUDaZ z1dj0wAZH@b$;Xf5O14%6lNpz+ zPh|vSTP@RTPek#m7oOn;I_N)|&zR1ZROU!qBK?rPgz-dWEkQm2AD@P{Nl53FqgOOOXDXYnxR3+fXz zDScA;E$FShB>omvYP5%SsTt$dY^;FM>2;=$>C;GS<8XpO791en^bIVZI&~oaDE+F{NJ4v4 zr$*^zeCS^sb($8}UzMLfpnepeq=%=gVjVvQj#rd_>;%Q#4El_$?g5uR;8Ji2Rvm3nK~U^;!8XHWq>EB?k@-)TOaTBoEw(-(cRkQ`uy$b(UVNcp1FFK> z6SAu+xgIKKST+FG{vhbLV7w49wXXgi`7!%3P1de!VQk-JFW_#q~yW_S{lZ z@1?nR1!3sd7NZ6xi~OfDlzU;wgfgF_N(>qj9kv+htkI-V2VU|0helc-Gq=$QlqCMi zi}j&;k3EdVJep%O-gGway2{F&vOgWf8e=E`w7Mg=v^7zDs^n#0_=``&`2| z&;9jkZJaJbREIQQQ0 zww(-V_}X&Doe^bam1SkhF_V6_(L&3){{Ewcit8nse5|o41jc3Dhp79+%J&-I`V2#& zjQlD!M(fu<$x2mHMorNrM@+t#sTdZejvno=l3I`&&La{SCodGhBeC?iY=rV|Qu1PW z(y}bkjW)b!Dx)!BWY^pxgZa;FP0Pg;zAF39l4?{Z&sp`s@SD}LE4>~=K9$cb#}D<$ zBOTx`OP)cF@`z2c52?ymj`x&}@nf(kBye{1A3b_B+OZSFDPi=Ghbc-ml_h$|Rnc%4 z`^Hf^t6EmuRUfR@`GtORX6p5%PdAfYDWqMli;58s0=-Sh;G3CoCOd;sUE% z?t|e4ujW*AwN?F-vrHy80eQhV(+E`j+9xR!d)WI0%X~bfDp|V&XC>2L1WFji_csEoS@g>n` zWeY7j7ck?=Up2Ty;Iy{TQ(ok&@*8G)TSso`354%vXqrrkL;ob*RF+kWw5wW)E^5p{ z{Y&2If5J2EvBj>am#KV_OY&)u9hXXkypA`@^%6bNs2{3Y=$E*egn>S2JY_b+e^SI6 zmba`P^~JbUc|wY61!>e%PkKasFhxt$Lqs~!-%Rv26MfA@PxD^GFXh&wUL>~=^)Z=- z#Ii*H5@nK3qYsKg`N)^K43X$pMth{{CHj=gqm-==@F#mz4f#+UBx^f%DF~I3=tL&E zkP0LoCHaqr`-lnG*V57#2owM!>N_fEBq|><*Ul|SodG2#Q;=f$kCO?`VWMjY-XiTznaxpmYQ~68ykxyukm+1th-@o4)JGi`Q`S@~ZMEqsl5Xpbp__fRac=I&+2_XE<XlvcVB3h2W~aO16H0m@er+^~nUMHTD*;1u4CoDlK<4 z{Eyr|t+LIMSK20@v_B#@7sqIQfgTH`;kjTntknA2AZ?%+(Hijl2f(8I-?3%PeiiaSWbjEwxAcE@-~(M5Ue6 zRIjqFbG4oN`CEw>#kl(90$!O*CdSeJb7X2rTM>mIp%NNrqiqV>t3N{AESS7loO?ZZmPUKrUqm2G|CQ(hTuW_2SClf25()5)7x zXTZtT+<%KE8PG|aRApy4m*|4sMGh05J=77#M|{G`Q{n&+GQKfLW3!3yv*2mg+^XE% zyJ`HW8qt9V`VWCT+Rqj<7{InN@<9&<`HkFVeVRQtw=);|4Bh5GN+2t$X4I~T?4`|8 zsj=O=UbCxyu5Wgx&Jg*?CcdoKI@J?>;WYRWz|6=4oLFD#Bsk^|;P~05_)*o82NIm9 zFZo0EAoMkfC;;2gUSDZtDNT)+EHAaaM7u56Y&nEN{3HGtPeP2e+)Y`JZ9rlKopg-waNeY8&p?)SEQ(y|{$BwXK))#xwxCr{DG44`)H z3V#m|7^Y5JSKo=YsA*^zg_uL7BqzAA=Fvmzvskd@*V1tcz@KYLPWF%a|A2mdq2o%bva^xm#3ZPV_rBMdZ zsiX#NfbOBnxFlX18(-9fGNugk#72e!!BIY!(+#Qm6?&V`Nw~hAl(o zREBwXqLply%C?@i9*kgL8x&sv?33$hUt4J|>0=qxUTqjA0aPPqXnmwOD|7o;tf9}y z0wMduo@;~NsHA#qbfjA7SsEby9VpEsVWtbx!vA4DKsfOVbp`F*M{W(YcAu7DMZkPu zAky#3WQa2t|`-s@ReR(>CSa<)Vsx9_by#}&t17<^2F6^mM`(HEb~nI(emfKlUA*9Ye~tn2mjx>|8FG< B8w~&e literal 39424 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 diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin index 65af5be59d17c764c3ae5d7527a824b153765312..c00414ab835a0a1b1a9dec207626a352eee48e91 100644 GIT binary patch literal 39424 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

dj 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 literal 38912 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 From 80347ae9f26dfafbd358d7d8d9ad175a2a624365 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:04 -0700 Subject: [PATCH 0730/1334] target/riscv: Use gen_arith_per_ol for RVM The multiply high-part instructions require a separate implementation for RV32 when TARGET_LONG_BITS == 64. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-11-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvm.c.inc | 26 ++++++++++++++++++++++--- target/riscv/translate.c | 16 +++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvm.c.inc b/target/riscv/insn_trans/trans_rvm.c.inc index 9a1fe3c799..2af0e5c139 100644 --- a/target/riscv/insn_trans/trans_rvm.c.inc +++ b/target/riscv/insn_trans/trans_rvm.c.inc @@ -33,10 +33,16 @@ static void gen_mulh(TCGv ret, TCGv s1, TCGv s2) tcg_temp_free(discard); } +static void gen_mulh_w(TCGv ret, TCGv s1, TCGv s2) +{ + tcg_gen_mul_tl(ret, s1, s2); + tcg_gen_sari_tl(ret, ret, 32); +} + static bool trans_mulh(DisasContext *ctx, arg_mulh *a) { REQUIRE_EXT(ctx, RVM); - return gen_arith(ctx, a, EXT_NONE, gen_mulh); + return gen_arith_per_ol(ctx, a, EXT_SIGN, gen_mulh, gen_mulh_w); } static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2) @@ -54,10 +60,23 @@ static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2) tcg_temp_free(rh); } +static void gen_mulhsu_w(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + tcg_gen_ext32s_tl(t1, arg1); + tcg_gen_ext32u_tl(t2, arg2); + tcg_gen_mul_tl(ret, t1, t2); + tcg_temp_free(t1); + tcg_temp_free(t2); + tcg_gen_sari_tl(ret, ret, 32); +} + static bool trans_mulhsu(DisasContext *ctx, arg_mulhsu *a) { REQUIRE_EXT(ctx, RVM); - return gen_arith(ctx, a, EXT_NONE, gen_mulhsu); + return gen_arith_per_ol(ctx, a, EXT_NONE, gen_mulhsu, gen_mulhsu_w); } static void gen_mulhu(TCGv ret, TCGv s1, TCGv s2) @@ -71,7 +90,8 @@ static void gen_mulhu(TCGv ret, TCGv s1, TCGv s2) static bool trans_mulhu(DisasContext *ctx, arg_mulhu *a) { REQUIRE_EXT(ctx, RVM); - return gen_arith(ctx, a, EXT_NONE, gen_mulhu); + /* gen_mulh_w works for either sign as input. */ + return gen_arith_per_ol(ctx, a, EXT_ZERO, gen_mulhu, gen_mulh_w); } static void gen_div(TCGv ret, TCGv source1, TCGv source2) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index afd59ef690..cb515e2a3c 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -403,6 +403,22 @@ static bool gen_arith(DisasContext *ctx, arg_r *a, DisasExtend ext, return true; } +static bool gen_arith_per_ol(DisasContext *ctx, arg_r *a, DisasExtend ext, + void (*f_tl)(TCGv, TCGv, TCGv), + void (*f_32)(TCGv, TCGv, TCGv)) +{ + int olen = get_olen(ctx); + + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + f_tl = f_32; + } else { + g_assert_not_reached(); + } + } + return gen_arith(ctx, a, ext, f_tl); +} + static bool gen_shift_imm_fn(DisasContext *ctx, arg_shift *a, DisasExtend ext, void (*func)(TCGv, TCGv, target_long)) { From 673be37163cb44d2e1699503ccd065e9d13d4db7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:05 -0700 Subject: [PATCH 0731/1334] target/riscv: Adjust trans_rev8_32 for riscv64 When target_long is 64-bit, we still want a 32-bit bswap for rev8. Since this opcode is specific to RV32, we need not conditionalize. Acked-by: Alistair Francis Reviewed-by: LIU Zhiwei Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-12-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 9ef8ab94ad..d6f9e9fc83 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -232,11 +232,16 @@ static bool trans_rol(DisasContext *ctx, arg_rol *a) return gen_shift(ctx, a, EXT_NONE, tcg_gen_rotl_tl); } +static void gen_rev8_32(TCGv ret, TCGv src1) +{ + tcg_gen_bswap32_tl(ret, src1, TCG_BSWAP_OS); +} + static bool trans_rev8_32(DisasContext *ctx, arg_rev8_32 *a) { REQUIRE_32BIT(ctx); REQUIRE_ZBB(ctx); - return gen_unary(ctx, a, EXT_NONE, tcg_gen_bswap_tl); + return gen_unary(ctx, a, EXT_NONE, gen_rev8_32); } static bool trans_rev8_64(DisasContext *ctx, arg_rev8_64 *a) From fdab665f6e9d0919bbbab88b16ae2c4be1bf61c6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:06 -0700 Subject: [PATCH 0732/1334] target/riscv: Use gen_unary_per_ol for RVB The count zeros instructions require a separate implementation for RV32 when TARGET_LONG_BITS == 64. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-13-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 33 ++++++++++++------------- target/riscv/translate.c | 16 ++++++++++++ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index d6f9e9fc83..4eb41756fa 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -47,10 +47,18 @@ static void gen_clz(TCGv ret, TCGv arg1) tcg_gen_clzi_tl(ret, arg1, TARGET_LONG_BITS); } +static void gen_clzw(TCGv ret, TCGv arg1) +{ + TCGv t = tcg_temp_new(); + tcg_gen_shli_tl(t, arg1, 32); + tcg_gen_clzi_tl(ret, t, 32); + tcg_temp_free(t); +} + static bool trans_clz(DisasContext *ctx, arg_clz *a) { REQUIRE_ZBB(ctx); - return gen_unary(ctx, a, EXT_ZERO, gen_clz); + return gen_unary_per_ol(ctx, a, EXT_NONE, gen_clz, gen_clzw); } static void gen_ctz(TCGv ret, TCGv arg1) @@ -58,10 +66,15 @@ static void gen_ctz(TCGv ret, TCGv arg1) tcg_gen_ctzi_tl(ret, arg1, TARGET_LONG_BITS); } +static void gen_ctzw(TCGv ret, TCGv arg1) +{ + tcg_gen_ctzi_tl(ret, arg1, 32); +} + static bool trans_ctz(DisasContext *ctx, arg_ctz *a) { REQUIRE_ZBB(ctx); - return gen_unary(ctx, a, EXT_ZERO, gen_ctz); + return gen_unary_per_ol(ctx, a, EXT_ZERO, gen_ctz, gen_ctzw); } static bool trans_cpop(DisasContext *ctx, arg_cpop *a) @@ -317,14 +330,6 @@ static bool trans_zext_h_64(DisasContext *ctx, arg_zext_h_64 *a) return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext16u_tl); } -static void gen_clzw(TCGv ret, TCGv arg1) -{ - TCGv t = tcg_temp_new(); - tcg_gen_shli_tl(t, arg1, 32); - tcg_gen_clzi_tl(ret, t, 32); - tcg_temp_free(t); -} - static bool trans_clzw(DisasContext *ctx, arg_clzw *a) { REQUIRE_64BIT(ctx); @@ -332,17 +337,11 @@ static bool trans_clzw(DisasContext *ctx, arg_clzw *a) return gen_unary(ctx, a, EXT_NONE, gen_clzw); } -static void gen_ctzw(TCGv ret, TCGv arg1) -{ - tcg_gen_ori_tl(ret, arg1, (target_ulong)MAKE_64BIT_MASK(32, 32)); - tcg_gen_ctzi_tl(ret, ret, 64); -} - static bool trans_ctzw(DisasContext *ctx, arg_ctzw *a) { REQUIRE_64BIT(ctx); REQUIRE_ZBB(ctx); - return gen_unary(ctx, a, EXT_NONE, gen_ctzw); + return gen_unary(ctx, a, EXT_ZERO, gen_ctzw); } static bool trans_cpopw(DisasContext *ctx, arg_cpopw *a) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index cb515e2a3c..f3a5870ad0 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -486,6 +486,22 @@ static bool gen_unary(DisasContext *ctx, arg_r2 *a, DisasExtend ext, return true; } +static bool gen_unary_per_ol(DisasContext *ctx, arg_r2 *a, DisasExtend ext, + void (*f_tl)(TCGv, TCGv), + void (*f_32)(TCGv, TCGv)) +{ + int olen = get_olen(ctx); + + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + f_tl = f_32; + } else { + g_assert_not_reached(); + } + } + return gen_unary(ctx, a, ext, f_tl); +} + static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) { DisasContext *ctx = container_of(dcbase, DisasContext, base); From a0245d91dd1ca33ecde8c430ac6986dbd90a84db Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:07 -0700 Subject: [PATCH 0733/1334] target/riscv: Use gen_shift*_per_ol for RVB, RVI Most shift instructions require a separate implementation for RV32 when TARGET_LONG_BITS == 64. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-14-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 92 ++++++++++++++----------- target/riscv/insn_trans/trans_rvi.c.inc | 26 +++---- target/riscv/translate.c | 31 +++++++++ 3 files changed, 97 insertions(+), 52 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 4eb41756fa..c8d31907c5 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -227,22 +227,70 @@ static bool trans_bexti(DisasContext *ctx, arg_bexti *a) return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_bext); } +static void gen_rorw(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + /* truncate to 32-bits */ + tcg_gen_trunc_tl_i32(t1, arg1); + tcg_gen_trunc_tl_i32(t2, arg2); + + tcg_gen_rotr_i32(t1, t1, t2); + + /* sign-extend 64-bits */ + tcg_gen_ext_i32_tl(ret, t1); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); +} + static bool trans_ror(DisasContext *ctx, arg_ror *a) { REQUIRE_ZBB(ctx); - return gen_shift(ctx, a, EXT_NONE, tcg_gen_rotr_tl); + return gen_shift_per_ol(ctx, a, EXT_NONE, tcg_gen_rotr_tl, gen_rorw); +} + +static void gen_roriw(TCGv ret, TCGv arg1, target_long shamt) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t1, arg1); + tcg_gen_rotri_i32(t1, t1, shamt); + tcg_gen_ext_i32_tl(ret, t1); + + tcg_temp_free_i32(t1); } static bool trans_rori(DisasContext *ctx, arg_rori *a) { REQUIRE_ZBB(ctx); - return gen_shift_imm_fn(ctx, a, EXT_NONE, tcg_gen_rotri_tl); + return gen_shift_imm_fn_per_ol(ctx, a, EXT_NONE, + tcg_gen_rotri_tl, gen_roriw); +} + +static void gen_rolw(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + /* truncate to 32-bits */ + tcg_gen_trunc_tl_i32(t1, arg1); + tcg_gen_trunc_tl_i32(t2, arg2); + + tcg_gen_rotl_i32(t1, t1, t2); + + /* sign-extend 64-bits */ + tcg_gen_ext_i32_tl(ret, t1); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); } static bool trans_rol(DisasContext *ctx, arg_rol *a) { REQUIRE_ZBB(ctx); - return gen_shift(ctx, a, EXT_NONE, tcg_gen_rotl_tl); + return gen_shift_per_ol(ctx, a, EXT_NONE, tcg_gen_rotl_tl, gen_rolw); } static void gen_rev8_32(TCGv ret, TCGv src1) @@ -352,24 +400,6 @@ static bool trans_cpopw(DisasContext *ctx, arg_cpopw *a) return gen_unary(ctx, a, EXT_ZERO, tcg_gen_ctpop_tl); } -static void gen_rorw(TCGv ret, TCGv arg1, TCGv arg2) -{ - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - - /* truncate to 32-bits */ - tcg_gen_trunc_tl_i32(t1, arg1); - tcg_gen_trunc_tl_i32(t2, arg2); - - tcg_gen_rotr_i32(t1, t1, t2); - - /* sign-extend 64-bits */ - tcg_gen_ext_i32_tl(ret, t1); - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); -} - static bool trans_rorw(DisasContext *ctx, arg_rorw *a) { REQUIRE_64BIT(ctx); @@ -383,25 +413,7 @@ static bool trans_roriw(DisasContext *ctx, arg_roriw *a) REQUIRE_64BIT(ctx); REQUIRE_ZBB(ctx); ctx->ol = MXL_RV32; - return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_rorw); -} - -static void gen_rolw(TCGv ret, TCGv arg1, TCGv arg2) -{ - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - - /* truncate to 32-bits */ - tcg_gen_trunc_tl_i32(t1, arg1); - tcg_gen_trunc_tl_i32(t2, arg2); - - tcg_gen_rotl_i32(t1, t1, t2); - - /* sign-extend 64-bits */ - tcg_gen_ext_i32_tl(ret, t1); - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_roriw); } static bool trans_rolw(DisasContext *ctx, arg_rolw *a) diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 9cf0383cfb..91dc438a3a 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -268,14 +268,26 @@ static bool trans_slli(DisasContext *ctx, arg_slli *a) return gen_shift_imm_fn(ctx, a, EXT_NONE, tcg_gen_shli_tl); } +static void gen_srliw(TCGv dst, TCGv src, target_long shamt) +{ + tcg_gen_extract_tl(dst, src, shamt, 32 - shamt); +} + static bool trans_srli(DisasContext *ctx, arg_srli *a) { - return gen_shift_imm_fn(ctx, a, EXT_ZERO, tcg_gen_shri_tl); + return gen_shift_imm_fn_per_ol(ctx, a, EXT_NONE, + tcg_gen_shri_tl, gen_srliw); +} + +static void gen_sraiw(TCGv dst, TCGv src, target_long shamt) +{ + tcg_gen_sextract_tl(dst, src, shamt, 32 - shamt); } static bool trans_srai(DisasContext *ctx, arg_srai *a) { - return gen_shift_imm_fn(ctx, a, EXT_SIGN, tcg_gen_sari_tl); + return gen_shift_imm_fn_per_ol(ctx, a, EXT_NONE, + tcg_gen_sari_tl, gen_sraiw); } static bool trans_add(DisasContext *ctx, arg_add *a) @@ -342,11 +354,6 @@ static bool trans_slliw(DisasContext *ctx, arg_slliw *a) return gen_shift_imm_fn(ctx, a, EXT_NONE, tcg_gen_shli_tl); } -static void gen_srliw(TCGv dst, TCGv src, target_long shamt) -{ - tcg_gen_extract_tl(dst, src, shamt, 32 - shamt); -} - static bool trans_srliw(DisasContext *ctx, arg_srliw *a) { REQUIRE_64BIT(ctx); @@ -354,11 +361,6 @@ static bool trans_srliw(DisasContext *ctx, arg_srliw *a) return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_srliw); } -static void gen_sraiw(TCGv dst, TCGv src, target_long shamt) -{ - tcg_gen_sextract_tl(dst, src, shamt, 32 - shamt); -} - static bool trans_sraiw(DisasContext *ctx, arg_sraiw *a) { REQUIRE_64BIT(ctx); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index f3a5870ad0..bed1c2174e 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -438,6 +438,22 @@ static bool gen_shift_imm_fn(DisasContext *ctx, arg_shift *a, DisasExtend ext, return true; } +static bool gen_shift_imm_fn_per_ol(DisasContext *ctx, arg_shift *a, + DisasExtend ext, + void (*f_tl)(TCGv, TCGv, target_long), + void (*f_32)(TCGv, TCGv, target_long)) +{ + int olen = get_olen(ctx); + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + f_tl = f_32; + } else { + g_assert_not_reached(); + } + } + return gen_shift_imm_fn(ctx, a, ext, f_tl); +} + static bool gen_shift_imm_tl(DisasContext *ctx, arg_shift *a, DisasExtend ext, void (*func)(TCGv, TCGv, TCGv)) { @@ -474,6 +490,21 @@ static bool gen_shift(DisasContext *ctx, arg_r *a, DisasExtend ext, return true; } +static bool gen_shift_per_ol(DisasContext *ctx, arg_r *a, DisasExtend ext, + void (*f_tl)(TCGv, TCGv, TCGv), + void (*f_32)(TCGv, TCGv, TCGv)) +{ + int olen = get_olen(ctx); + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + f_tl = f_32; + } else { + g_assert_not_reached(); + } + } + return gen_shift(ctx, a, ext, f_tl); +} + static bool gen_unary(DisasContext *ctx, arg_r2 *a, DisasExtend ext, void (*func)(TCGv, TCGv)) { From 665b90d8a4aa7d90bbeee9ff279e37edcd573635 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:08 -0700 Subject: [PATCH 0734/1334] target/riscv: Use riscv_csrrw_debug for cpu_dump Use the official debug read interface to the csrs, rather than referencing the env slots directly. Put the list of csrs to dump into a table. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-15-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 89 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index dd9eccd68e..788fa0b11c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -242,51 +242,52 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) #endif qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc); #ifndef CONFIG_USER_ONLY - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", (target_ulong)env->mstatus); - if (riscv_cpu_mxl(env) == MXL_RV32) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatush ", - (target_ulong)(env->mstatus >> 32)); + { + static const int dump_csrs[] = { + CSR_MHARTID, + CSR_MSTATUS, + CSR_MSTATUSH, + CSR_HSTATUS, + CSR_VSSTATUS, + CSR_MIP, + CSR_MIE, + CSR_MIDELEG, + CSR_HIDELEG, + CSR_MEDELEG, + CSR_HEDELEG, + CSR_MTVEC, + CSR_STVEC, + CSR_VSTVEC, + CSR_MEPC, + CSR_SEPC, + CSR_VSEPC, + CSR_MCAUSE, + CSR_SCAUSE, + CSR_VSCAUSE, + CSR_MTVAL, + CSR_STVAL, + CSR_HTVAL, + CSR_MTVAL2, + CSR_MSCRATCH, + CSR_SSCRATCH, + CSR_SATP, + }; + + for (int i = 0; i < ARRAY_SIZE(dump_csrs); ++i) { + int csrno = dump_csrs[i]; + target_ulong val = 0; + RISCVException res = riscv_csrrw_debug(env, csrno, &val, 0, 0); + + /* + * Rely on the smode, hmode, etc, predicates within csr.c + * to do the filtering of the registers that are present. + */ + if (res == RISCV_EXCP_NONE) { + qemu_fprintf(f, " %-8s " TARGET_FMT_lx "\n", + csr_ops[csrno].name, val); + } + } } - if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hstatus ", env->hstatus); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsstatus", - (target_ulong)env->vsstatus); - } - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ", env->mip); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg); - if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hideleg ", env->hideleg); - } - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg); - if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hedeleg ", env->hedeleg); - } - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec ", env->mtvec); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "stvec ", env->stvec); - if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vstvec ", env->vstvec); - } - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc ", env->mepc); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "sepc ", env->sepc); - if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsepc ", env->vsepc); - } - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause ", env->mcause); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "scause ", env->scause); - if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vscause ", env->vscause); - } - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval ", env->mtval); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "stval ", env->stval); - if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "htval ", env->htval); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval2 ", env->mtval2); - } - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mscratch", env->mscratch); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "sscratch", env->sscratch); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "satp ", env->satp); #endif for (i = 0; i < 32; i++) { From b550f89457ef5022231f8255ae1a6ba496c492b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:09 -0700 Subject: [PATCH 0735/1334] target/riscv: Compute mstatus.sd on demand The position of this read-only field is dependent on the current xlen. Rather than having to compute that difference in many places, compute it only on read. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-16-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 3 +-- target/riscv/csr.c | 37 ++++++++++++++++++++++--------------- target/riscv/translate.c | 5 ++--- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 429afd1f48..0d1132f39d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -185,10 +185,9 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env) void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) { - uint64_t sd = riscv_cpu_mxl(env) == MXL_RV32 ? MSTATUS32_SD : MSTATUS64_SD; uint64_t mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS | MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE | - MSTATUS64_UXL | sd; + MSTATUS64_UXL; bool current_virt = riscv_cpu_virt_enabled(env); g_assert(riscv_has_ext(env, RVH)); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index c4a479ddd2..69e4d65fcd 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -477,10 +477,28 @@ static RISCVException read_mhartid(CPURISCVState *env, int csrno, } /* Machine Trap Setup */ + +/* We do not store SD explicitly, only compute it on demand. */ +static uint64_t add_status_sd(RISCVMXL xl, uint64_t status) +{ + if ((status & MSTATUS_FS) == MSTATUS_FS || + (status & MSTATUS_XS) == MSTATUS_XS) { + switch (xl) { + case MXL_RV32: + return status | MSTATUS32_SD; + case MXL_RV64: + return status | MSTATUS64_SD; + default: + g_assert_not_reached(); + } + } + return status; +} + static RISCVException read_mstatus(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->mstatus; + *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus); return RISCV_EXCP_NONE; } @@ -498,7 +516,6 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, { uint64_t mstatus = env->mstatus; uint64_t mask = 0; - int dirty; /* flush tlb on mstatus fields that affect VM */ if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV | @@ -520,12 +537,7 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, mstatus = (mstatus & ~mask) | (val & mask); - dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | - ((mstatus & MSTATUS_XS) == MSTATUS_XS); - if (riscv_cpu_mxl(env) == MXL_RV32) { - mstatus = set_field(mstatus, MSTATUS32_SD, dirty); - } else { - mstatus = set_field(mstatus, MSTATUS64_SD, dirty); + if (riscv_cpu_mxl(env) == MXL_RV64) { /* SXL and UXL fields are for now read only */ mstatus = set_field(mstatus, MSTATUS64_SXL, MXL_RV64); mstatus = set_field(mstatus, MSTATUS64_UXL, MXL_RV64); @@ -798,13 +810,8 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno, { target_ulong mask = (sstatus_v1_10_mask); - if (riscv_cpu_mxl(env) == MXL_RV32) { - mask |= SSTATUS32_SD; - } else { - mask |= SSTATUS64_SD; - } - - *val = env->mstatus & mask; + /* TODO: Use SXL not MXL. */ + *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask); return RISCV_EXCP_NONE; } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index bed1c2174e..d38f87d718 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -280,7 +280,6 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) static void mark_fs_dirty(DisasContext *ctx) { TCGv tmp; - target_ulong sd = get_xl(ctx) == MXL_RV32 ? MSTATUS32_SD : MSTATUS64_SD; if (ctx->mstatus_fs != MSTATUS_FS) { /* Remember the state change for the rest of the TB. */ @@ -288,7 +287,7 @@ static void mark_fs_dirty(DisasContext *ctx) tmp = tcg_temp_new(); tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | sd); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); tcg_temp_free(tmp); } @@ -299,7 +298,7 @@ static void mark_fs_dirty(DisasContext *ctx) tmp = tcg_temp_new(); tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | sd); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); tcg_temp_free(tmp); } From ef6310064820183cce7c5969cae293e680c57679 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 18 Oct 2021 12:38:39 +1000 Subject: [PATCH 0736/1334] hw/riscv: opentitan: Update to the latest build Update the OpenTitan machine model to match the latest OpenTitan FPGA design. Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 18b1b681b0f8dd2461e819d1217bf0b530812680.1634524691.git.alistair.francis@wdc.com --- hw/riscv/opentitan.c | 22 +++++++++++++++++----- include/hw/riscv/opentitan.h | 6 +++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 9803ae6d70..601f8deebe 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -46,19 +46,19 @@ static const MemMapEntry ibex_memmap[] = { [IBEX_DEV_PINMUX] = { 0x40460000, 0x1000 }, [IBEX_DEV_PADCTRL] = { 0x40470000, 0x1000 }, [IBEX_DEV_FLASH_CTRL] = { 0x41000000, 0x1000 }, - [IBEX_DEV_PLIC] = { 0x41010000, 0x1000 }, [IBEX_DEV_AES] = { 0x41100000, 0x1000 }, [IBEX_DEV_HMAC] = { 0x41110000, 0x1000 }, [IBEX_DEV_KMAC] = { 0x41120000, 0x1000 }, - [IBEX_DEV_KEYMGR] = { 0x41130000, 0x1000 }, + [IBEX_DEV_OTBN] = { 0x41130000, 0x10000 }, + [IBEX_DEV_KEYMGR] = { 0x41140000, 0x1000 }, [IBEX_DEV_CSRNG] = { 0x41150000, 0x1000 }, [IBEX_DEV_ENTROPY] = { 0x41160000, 0x1000 }, [IBEX_DEV_EDNO] = { 0x41170000, 0x1000 }, [IBEX_DEV_EDN1] = { 0x41180000, 0x1000 }, [IBEX_DEV_ALERT_HANDLER] = { 0x411b0000, 0x1000 }, [IBEX_DEV_NMI_GEN] = { 0x411c0000, 0x1000 }, - [IBEX_DEV_OTBN] = { 0x411d0000, 0x10000 }, [IBEX_DEV_PERI] = { 0x411f0000, 0x10000 }, + [IBEX_DEV_PLIC] = { 0x48000000, 0x4005000 }, [IBEX_DEV_FLASH_VIRTUAL] = { 0x80000000, 0x80000 }, }; @@ -105,7 +105,7 @@ static void lowrisc_ibex_soc_init(Object *obj) object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); - object_initialize_child(obj, "plic", &s->plic, TYPE_IBEX_PLIC); + object_initialize_child(obj, "plic", &s->plic, TYPE_SIFIVE_PLIC); object_initialize_child(obj, "uart", &s->uart, TYPE_IBEX_UART); @@ -145,6 +145,18 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) &s->flash_alias); /* PLIC */ + qdev_prop_set_string(DEVICE(&s->plic), "hart-config", "M"); + qdev_prop_set_uint32(DEVICE(&s->plic), "hartid-base", 0); + qdev_prop_set_uint32(DEVICE(&s->plic), "num-sources", 180); + qdev_prop_set_uint32(DEVICE(&s->plic), "num-priorities", 3); + qdev_prop_set_uint32(DEVICE(&s->plic), "priority-base", 0x00); + qdev_prop_set_uint32(DEVICE(&s->plic), "pending-base", 0x1000); + qdev_prop_set_uint32(DEVICE(&s->plic), "enable-base", 0x2000); + qdev_prop_set_uint32(DEVICE(&s->plic), "enable-stride", 0x18); + qdev_prop_set_uint32(DEVICE(&s->plic), "context-base", 0x200004); + qdev_prop_set_uint32(DEVICE(&s->plic), "context-stride", 4); + qdev_prop_set_uint32(DEVICE(&s->plic), "aperture-size", memmap[IBEX_DEV_PLIC].size); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->plic), errp)) { return; } @@ -153,7 +165,7 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) for (i = 0; i < ms->smp.cpus; i++) { CPUState *cpu = qemu_get_cpu(i); - qdev_connect_gpio_out(DEVICE(&s->plic), i, + qdev_connect_gpio_out(DEVICE(&s->plic), ms->smp.cpus + i, qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT)); } diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index 9f93bebdac..eac35ef590 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -20,7 +20,7 @@ #define HW_OPENTITAN_H #include "hw/riscv/riscv_hart.h" -#include "hw/intc/ibex_plic.h" +#include "hw/intc/sifive_plic.h" #include "hw/char/ibex_uart.h" #include "hw/timer/ibex_timer.h" #include "qom/object.h" @@ -34,7 +34,7 @@ struct LowRISCIbexSoCState { /*< public >*/ RISCVHartArrayState cpus; - IbexPlicState plic; + SiFivePLICState plic; IbexUartState uart; IbexTimerState timer; @@ -87,7 +87,7 @@ enum { }; enum { - IBEX_TIMER_TIMEREXPIRED0_0 = 125, + IBEX_TIMER_TIMEREXPIRED0_0 = 126, IBEX_UART0_RX_PARITY_ERR_IRQ = 8, IBEX_UART0_RX_TIMEOUT_IRQ = 7, IBEX_UART0_RX_BREAK_ERR_IRQ = 6, From 434e7e0217d73c20f60bd52437d0a0ba8a3c6247 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 18 Oct 2021 12:38:55 +1000 Subject: [PATCH 0737/1334] hw/intc: Remove the Ibex PLIC The Ibex PLIC is now spec compliant. Let's remove the Ibex PLIC and instead use the SiFive PLIC. Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 5557935c2660c5e6281b6d21e6514e019593662e.1634524691.git.alistair.francis@wdc.com --- hw/intc/ibex_plic.c | 307 -------------------------------------------- hw/intc/meson.build | 1 - 2 files changed, 308 deletions(-) delete mode 100644 hw/intc/ibex_plic.c diff --git a/hw/intc/ibex_plic.c b/hw/intc/ibex_plic.c deleted file mode 100644 index ff430356f8..0000000000 --- a/hw/intc/ibex_plic.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * QEMU RISC-V lowRISC Ibex PLIC - * - * Copyright (c) 2020 Western Digital - * - * Documentation avaliable: https://docs.opentitan.org/hw/ip/rv_plic/doc/ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2 or later, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "hw/qdev-properties.h" -#include "hw/core/cpu.h" -#include "hw/boards.h" -#include "hw/pci/msi.h" -#include "target/riscv/cpu_bits.h" -#include "target/riscv/cpu.h" -#include "hw/intc/ibex_plic.h" -#include "hw/irq.h" - -static bool addr_between(uint32_t addr, uint32_t base, uint32_t num) -{ - uint32_t end = base + (num * 0x04); - - if (addr >= base && addr < end) { - return true; - } - - return false; -} - -static void ibex_plic_irqs_set_pending(IbexPlicState *s, int irq, bool level) -{ - int pending_num = irq / 32; - - if (!level) { - /* - * If the level is low make sure we clear the hidden_pending. - */ - s->hidden_pending[pending_num] &= ~(1 << (irq % 32)); - } - - if (s->claimed[pending_num] & 1 << (irq % 32)) { - /* - * The interrupt has been claimed, but not completed. - * The pending bit can't be set. - * Save the pending level for after the interrupt is completed. - */ - s->hidden_pending[pending_num] |= level << (irq % 32); - } else { - s->pending[pending_num] |= level << (irq % 32); - } -} - -static bool ibex_plic_irqs_pending(IbexPlicState *s, uint32_t context) -{ - int i; - uint32_t max_irq = 0; - uint32_t max_prio = s->threshold; - - for (i = 0; i < s->pending_num; i++) { - uint32_t irq_num = ctz64(s->pending[i]) + (i * 32); - - if (!(s->pending[i] & s->enable[i])) { - /* No pending and enabled IRQ */ - continue; - } - - if (s->priority[irq_num] > max_prio) { - max_irq = irq_num; - max_prio = s->priority[irq_num]; - } - } - - if (max_irq) { - s->claim = max_irq; - return true; - } - - return false; -} - -static void ibex_plic_update(IbexPlicState *s) -{ - int i; - - for (i = 0; i < s->num_cpus; i++) { - qemu_set_irq(s->external_irqs[i], ibex_plic_irqs_pending(s, 0)); - } -} - -static void ibex_plic_reset(DeviceState *dev) -{ - IbexPlicState *s = IBEX_PLIC(dev); - - s->threshold = 0x00000000; - s->claim = 0x00000000; -} - -static uint64_t ibex_plic_read(void *opaque, hwaddr addr, - unsigned int size) -{ - IbexPlicState *s = opaque; - int offset; - uint32_t ret = 0; - - if (addr_between(addr, s->pending_base, s->pending_num)) { - offset = (addr - s->pending_base) / 4; - ret = s->pending[offset]; - } else if (addr_between(addr, s->source_base, s->source_num)) { - qemu_log_mask(LOG_UNIMP, - "%s: Interrupt source mode not supported\n", __func__); - } else if (addr_between(addr, s->priority_base, s->priority_num)) { - offset = (addr - s->priority_base) / 4; - ret = s->priority[offset]; - } else if (addr_between(addr, s->enable_base, s->enable_num)) { - offset = (addr - s->enable_base) / 4; - ret = s->enable[offset]; - } else if (addr_between(addr, s->threshold_base, 1)) { - ret = s->threshold; - } else if (addr_between(addr, s->claim_base, 1)) { - int pending_num = s->claim / 32; - s->pending[pending_num] &= ~(1 << (s->claim % 32)); - - /* Set the interrupt as claimed, but not completed */ - s->claimed[pending_num] |= 1 << (s->claim % 32); - - /* Return the current claimed interrupt */ - ret = s->claim; - - /* Clear the claimed interrupt */ - s->claim = 0x00000000; - - /* Update the interrupt status after the claim */ - ibex_plic_update(s); - } - - return ret; -} - -static void ibex_plic_write(void *opaque, hwaddr addr, - uint64_t value, unsigned int size) -{ - IbexPlicState *s = opaque; - - if (addr_between(addr, s->pending_base, s->pending_num)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Pending registers are read only\n", __func__); - } else if (addr_between(addr, s->source_base, s->source_num)) { - qemu_log_mask(LOG_UNIMP, - "%s: Interrupt source mode not supported\n", __func__); - } else if (addr_between(addr, s->priority_base, s->priority_num)) { - uint32_t irq = ((addr - s->priority_base) >> 2) + 1; - s->priority[irq] = value & 7; - ibex_plic_update(s); - } else if (addr_between(addr, s->enable_base, s->enable_num)) { - uint32_t enable_reg = (addr - s->enable_base) / 4; - - s->enable[enable_reg] = value; - } else if (addr_between(addr, s->threshold_base, 1)) { - s->threshold = value & 3; - } else if (addr_between(addr, s->claim_base, 1)) { - if (s->claim == value) { - /* Interrupt was completed */ - s->claim = 0; - } - if (s->claimed[value / 32] & 1 << (value % 32)) { - int pending_num = value / 32; - - /* This value was already claimed, clear it. */ - s->claimed[pending_num] &= ~(1 << (value % 32)); - - if (s->hidden_pending[pending_num] & (1 << (value % 32))) { - /* - * If the bit in hidden_pending is set then that means we - * received an interrupt between claiming and completing - * the interrupt that hasn't since been de-asserted. - * On hardware this would trigger an interrupt, so let's - * trigger one here as well. - */ - s->pending[pending_num] |= 1 << (value % 32); - } - } - } - - ibex_plic_update(s); -} - -static const MemoryRegionOps ibex_plic_ops = { - .read = ibex_plic_read, - .write = ibex_plic_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void ibex_plic_irq_request(void *opaque, int irq, int level) -{ - IbexPlicState *s = opaque; - - ibex_plic_irqs_set_pending(s, irq, level > 0); - ibex_plic_update(s); -} - -static Property ibex_plic_properties[] = { - DEFINE_PROP_UINT32("num-cpus", IbexPlicState, num_cpus, 1), - DEFINE_PROP_UINT32("num-sources", IbexPlicState, num_sources, 176), - - DEFINE_PROP_UINT32("pending-base", IbexPlicState, pending_base, 0), - DEFINE_PROP_UINT32("pending-num", IbexPlicState, pending_num, 6), - - DEFINE_PROP_UINT32("source-base", IbexPlicState, source_base, 0x18), - DEFINE_PROP_UINT32("source-num", IbexPlicState, source_num, 6), - - DEFINE_PROP_UINT32("priority-base", IbexPlicState, priority_base, 0x30), - DEFINE_PROP_UINT32("priority-num", IbexPlicState, priority_num, 177), - - DEFINE_PROP_UINT32("enable-base", IbexPlicState, enable_base, 0x300), - DEFINE_PROP_UINT32("enable-num", IbexPlicState, enable_num, 6), - - DEFINE_PROP_UINT32("threshold-base", IbexPlicState, threshold_base, 0x318), - - DEFINE_PROP_UINT32("claim-base", IbexPlicState, claim_base, 0x31c), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ibex_plic_init(Object *obj) -{ - IbexPlicState *s = IBEX_PLIC(obj); - - memory_region_init_io(&s->mmio, obj, &ibex_plic_ops, s, - TYPE_IBEX_PLIC, 0x400); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); -} - -static void ibex_plic_realize(DeviceState *dev, Error **errp) -{ - IbexPlicState *s = IBEX_PLIC(dev); - int i; - - s->pending = g_new0(uint32_t, s->pending_num); - s->hidden_pending = g_new0(uint32_t, s->pending_num); - s->claimed = g_new0(uint32_t, s->pending_num); - s->source = g_new0(uint32_t, s->source_num); - s->priority = g_new0(uint32_t, s->priority_num); - s->enable = g_new0(uint32_t, s->enable_num); - - qdev_init_gpio_in(dev, ibex_plic_irq_request, s->num_sources); - - s->external_irqs = g_malloc(sizeof(qemu_irq) * s->num_cpus); - qdev_init_gpio_out(dev, s->external_irqs, s->num_cpus); - - /* - * We can't allow the supervisor to control SEIP as this would allow the - * supervisor to clear a pending external interrupt which will result in - * a lost interrupt in the case a PLIC is attached. The SEIP bit must be - * hardware controlled when a PLIC is attached. - */ - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int smp_cpus = ms->smp.cpus; - for (i = 0; i < smp_cpus; i++) { - RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i)); - if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { - error_report("SEIP already claimed"); - exit(1); - } - } - - msi_nonbroken = true; -} - -static void ibex_plic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = ibex_plic_reset; - device_class_set_props(dc, ibex_plic_properties); - dc->realize = ibex_plic_realize; -} - -static const TypeInfo ibex_plic_info = { - .name = TYPE_IBEX_PLIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IbexPlicState), - .instance_init = ibex_plic_init, - .class_init = ibex_plic_class_init, -}; - -static void ibex_plic_register_types(void) -{ - type_register_static(&ibex_plic_info); -} - -type_init(ibex_plic_register_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index a1d00aa48d..c89d2ca180 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -32,7 +32,6 @@ specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) specific_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_vic.c')) specific_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_gic.c', 'exynos4210_combiner.c')) specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) -specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_plic.c')) specific_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) specific_ss.add(when: 'CONFIG_LOONGSON_LIOINTC', if_true: files('loongson_liointc.c')) specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gic.c')) From d8c6590f183cd93956b7a458d212778a143b89c8 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 18 Oct 2021 12:39:11 +1000 Subject: [PATCH 0738/1334] hw/intc: sifive_plic: Move the properties Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 3c125e27c49a4969df82bf8b197535ccd1996939.1634524691.git.alistair.francis@wdc.com --- hw/intc/sifive_plic.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index 9ba36dc0b3..f0e2799efc 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -355,21 +355,6 @@ static const MemoryRegionOps sifive_plic_ops = { } }; -static Property sifive_plic_properties[] = { - DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), - DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), - DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), - DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), - DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), - DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), - DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), - DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), - DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), - DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), - DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), - DEFINE_PROP_END_OF_LIST(), -}; - /* * parse PLIC hart/mode address offset config * @@ -496,6 +481,21 @@ static const VMStateDescription vmstate_sifive_plic = { } }; +static Property sifive_plic_properties[] = { + DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), + DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), + DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), + DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), + DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), + DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), + DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), + DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), + DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), + DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), + DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void sifive_plic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); From d680ff664e1c7e097922f62fef824027b8fb711a Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 18 Oct 2021 12:39:26 +1000 Subject: [PATCH 0739/1334] hw/intc: sifive_plic: Cleanup the realize function Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Message-id: b94c098cb221e744683349b1ac794c23102ef471.1634524691.git.alistair.francis@wdc.com --- hw/intc/sifive_plic.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index f0e2799efc..d77a5ced23 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -422,35 +422,38 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level) static void sifive_plic_realize(DeviceState *dev, Error **errp) { - SiFivePLICState *plic = SIFIVE_PLIC(dev); + SiFivePLICState *s = SIFIVE_PLIC(dev); int i; - memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, - TYPE_SIFIVE_PLIC, plic->aperture_size); - parse_hart_config(plic); - plic->bitfield_words = (plic->num_sources + 31) >> 5; - plic->num_enables = plic->bitfield_words * plic->num_addrs; - plic->source_priority = g_new0(uint32_t, plic->num_sources); - plic->target_priority = g_new(uint32_t, plic->num_addrs); - plic->pending = g_new0(uint32_t, plic->bitfield_words); - plic->claimed = g_new0(uint32_t, plic->bitfield_words); - plic->enable = g_new0(uint32_t, plic->num_enables); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); - qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); + memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_plic_ops, s, + TYPE_SIFIVE_PLIC, s->aperture_size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); - plic->s_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts); - qdev_init_gpio_out(dev, plic->s_external_irqs, plic->num_harts); + parse_hart_config(s); - plic->m_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts); - qdev_init_gpio_out(dev, plic->m_external_irqs, plic->num_harts); + s->bitfield_words = (s->num_sources + 31) >> 5; + s->num_enables = s->bitfield_words * s->num_addrs; + s->source_priority = g_new0(uint32_t, s->num_sources); + s->target_priority = g_new(uint32_t, s->num_addrs); + s->pending = g_new0(uint32_t, s->bitfield_words); + s->claimed = g_new0(uint32_t, s->bitfield_words); + s->enable = g_new0(uint32_t, s->num_enables); + + qdev_init_gpio_in(dev, sifive_plic_irq_request, s->num_sources); + + s->s_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); + qdev_init_gpio_out(dev, s->s_external_irqs, s->num_harts); + + s->m_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); + qdev_init_gpio_out(dev, s->m_external_irqs, s->num_harts); /* We can't allow the supervisor to control SEIP as this would allow the * supervisor to clear a pending external interrupt which will result in * lost a interrupt in the case a PLIC is attached. The SEIP bit must be * hardware controlled when a PLIC is attached. */ - for (i = 0; i < plic->num_harts; i++) { - RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(plic->hartid_base + i)); + for (i = 0; i < s->num_harts; i++) { + RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i)); if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { error_report("SEIP already claimed"); exit(1); From 8d3dae162e61e6c71bbd3d9878cd1768ae9dd989 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 18 Oct 2021 12:39:41 +1000 Subject: [PATCH 0740/1334] hw/intc: sifive_plic: Cleanup the irq_request function Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 4200da222a65c89ed1ba35f754dcca7fdd9f08d6.1634524691.git.alistair.francis@wdc.com --- hw/intc/sifive_plic.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index d77a5ced23..877e76877c 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -412,12 +412,10 @@ static void parse_hart_config(SiFivePLICState *plic) static void sifive_plic_irq_request(void *opaque, int irq, int level) { - SiFivePLICState *plic = opaque; - if (RISCV_DEBUG_PLIC) { - qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level); - } - sifive_plic_set_pending(plic, irq, level > 0); - sifive_plic_update(plic); + SiFivePLICState *s = opaque; + + sifive_plic_set_pending(s, irq, level > 0); + sifive_plic_update(s); } static void sifive_plic_realize(DeviceState *dev, Error **errp) From d4c624f482778cfe91938996a588df2df0e70f20 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 20 Oct 2021 09:41:07 +0800 Subject: [PATCH 0741/1334] hw/riscv: microchip_pfsoc: Use MachineState::ram and MachineClass::default_ram_id Using memory_region_init_ram(), which can't possibly handle vhost-user, and can't work as expected with '-numa node,memdev' options. Use MachineState::ram instead of manually initializing RAM memory region, as well as by providing MachineClass::default_ram_id to opt in to memdev scheme. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Reviewed-by: Igor Mammedov Message-id: 20211020014112.7336-2-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index e475b6d511..3fc8545562 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -463,7 +463,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) MemoryRegion *mem_low_alias = g_new(MemoryRegion, 1); MemoryRegion *mem_high = g_new(MemoryRegion, 1); MemoryRegion *mem_high_alias = g_new(MemoryRegion, 1); - uint64_t mem_high_size; + uint64_t mem_low_size, mem_high_size; hwaddr firmware_load_addr; const char *firmware_name; bool kernel_as_payload = false; @@ -485,31 +485,34 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) TYPE_MICROCHIP_PFSOC); qdev_realize(DEVICE(&s->soc), NULL, &error_abort); + /* Split RAM into low and high regions using aliases to machine->ram */ + mem_low_size = memmap[MICROCHIP_PFSOC_DRAM_LO].size; + mem_high_size = machine->ram_size - mem_low_size; + memory_region_init_alias(mem_low, NULL, + "microchip.icicle.kit.ram_low", machine->ram, + 0, mem_low_size); + memory_region_init_alias(mem_high, NULL, + "microchip.icicle.kit.ram_high", machine->ram, + mem_low_size, mem_high_size); + /* Register RAM */ - memory_region_init_ram(mem_low, NULL, "microchip.icicle.kit.ram_low", - memmap[MICROCHIP_PFSOC_DRAM_LO].size, - &error_fatal); - memory_region_init_alias(mem_low_alias, NULL, - "microchip.icicle.kit.ram_low.alias", - mem_low, 0, - memmap[MICROCHIP_PFSOC_DRAM_LO_ALIAS].size); memory_region_add_subregion(system_memory, memmap[MICROCHIP_PFSOC_DRAM_LO].base, mem_low); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM_HI].base, + mem_high); + + /* Create aliases for the low and high RAM regions */ + memory_region_init_alias(mem_low_alias, NULL, + "microchip.icicle.kit.ram_low.alias", + mem_low, 0, mem_low_size); memory_region_add_subregion(system_memory, memmap[MICROCHIP_PFSOC_DRAM_LO_ALIAS].base, mem_low_alias); - - mem_high_size = machine->ram_size - 1 * GiB; - - memory_region_init_ram(mem_high, NULL, "microchip.icicle.kit.ram_high", - mem_high_size, &error_fatal); memory_region_init_alias(mem_high_alias, NULL, "microchip.icicle.kit.ram_high.alias", mem_high, 0, mem_high_size); - memory_region_add_subregion(system_memory, - memmap[MICROCHIP_PFSOC_DRAM_HI].base, - mem_high); memory_region_add_subregion(system_memory, memmap[MICROCHIP_PFSOC_DRAM_HI_ALIAS].base, mem_high_alias); @@ -606,6 +609,7 @@ static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) MICROCHIP_PFSOC_COMPUTE_CPU_COUNT; mc->min_cpus = MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT + 1; mc->default_cpus = mc->min_cpus; + mc->default_ram_id = "microchip.icicle.kit.ram"; /* * Map 513 MiB high memory, the mimimum required high memory size, because From 91b1fbdc0cca11ac23ddc61ee3ea0e9706b645cf Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 20 Oct 2021 09:41:08 +0800 Subject: [PATCH 0742/1334] hw/riscv: opentitan: Use MachineState::ram and MachineClass::default_ram_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using memory_region_init_ram(), which can't possibly handle vhost-user, and can't work as expected with '-numa node,memdev' options. Use MachineState::ram instead of manually initializing RAM memory region, as well as by providing MachineClass::default_ram_id to opt in to memdev scheme. While at it add check for user supplied RAM size and error out if it mismatches board expected value. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Igor Mammedov Message-id: 20211020014112.7336-3-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/riscv/opentitan.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 601f8deebe..83e1511f28 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "hw/riscv/opentitan.h" #include "qapi/error.h" #include "hw/boards.h" @@ -64,20 +65,25 @@ static const MemMapEntry ibex_memmap[] = { static void opentitan_board_init(MachineState *machine) { + MachineClass *mc = MACHINE_GET_CLASS(machine); const MemMapEntry *memmap = ibex_memmap; OpenTitanState *s = g_new0(OpenTitanState, 1); MemoryRegion *sys_mem = get_system_memory(); - MemoryRegion *main_mem = g_new(MemoryRegion, 1); + + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } /* Initialize SoC */ object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_IBEX_SOC); qdev_realize(DEVICE(&s->soc), NULL, &error_abort); - memory_region_init_ram(main_mem, NULL, "riscv.lowrisc.ibex.ram", - memmap[IBEX_DEV_RAM].size, &error_fatal); memory_region_add_subregion(sys_mem, - memmap[IBEX_DEV_RAM].base, main_mem); + memmap[IBEX_DEV_RAM].base, machine->ram); if (machine->firmware) { riscv_load_firmware(machine->firmware, memmap[IBEX_DEV_RAM].base, NULL); @@ -95,6 +101,8 @@ static void opentitan_machine_init(MachineClass *mc) mc->init = opentitan_board_init; mc->max_cpus = 1; mc->default_cpu_type = TYPE_RISCV_CPU_IBEX; + mc->default_ram_id = "riscv.lowrisc.ibex.ram"; + mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; } DEFINE_MACHINE("opentitan", opentitan_machine_init) From 56917307f4780535d319dedc8973361a5da76f7b Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 20 Oct 2021 09:41:09 +0800 Subject: [PATCH 0743/1334] hw/riscv: shakti_c: Use MachineState::ram and MachineClass::default_ram_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using memory_region_init_ram(), which can't possibly handle vhost-user, and can't work as expected with '-numa node,memdev' options. Use MachineState::ram instead of manually initializing RAM memory region, as well as by providing MachineClass::default_ram_id to opt in to memdev scheme. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Reviewed-by: Alistair Francis Message-id: 20211020014112.7336-4-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/riscv/shakti_c.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index d7d1f91fa5..90e2cf609f 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -45,7 +45,6 @@ static void shakti_c_machine_state_init(MachineState *mstate) { ShaktiCMachineState *sms = RISCV_SHAKTI_MACHINE(mstate); MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *main_mem = g_new(MemoryRegion, 1); /* Allow only Shakti C CPU for this platform */ if (strcmp(mstate->cpu_type, TYPE_RISCV_CPU_SHAKTI_C) != 0) { @@ -59,11 +58,9 @@ static void shakti_c_machine_state_init(MachineState *mstate) qdev_realize(DEVICE(&sms->soc), NULL, &error_abort); /* register RAM */ - memory_region_init_ram(main_mem, NULL, "riscv.shakti.c.ram", - mstate->ram_size, &error_fatal); memory_region_add_subregion(system_memory, shakti_c_memmap[SHAKTI_C_RAM].base, - main_mem); + mstate->ram); /* ROM reset vector */ riscv_setup_rom_reset_vec(mstate, &sms->soc.cpus, @@ -88,6 +85,7 @@ static void shakti_c_machine_class_init(ObjectClass *klass, void *data) mc->desc = "RISC-V Board compatible with Shakti SDK"; mc->init = shakti_c_machine_state_init; mc->default_cpu_type = TYPE_RISCV_CPU_SHAKTI_C; + mc->default_ram_id = "riscv.shakti.c.ram"; } static const TypeInfo shakti_c_machine_type_info = { From e2b3ef75445fc2342b00680dad7af3618dc69deb Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 20 Oct 2021 09:41:10 +0800 Subject: [PATCH 0744/1334] hw/riscv: sifive_e: Use MachineState::ram and MachineClass::default_ram_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using memory_region_init_ram(), which can't possibly handle vhost-user, and can't work as expected with '-numa node,memdev' options. Use MachineState::ram instead of manually initializing RAM memory region, as well as by providing MachineClass::default_ram_id to opt in to memdev scheme. While at it add check for user supplied RAM size and error out if it mismatches board expected value. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Igor Mammedov Message-id: 20211020014112.7336-5-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/riscv/sifive_e.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 6e95ea5896..9b206407a6 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/boards.h" @@ -71,22 +72,27 @@ static const MemMapEntry sifive_e_memmap[] = { static void sifive_e_machine_init(MachineState *machine) { + MachineClass *mc = MACHINE_GET_CLASS(machine); const MemMapEntry *memmap = sifive_e_memmap; SiFiveEState *s = RISCV_E_MACHINE(machine); MemoryRegion *sys_mem = get_system_memory(); - MemoryRegion *main_mem = g_new(MemoryRegion, 1); int i; + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + /* Initialize SoC */ object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_E_SOC); qdev_realize(DEVICE(&s->soc), NULL, &error_abort); /* Data Tightly Integrated Memory */ - memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram", - memmap[SIFIVE_E_DEV_DTIM].size, &error_fatal); memory_region_add_subregion(sys_mem, - memmap[SIFIVE_E_DEV_DTIM].base, main_mem); + memmap[SIFIVE_E_DEV_DTIM].base, machine->ram); /* Mask ROM reset vector */ uint32_t reset_vec[4]; @@ -142,6 +148,8 @@ static void sifive_e_machine_class_init(ObjectClass *oc, void *data) mc->init = sifive_e_machine_init; mc->max_cpus = 1; mc->default_cpu_type = SIFIVE_E_CPU; + mc->default_ram_id = "riscv.sifive.e.ram"; + mc->default_ram_size = sifive_e_memmap[SIFIVE_E_DEV_DTIM].size; object_class_property_add_bool(oc, "revb", sifive_e_machine_get_revb, sifive_e_machine_set_revb); From c188a9c4f78e21f7f979ab7ef19c5bf21e9dcf08 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 20 Oct 2021 09:41:11 +0800 Subject: [PATCH 0745/1334] hw/riscv: sifive_u: Use MachineState::ram and MachineClass::default_ram_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using memory_region_init_ram(), which can't possibly handle vhost-user, and can't work as expected with '-numa node,memdev' options. Use MachineState::ram instead of manually initializing RAM memory region, as well as by providing MachineClass::default_ram_id to opt in to memdev scheme. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Reviewed-by: Alistair Francis Message-id: 20211020014112.7336-6-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/riscv/sifive_u.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index fc5790b8ce..0217006c27 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -528,7 +528,6 @@ static void sifive_u_machine_init(MachineState *machine) const MemMapEntry *memmap = sifive_u_memmap; SiFiveUState *s = RISCV_U_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *main_mem = g_new(MemoryRegion, 1); MemoryRegion *flash0 = g_new(MemoryRegion, 1); target_ulong start_addr = memmap[SIFIVE_U_DEV_DRAM].base; target_ulong firmware_end_addr, kernel_start_addr; @@ -549,10 +548,8 @@ static void sifive_u_machine_init(MachineState *machine) qdev_realize(DEVICE(&s->soc), NULL, &error_abort); /* register RAM */ - memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram", - machine->ram_size, &error_fatal); memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DEV_DRAM].base, - main_mem); + machine->ram); /* register QSPI0 Flash */ memory_region_init_ram(flash0, NULL, "riscv.sifive.u.flash0", @@ -748,6 +745,7 @@ static void sifive_u_machine_class_init(ObjectClass *oc, void *data) mc->min_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + 1; mc->default_cpu_type = SIFIVE_U_CPU; mc->default_cpus = mc->min_cpus; + mc->default_ram_id = "riscv.sifive.u.ram"; object_class_property_add_bool(oc, "start-in-flash", sifive_u_machine_get_start_in_flash, From 11ec06f9eaedc801ded34c79861367b76ab2b731 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 20 Oct 2021 09:41:12 +0800 Subject: [PATCH 0746/1334] hw/riscv: spike: Use MachineState::ram and MachineClass::default_ram_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using memory_region_init_ram(), which can't possibly handle vhost-user, and can't work as expected with '-numa node,memdev' options. Use MachineState::ram instead of manually initializing RAM memory region, as well as by providing MachineClass::default_ram_id to opt in to memdev scheme. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Reviewed-by: Alistair Francis Message-id: 20211020014112.7336-7-bmeng.cn@gmail.com Signed-off-by: Alistair Francis --- hw/riscv/spike.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 79ae355ae2..288d69cd9f 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -180,7 +180,6 @@ static void spike_board_init(MachineState *machine) const MemMapEntry *memmap = spike_memmap; SpikeState *s = SPIKE_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *main_mem = g_new(MemoryRegion, 1); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); target_ulong firmware_end_addr, kernel_start_addr; uint32_t fdt_load_addr; @@ -239,10 +238,8 @@ static void spike_board_init(MachineState *machine) } /* register system main memory (actual RAM) */ - memory_region_init_ram(main_mem, NULL, "riscv.spike.ram", - machine->ram_size, &error_fatal); memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base, - main_mem); + machine->ram); /* create device tree */ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, @@ -326,6 +323,7 @@ static void spike_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id; mc->numa_mem_supported = true; + mc->default_ram_id = "riscv.spike.ram"; } static const TypeInfo spike_machine_typeinfo = { From 764ecf77d03bb5fd42fac3d1042148da9130c552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Sun, 15 Aug 2021 22:22:18 +0300 Subject: [PATCH 0747/1334] po: update turkish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-Id: Signed-off-by: Oğuz Ersen Reviewed-by: Laurent Vivier [lv,pb: s/K_opyala/_Kopyala/;s/Se_kmeleri/_Sekmeleri/] Signed-off-by: Laurent Vivier --- po/tr.po | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/po/tr.po b/po/tr.po index 632c7f3851..f4f0425c43 100644 --- a/po/tr.po +++ b/po/tr.po @@ -1,14 +1,15 @@ # Turkish translation for QEMU. # This file is put in the public domain. # Ozan Çağlayan , 2013. +# Oğuz Ersen , 2021. # msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" "POT-Creation-Date: 2018-07-18 07:56+0200\n" -"PO-Revision-Date: 2013-04-22 18:35+0300\n" -"Last-Translator: Ozan Çağlayan \n" +"PO-Revision-Date: 2021-08-15 22:17+0300\n" +"Last-Translator: Oğuz Ersen \n" "Language-Team: Türkçe <>\n" "Language: tr\n" "MIME-Version: 1.0\n" @@ -33,24 +34,22 @@ msgid "Power _Down" msgstr "_Kapat" msgid "_Quit" -msgstr "" +msgstr "_Çıkış" msgid "_Fullscreen" -msgstr "" +msgstr "_Tam Ekran" msgid "_Copy" -msgstr "" +msgstr "_Kopyala" -#, fuzzy msgid "Zoom _In" -msgstr "Yakınlaş ve Sığ_dır" +msgstr "_Yakınlaş" -#, fuzzy msgid "Zoom _Out" -msgstr "Yakınlaş ve Sığ_dır" +msgstr "_Uzaklaş" msgid "Best _Fit" -msgstr "" +msgstr "_En Uygun" msgid "Zoom To _Fit" msgstr "Yakınlaş ve Sığ_dır" @@ -62,13 +61,13 @@ msgid "_Grab Input" msgstr "Girdiyi _Yakala" msgid "Show _Tabs" -msgstr "Se_kmeleri Göster" +msgstr "_Sekmeleri Göster" msgid "Detach Tab" -msgstr "" +msgstr "Sekmeyi Ayır" msgid "Show Menubar" -msgstr "" +msgstr "Menü Çubuğunu Göster" msgid "_Machine" msgstr "_Makine" From ed899ac77d5c8b83797a001d33a1e57390181574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 7 Aug 2021 13:09:38 +0200 Subject: [PATCH 0748/1334] disas/nios2: Fix style in print_insn_nios2() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to modify this function, fix its style first. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Reviewed-by: Thomas Huth Message-Id: <20210807110939.95853-2-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- disas/nios2.c | 53 +++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/disas/nios2.c b/disas/nios2.c index c3e82140c7..d124902ae3 100644 --- a/disas/nios2.c +++ b/disas/nios2.c @@ -3482,38 +3482,37 @@ static int print_insn_nios2 (bfd_vma address, disassemble_info *info, enum bfd_endian endianness) { - bfd_byte buffer[INSNLEN]; - int status; + bfd_byte buffer[INSNLEN]; + int status; - status = (*info->read_memory_func) (address, buffer, INSNLEN, info); - if (status == 0) - { - unsigned long insn; - if (endianness == BFD_ENDIAN_BIG) - insn = (unsigned long) bfd_getb32 (buffer); - else - insn = (unsigned long) bfd_getl32 (buffer); - return nios2_disassemble (address, insn, info); + status = (*info->read_memory_func)(address, buffer, INSNLEN, info); + if (status == 0) { + unsigned long insn; + if (endianness == BFD_ENDIAN_BIG) { + insn = (unsigned long) bfd_getb32(buffer); + } else { + insn = (unsigned long) bfd_getl32(buffer); + } + return nios2_disassemble(address, insn, info); } - /* We might have a 16-bit R2 instruction at the end of memory. Try that. */ - if (info->mach == bfd_mach_nios2r2) - { - status = (*info->read_memory_func) (address, buffer, 2, info); - if (status == 0) - { - unsigned long insn; - if (endianness == BFD_ENDIAN_BIG) - insn = (unsigned long) bfd_getb16 (buffer); - else - insn = (unsigned long) bfd_getl16 (buffer); - return nios2_disassemble (address, insn, info); - } + /* We might have a 16-bit R2 instruction at the end of memory. Try that. */ + if (info->mach == bfd_mach_nios2r2) { + status = (*info->read_memory_func)(address, buffer, 2, info); + if (status == 0) { + unsigned long insn; + if (endianness == BFD_ENDIAN_BIG) { + insn = (unsigned long) bfd_getb16(buffer); + } else { + insn = (unsigned long) bfd_getl16(buffer); + } + return nios2_disassemble(address, insn, info); + } } - /* If we got here, we couldn't read anything. */ - (*info->memory_error_func) (status, address, info); - return -1; + /* If we got here, we couldn't read anything. */ + (*info->memory_error_func)(status, address, info); + return -1; } /* These two functions are the main entry points, accessed from From dcc99bd833840c6c4e909d391df17b71e47dea62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 7 Aug 2021 13:09:39 +0200 Subject: [PATCH 0749/1334] disas/nios2: Simplify endianess conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 12b6e9b27d4 ("disas: Clean up CPUDebug initialization") the disassemble_info->bfd_endian enum is set for all targets in target_disas(). We can directly call print_insn_nios2() and simplify. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Reviewed-by: Thomas Huth Message-Id: <20210807110939.95853-3-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- disas/nios2.c | 22 +++------------------- include/disas/dis-asm.h | 3 +-- target/nios2/cpu.c | 6 +----- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/disas/nios2.c b/disas/nios2.c index d124902ae3..98ac07d72e 100644 --- a/disas/nios2.c +++ b/disas/nios2.c @@ -3478,9 +3478,7 @@ nios2_disassemble (bfd_vma address, unsigned long opcode, instruction word at the address given, and prints the disassembled instruction on the stream info->stream using info->fprintf_func. */ -static int -print_insn_nios2 (bfd_vma address, disassemble_info *info, - enum bfd_endian endianness) +int print_insn_nios2(bfd_vma address, disassemble_info *info) { bfd_byte buffer[INSNLEN]; int status; @@ -3488,7 +3486,7 @@ print_insn_nios2 (bfd_vma address, disassemble_info *info, status = (*info->read_memory_func)(address, buffer, INSNLEN, info); if (status == 0) { unsigned long insn; - if (endianness == BFD_ENDIAN_BIG) { + if (info->endian == BFD_ENDIAN_BIG) { insn = (unsigned long) bfd_getb32(buffer); } else { insn = (unsigned long) bfd_getl32(buffer); @@ -3501,7 +3499,7 @@ print_insn_nios2 (bfd_vma address, disassemble_info *info, status = (*info->read_memory_func)(address, buffer, 2, info); if (status == 0) { unsigned long insn; - if (endianness == BFD_ENDIAN_BIG) { + if (info->endian == BFD_ENDIAN_BIG) { insn = (unsigned long) bfd_getb16(buffer); } else { insn = (unsigned long) bfd_getl16(buffer); @@ -3514,17 +3512,3 @@ print_insn_nios2 (bfd_vma address, disassemble_info *info, (*info->memory_error_func)(status, address, info); return -1; } - -/* These two functions are the main entry points, accessed from - disassemble.c. */ -int -print_insn_big_nios2 (bfd_vma address, disassemble_info *info) -{ - return print_insn_nios2 (address, info, BFD_ENDIAN_BIG); -} - -int -print_insn_little_nios2 (bfd_vma address, disassemble_info *info) -{ - return print_insn_nios2 (address, info, BFD_ENDIAN_LITTLE); -} diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 524f29196d..08e1beec85 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -455,8 +455,7 @@ int print_insn_crisv32 (bfd_vma, disassemble_info*); int print_insn_crisv10 (bfd_vma, disassemble_info*); int print_insn_microblaze (bfd_vma, disassemble_info*); int print_insn_ia64 (bfd_vma, disassemble_info*); -int print_insn_big_nios2 (bfd_vma, disassemble_info*); -int print_insn_little_nios2 (bfd_vma, disassemble_info*); +int print_insn_nios2(bfd_vma, disassemble_info*); int print_insn_xtensa (bfd_vma, disassemble_info*); int print_insn_riscv32 (bfd_vma, disassemble_info*); int print_insn_riscv64 (bfd_vma, disassemble_info*); diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 947bb09bc1..58ecd27d75 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -147,11 +147,7 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { /* NOTE: NiosII R2 is not supported yet. */ info->mach = bfd_arch_nios2; -#ifdef TARGET_WORDS_BIGENDIAN - info->print_insn = print_insn_big_nios2; -#else - info->print_insn = print_insn_little_nios2; -#endif + info->print_insn = print_insn_nios2; } static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) From 3bc1bb80423b789cabedde2813630dd5092cb24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 11:31:08 +0200 Subject: [PATCH 0750/1334] MAINTAINERS: Add myself as reviewer of 'Machine core' API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to help Eduardo and Marcel with the machine core API, add myself as reviewer. That will also help me to learn more about this subsystem :) Signed-off-by: Philippe Mathieu-Daudé Reviewed by: Marcel Apfelbaum Message-Id: <20211007093108.323223-1-philmd@redhat.com> Signed-off-by: Laurent Vivier --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4e77d03651..894dc43105 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1617,6 +1617,7 @@ F: pc-bios/bios-microvm.bin Machine core M: Eduardo Habkost M: Marcel Apfelbaum +R: Philippe Mathieu-Daudé S: Supported F: cpu.c F: hw/core/cpu.c From f18d403f15b92ded56362703067eb67161cfe2a9 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Fri, 15 Oct 2021 11:29:44 +0200 Subject: [PATCH 0751/1334] softmmu/physmem.c: Fix typo in comment Fix the comment to match what the code is doing, as explained in the changelog of commit 86cf9e154632cb28d749db0ea47946fba8cf3f09 that introduced the change: Commit 9458a9a1df1a4c719e24512394d548c1fc7abd22 added synchronization of vCPU and migration operations through calling run_on_cpu operation. However, in replay mode this synchronization is unneeded, because I/O and vCPU threads are already synchronized. This patch disables such synchronization for record/replay mode. Signed-off-by: Greg Kurz Reviewed-by: David Hildenbrand Message-Id: <163429018454.1146856.3429437540871060739.stgit@bahia.huguette> Signed-off-by: Laurent Vivier --- softmmu/physmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index f67ad29981..555c907f67 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -2633,7 +2633,7 @@ static void tcg_log_global_after_sync(MemoryListener *listener) * In record/replay mode this causes a deadlock, because * run_on_cpu waits for rr mutex. Therefore no races are possible * in this case and no need for making run_on_cpu when - * record/replay is not enabled. + * record/replay is enabled. */ cpuas = container_of(listener, CPUAddressSpace, tcg_as_listener); run_on_cpu(cpuas->cpu, do_nothing, RUN_ON_CPU_NULL); From c4e4d0d92b37430b74be3e9dab6066d2f4b69a95 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Fri, 15 Oct 2021 13:35:30 -0700 Subject: [PATCH 0752/1334] hw/nvram: Fix Memory Leak in Xilinx eFuse QOM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tong Ho Reviewed-by: Edgar E. Iglesias Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211015203532.2463705-2-tong.ho@xilinx.com> Signed-off-by: Laurent Vivier --- hw/nvram/xlnx-efuse.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index ee1caab54c..a0fd77b586 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -144,10 +144,11 @@ static bool efuse_ro_bits_find(XlnxEFuse *s, uint32_t k) bool xlnx_efuse_set_bit(XlnxEFuse *s, unsigned int bit) { if (efuse_ro_bits_find(s, bit)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + qemu_log_mask(LOG_GUEST_ERROR, "%s: WARN: " "Ignored setting of readonly efuse bit<%u,%u>!\n", - object_get_canonical_path(OBJECT(s)), - (bit / 32), (bit % 32)); + path, (bit / 32), (bit % 32)); return false; } @@ -202,9 +203,11 @@ static void efuse_realize(DeviceState *dev, Error **errp) efuse_ro_bits_sort(s); if ((s->efuse_size % 32) != 0) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + error_setg(errp, "%s.efuse-size: %u: property value not multiple of 32.", - object_get_canonical_path(OBJECT(dev)), s->efuse_size); + path, s->efuse_size); return; } From 512a63b2b00ee0e7bf99bda2d8e6ce807dfa32c2 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Fri, 15 Oct 2021 13:35:31 -0700 Subject: [PATCH 0753/1334] hw/nvram: Fix Memory Leak in Xilinx Versal eFuse device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tong Ho Reviewed-by: Edgar E. Iglesias Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211015203532.2463705-3-tong.ho@xilinx.com> Signed-off-by: Laurent Vivier --- hw/nvram/xlnx-versal-efuse-ctrl.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index d362376703..b35ba65ab5 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -439,9 +439,11 @@ static void efuse_pgm_addr_postw(RegisterInfo *reg, uint64_t val64) * up to guest to do so (or by reset). */ if (efuse_pgm_locked(s, bit)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Denied setting of efuse<%u, %u, %u>\n", - object_get_canonical_path(OBJECT(s)), + path, FIELD_EX32(bit, EFUSE_PGM_ADDR, PAGE), FIELD_EX32(bit, EFUSE_PGM_ADDR, ROW), FIELD_EX32(bit, EFUSE_PGM_ADDR, COLUMN)); @@ -478,9 +480,11 @@ static void efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) s->regs[R_EFUSE_RD_DATA] = xlnx_versal_efuse_read_row(s->efuse, bit, &denied); if (denied) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Denied reading of efuse<%u, %u>\n", - object_get_canonical_path(OBJECT(s)), + path, FIELD_EX32(bit, EFUSE_RD_ADDR, PAGE), FIELD_EX32(bit, EFUSE_RD_ADDR, ROW)); @@ -625,9 +629,11 @@ static void efuse_ctrl_reg_write(void *opaque, hwaddr addr, s = XLNX_VERSAL_EFUSE_CTRL(dev); if (addr != A_WR_LOCK && s->regs[R_WR_LOCK]) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + qemu_log_mask(LOG_GUEST_ERROR, "%s[reg_0x%02lx]: Attempt to write locked register.\n", - object_get_canonical_path(OBJECT(s)), (long)addr); + path, (long)addr); } else { register_write_memory(opaque, addr, data, size); } @@ -681,16 +687,20 @@ static void efuse_ctrl_realize(DeviceState *dev, Error **errp) const uint32_t lks_sz = sizeof(XlnxEFuseLkSpec) / 2; if (!s->efuse) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + error_setg(errp, "%s.efuse: link property not connected to XLNX-EFUSE", - object_get_canonical_path(OBJECT(dev))); + path); return; } /* Sort property-defined pgm-locks for bsearch lookup */ if ((s->extra_pg0_lock_n16 % lks_sz) != 0) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + error_setg(errp, "%s.pg0-lock: array property item-count not multiple of %u", - object_get_canonical_path(OBJECT(dev)), lks_sz); + path, lks_sz); return; } From e3f368e0b280315d5dd3bc26fb00b56587551d87 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Fri, 15 Oct 2021 13:35:32 -0700 Subject: [PATCH 0754/1334] hw/nvram: Fix Memory Leak in Xilinx ZynqMP eFuse device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tong Ho Reviewed-by: Edgar E. Iglesias Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211015203532.2463705-4-tong.ho@xilinx.com> Signed-off-by: Laurent Vivier --- hw/nvram/xlnx-zynqmp-efuse.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index 1f87dbf988..228ba0bbfa 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -434,11 +434,12 @@ static void zynqmp_efuse_pgm_addr_postw(RegisterInfo *reg, uint64_t val64) if (!errmsg) { ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_ERROR, 0); } else { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_ERROR, 1); qemu_log_mask(LOG_GUEST_ERROR, "%s - eFuse write error: %s; addr=0x%x\n", - object_get_canonical_path(OBJECT(s)), - errmsg, (unsigned)val64); + path, errmsg, (unsigned)val64); } ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, PGM_DONE, 1); @@ -448,6 +449,7 @@ static void zynqmp_efuse_pgm_addr_postw(RegisterInfo *reg, uint64_t val64) static void zynqmp_efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) { XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(reg->opaque); + g_autofree char *path = NULL; /* * Grant reads only to allowed bits; reference sources: @@ -538,10 +540,10 @@ static void zynqmp_efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) return; denied: + path = object_get_canonical_path(OBJECT(s)); qemu_log_mask(LOG_GUEST_ERROR, "%s: Denied efuse read from array %u, row %u\n", - object_get_canonical_path(OBJECT(s)), - efuse_ary, efuse_row); + path, efuse_ary, efuse_row); s->regs[R_EFUSE_RD_DATA] = 0; @@ -731,9 +733,11 @@ static void zynqmp_efuse_reg_write(void *opaque, hwaddr addr, s = XLNX_ZYNQMP_EFUSE(dev); if (addr != A_WR_LOCK && s->regs[R_WR_LOCK]) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + qemu_log_mask(LOG_GUEST_ERROR, "%s[reg_0x%02lx]: Attempt to write locked register.\n", - object_get_canonical_path(OBJECT(s)), (long)addr); + path, (long)addr); } else { register_write_memory(opaque, addr, data, size); } @@ -784,8 +788,10 @@ static void zynqmp_efuse_realize(DeviceState *dev, Error **errp) XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(dev); if (!s->efuse) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + error_setg(errp, "%s.efuse: link property not connected to XLNX-EFUSE", - object_get_canonical_path(OBJECT(dev))); + path); return; } From 1c3515ad59878ad249a61a4a952f1820a12afb83 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 18 Oct 2021 15:45:08 +0200 Subject: [PATCH 0755/1334] README: Fix some documentation URLs All of these pages live in the wiki, not in the main web site. Signed-off-by: Greg Kurz Reviewed-by: Laurent Vivier Tested-by: Laurent Vivier Message-Id: <163456470882.196333.17366490695504718038.stgit@bahia.huguette> Signed-off-by: Laurent Vivier --- README.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 79b19f1481..23795b8377 100644 --- a/README.rst +++ b/README.rst @@ -59,9 +59,9 @@ of other UNIX targets. The simple steps to build QEMU are: Additional information can also be found online via the QEMU website: -* ``_ -* ``_ -* ``_ +* ``_ +* ``_ +* ``_ Submitting patches @@ -84,8 +84,8 @@ the Developers Guide. Additional information on submitting patches can be found online via the QEMU website -* ``_ -* ``_ +* ``_ +* ``_ The QEMU website is also maintained under source control. @@ -144,7 +144,7 @@ reported via GitLab. For additional information on bug reporting consult: -* ``_ +* ``_ ChangeLog @@ -168,4 +168,4 @@ main methods being email and IRC Information on additional methods of contacting the community can be found online via the QEMU website: -* ``_ +* ``_ From f98d372aeff5109d2b5a3b858a51347e0ccd36b1 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 15 Oct 2021 15:16:44 +0200 Subject: [PATCH 0756/1334] analyze-migration.py: fix a long standing typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parameters of '-d' can be either 'state' or 'desc', not 'dump' as it is reported in the error message. Fixes: b17425701d66 ("Add migration stream analyzation script") Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211015131645.501281-2-lvivier@redhat.com> Signed-off-by: Laurent Vivier --- scripts/analyze-migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index d7177b212c..9d239d309f 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -610,4 +610,4 @@ elif args.dump == "desc": dump.read(desc_only = True) print(jsonenc.encode(dump.vmsd_desc)) else: - raise Exception("Please specify either -x, -d state or -d dump") + raise Exception("Please specify either -x, -d state or -d desc") From 2c92be50bcfa8b7529a39fc99078ef14dcfc71aa Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 15 Oct 2021 15:16:45 +0200 Subject: [PATCH 0757/1334] analyze-migration.py: fix extract contents ('-x') errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we try to use 'analyze-migration.py -x' with python3, we have the following errors: Traceback (most recent call last): File "scripts/analyze-migration.py", line 593, in f.write(jsonenc.encode(dump.vmsd_desc)) TypeError: a bytes-like object is required, not 'str' Traceback (most recent call last): File "scripts/analyze-migration.py", line 601, in f.write(jsonenc.encode(dict)) TypeError: a bytes-like object is required, not 'str' This happens because the file 'f' is open in binary mode while jsonenc.encode() returns a string. The results are human-readable files, 'desc.json' and 'state.json', so there is no reason to use the binary mode. Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211015131645.501281-3-lvivier@redhat.com> Signed-off-by: Laurent Vivier --- scripts/analyze-migration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 9d239d309f..b82a1b0c58 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -588,7 +588,7 @@ if args.extract: dump.read(desc_only = True) print("desc.json") - f = open("desc.json", "wb") + f = open("desc.json", "w") f.truncate() f.write(jsonenc.encode(dump.vmsd_desc)) f.close() @@ -596,7 +596,7 @@ if args.extract: dump.read(write_memory = True) dict = dump.getDict() print("state.json") - f = open("state.json", "wb") + f = open("state.json", "w") f.truncate() f.write(jsonenc.encode(dict)) f.close() From 5afc8df46cdf1a10fc44d43208cf449357009d2a Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 22 Sep 2021 15:02:01 -0400 Subject: [PATCH 0758/1334] virtiofsd: xattr mapping add a new type "unsupported" Right now for xattr remapping, we support types of "prefix", "ok" or "bad". Type "bad" returns -EPERM on setxattr and hides xattr in listxattr. For getxattr, mapping code returns -EPERM but getxattr code converts it to -ENODATA. I need a new semantics where if an xattr is unsupported, then getxattr()/setxattr() return -ENOTSUP and listxattr() should hide the xattr. This is needed to simulate that security.selinux is not supported by virtiofs filesystem and in that case client falls back to some default label specified by policy. So add a new type "unsupported" which returns -ENOTSUP on getxattr() and setxattr() and hides xattrs in listxattr(). For example, one can use following mapping rule to not support security.selinux xattr and allow others. "-o xattrmap=/unsupported/all/security.selinux/security.selinux//ok/all///" Suggested-by: "Dr. David Alan Gilbert" Signed-off-by: Vivek Goyal Message-Id: Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Dr. David Alan Gilbert --- docs/tools/virtiofsd.rst | 6 ++++++ tools/virtiofsd/passthrough_ll.c | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst index b208f2a6f0..cc31402830 100644 --- a/docs/tools/virtiofsd.rst +++ b/docs/tools/virtiofsd.rst @@ -183,6 +183,12 @@ Using ':' as the separator a rule is of the form: 'ok' as either an explicit terminator or for special handling of certain patterns. +- 'unsupported' - If a client tries to use a name matching 'key' it's + denied using ENOTSUP; when the server passes an attribute + name matching 'prepend' it's hidden. In many ways it's use is very like + 'ok' as either an explicit terminator or for special handling of certain + patterns. + **key** is a string tested as a prefix on an attribute name originating on the client. It maybe empty in which case a 'client' rule will always match on client names. diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 38b2af8599..64b5b4fbb1 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -2465,6 +2465,11 @@ static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, * Automatically reversed on read */ #define XATTR_MAP_FLAG_PREFIX (1 << 2) +/* + * The attribute is unsupported; + * ENOTSUP on write, hidden on read. + */ +#define XATTR_MAP_FLAG_UNSUPPORTED (1 << 3) /* scopes */ /* Apply rule to get/set/remove */ @@ -2636,6 +2641,8 @@ static void parse_xattrmap(struct lo_data *lo) tmp_entry.flags |= XATTR_MAP_FLAG_OK; } else if (strstart(map, "bad", &map)) { tmp_entry.flags |= XATTR_MAP_FLAG_BAD; + } else if (strstart(map, "unsupported", &map)) { + tmp_entry.flags |= XATTR_MAP_FLAG_UNSUPPORTED; } else if (strstart(map, "map", &map)) { /* * map is sugar that adds a number of rules, and must be @@ -2646,8 +2653,8 @@ static void parse_xattrmap(struct lo_data *lo) } else { fuse_log(FUSE_LOG_ERR, "%s: Unexpected type;" - "Expecting 'prefix', 'ok', 'bad' or 'map' in rule %zu\n", - __func__, lo->xattr_map_nentries); + "Expecting 'prefix', 'ok', 'bad', 'unsupported' or 'map'" + " in rule %zu\n", __func__, lo->xattr_map_nentries); exit(1); } @@ -2749,6 +2756,9 @@ static int xattr_map_client(const struct lo_data *lo, const char *client_name, if (cur_entry->flags & XATTR_MAP_FLAG_BAD) { return -EPERM; } + if (cur_entry->flags & XATTR_MAP_FLAG_UNSUPPORTED) { + return -ENOTSUP; + } if (cur_entry->flags & XATTR_MAP_FLAG_OK) { /* Unmodified name */ return 0; @@ -2788,7 +2798,8 @@ static int xattr_map_server(const struct lo_data *lo, const char *server_name, if ((cur_entry->flags & XATTR_MAP_FLAG_SERVER) && (strstart(server_name, cur_entry->prepend, &end))) { - if (cur_entry->flags & XATTR_MAP_FLAG_BAD) { + if (cur_entry->flags & XATTR_MAP_FLAG_BAD || + cur_entry->flags & XATTR_MAP_FLAG_UNSUPPORTED) { return -ENODATA; } if (cur_entry->flags & XATTR_MAP_FLAG_OK) { From a88abc6f841ea7f0a9c57d69ccf133e2c7d12348 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Thu, 30 Sep 2021 11:30:27 -0400 Subject: [PATCH 0759/1334] virtiofsd: Remove unused virtio_fs_config definition "struct virtio_fs_config" definition seems to be unused in fuse_virtio.c. Remove it. Signed-off-by: Vivek Goyal Message-Id: <20210930153037.1194279-4-vgoyal@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Dr. David Alan Gilbert --- tools/virtiofsd/fuse_virtio.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index 8f4fd165b9..da7b6a76bf 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -82,12 +82,6 @@ struct fv_VuDev { struct fv_QueueInfo **qi; }; -/* From spec */ -struct virtio_fs_config { - char tag[36]; - uint32_t num_queues; -}; - /* Callback from libvhost-user */ static uint64_t fv_get_features(VuDev *dev) { From c68276556a1e6e035f9a27d0dbb2f87b349f3aea Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Thu, 30 Sep 2021 11:30:28 -0400 Subject: [PATCH 0760/1334] virtiofsd: Add a helper to send element on virtqueue We have open coded logic to take locks and push element on virtqueue at three places. Add a helper and use it everywhere. Code is easier to read and less number of lines of code. Signed-off-by: Vivek Goyal Message-Id: <20210930153037.1194279-5-vgoyal@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Dr. David Alan Gilbert --- tools/virtiofsd/fuse_virtio.c | 45 ++++++++++++++--------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index da7b6a76bf..fcf12db9cd 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -243,6 +243,21 @@ static void vu_dispatch_unlock(struct fv_VuDev *vud) assert(ret == 0); } +static void vq_send_element(struct fv_QueueInfo *qi, VuVirtqElement *elem, + ssize_t len) +{ + struct fuse_session *se = qi->virtio_dev->se; + VuDev *dev = &se->virtio_dev->dev; + VuVirtq *q = vu_get_queue(dev, qi->qidx); + + vu_dispatch_rdlock(qi->virtio_dev); + pthread_mutex_lock(&qi->vq_lock); + vu_queue_push(dev, q, elem, len); + vu_queue_notify(dev, q); + pthread_mutex_unlock(&qi->vq_lock); + vu_dispatch_unlock(qi->virtio_dev); +} + /* * Called back by ll whenever it wants to send a reply/message back * The 1st element of the iov starts with the fuse_out_header @@ -253,8 +268,6 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, { FVRequest *req = container_of(ch, FVRequest, ch); struct fv_QueueInfo *qi = ch->qi; - VuDev *dev = &se->virtio_dev->dev; - VuVirtq *q = vu_get_queue(dev, qi->qidx); VuVirtqElement *elem = &req->elem; int ret = 0; @@ -296,13 +309,7 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, copy_iov(iov, count, in_sg, in_num, tosend_len); - vu_dispatch_rdlock(qi->virtio_dev); - pthread_mutex_lock(&qi->vq_lock); - vu_queue_push(dev, q, elem, tosend_len); - vu_queue_notify(dev, q); - pthread_mutex_unlock(&qi->vq_lock); - vu_dispatch_unlock(qi->virtio_dev); - + vq_send_element(qi, elem, tosend_len); req->reply_sent = true; err: @@ -321,8 +328,6 @@ int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, { FVRequest *req = container_of(ch, FVRequest, ch); struct fv_QueueInfo *qi = ch->qi; - VuDev *dev = &se->virtio_dev->dev; - VuVirtq *q = vu_get_queue(dev, qi->qidx); VuVirtqElement *elem = &req->elem; int ret = 0; g_autofree struct iovec *in_sg_cpy = NULL; @@ -430,12 +435,7 @@ int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, out_sg->len = tosend_len; } - vu_dispatch_rdlock(qi->virtio_dev); - pthread_mutex_lock(&qi->vq_lock); - vu_queue_push(dev, q, elem, tosend_len); - vu_queue_notify(dev, q); - pthread_mutex_unlock(&qi->vq_lock); - vu_dispatch_unlock(qi->virtio_dev); + vq_send_element(qi, elem, tosend_len); req->reply_sent = true; return 0; } @@ -447,7 +447,6 @@ static void fv_queue_worker(gpointer data, gpointer user_data) { struct fv_QueueInfo *qi = user_data; struct fuse_session *se = qi->virtio_dev->se; - struct VuDev *dev = &qi->virtio_dev->dev; FVRequest *req = data; VuVirtqElement *elem = &req->elem; struct fuse_buf fbuf = {}; @@ -589,17 +588,9 @@ out: /* If the request has no reply, still recycle the virtqueue element */ if (!req->reply_sent) { - struct VuVirtq *q = vu_get_queue(dev, qi->qidx); - fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", __func__, elem->index); - - vu_dispatch_rdlock(qi->virtio_dev); - pthread_mutex_lock(&qi->vq_lock); - vu_queue_push(dev, q, elem, 0); - vu_queue_notify(dev, q); - pthread_mutex_unlock(&qi->vq_lock); - vu_dispatch_unlock(qi->virtio_dev); + vq_send_element(qi, elem, 0); } pthread_mutex_destroy(&req->ch.lock); From 50cf6d6cb7b6b0e43f626da2a65d7277add21bd9 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Thu, 30 Sep 2021 11:30:29 -0400 Subject: [PATCH 0761/1334] virtiofsd: Add a helper to stop all queues Use a helper to stop all the queues. Later in the patch series I am planning to use this helper at one more place later in the patch series. Signed-off-by: Vivek Goyal Message-Id: <20210930153037.1194279-6-vgoyal@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Dr. David Alan Gilbert --- tools/virtiofsd/fuse_virtio.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index fcf12db9cd..baead08b28 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -740,6 +740,18 @@ static void fv_queue_cleanup_thread(struct fv_VuDev *vud, int qidx) vud->qi[qidx] = NULL; } +static void stop_all_queues(struct fv_VuDev *vud) +{ + for (int i = 0; i < vud->nqueues; i++) { + if (!vud->qi[i]) { + continue; + } + + fuse_log(FUSE_LOG_INFO, "%s: Stopping queue %d thread\n", __func__, i); + fv_queue_cleanup_thread(vud, i); + } +} + /* Callback from libvhost-user on start or stop of a queue */ static void fv_queue_set_started(VuDev *dev, int qidx, bool started) { @@ -870,15 +882,7 @@ int virtio_loop(struct fuse_session *se) * Make sure all fv_queue_thread()s quit on exit, as we're about to * free virtio dev and fuse session, no one should access them anymore. */ - for (int i = 0; i < se->virtio_dev->nqueues; i++) { - if (!se->virtio_dev->qi[i]) { - continue; - } - - fuse_log(FUSE_LOG_INFO, "%s: Stopping queue %d thread\n", __func__, i); - fv_queue_cleanup_thread(se->virtio_dev, i); - } - + stop_all_queues(se->virtio_dev); fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__); return 0; From 555a76e5e5dc2cd3c84c5e1bc060be17d5b32584 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 14 Oct 2021 13:25:54 +0100 Subject: [PATCH 0762/1334] virtiofsd: Error on bad socket group name Make the '--socket-group=' option fail if the group name is unknown: ./tools/virtiofsd/virtiofsd .... --socket-group=zaphod vhost socket: unable to find group 'zaphod' Reported-by: Xiaoling Gao Signed-off-by: Dr. David Alan Gilbert Message-Id: <20211014122554.34599-1-dgilbert@redhat.com> Reviewed-by: Vivek Goyal Signed-off-by: Dr. David Alan Gilbert --- tools/virtiofsd/fuse_virtio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index baead08b28..60b96470c5 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -988,6 +988,13 @@ static int fv_create_listen_socket(struct fuse_session *se) "vhost socket failed to set group to %s (%d): %m\n", se->vu_socket_group, g->gr_gid); } + } else { + fuse_log(FUSE_LOG_ERR, + "vhost socket: unable to find group '%s'\n", + se->vu_socket_group); + close(listen_sock); + umask(old_umask); + return -1; } } umask(old_umask); From 669ced09b3b6070d478acce51810591b78ab0ccd Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Wed, 22 Sep 2021 15:13:31 +0200 Subject: [PATCH 0763/1334] 9pfs: fix wrong I/O block size in Rgetattr When client sent a 9p Tgetattr request then the wrong I/O block size value was returned by 9p server; instead of host file system's I/O block size it should rather return an I/O block size according to 9p session's 'msize' value, because the value returned to client should be an "optimum" block size for I/O (i.e. to maximize performance), it should not reflect the actual physical block size of the underlying storage media. The I/O block size of a host filesystem is typically 4k, so the value returned was far too low for good 9p I/O performance. This patch adds stat_to_iounit() with a similar approach as the existing get_iounit() function. Signed-off-by: Christian Schoenebeck Reviewed-by: Greg Kurz Message-Id: --- hw/9pfs/9p.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index c857b31321..708b030474 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1262,6 +1262,25 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path, #define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ +static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) +{ + int32_t iounit = 0; + V9fsState *s = pdu->s; + + /* + * iounit should be multiples of st_blksize (host filesystem block size) + * as well as less than (client msize - P9_IOHDRSZ) + */ + if (stbuf->st_blksize) { + iounit = stbuf->st_blksize; + iounit *= (s->msize - P9_IOHDRSZ) / stbuf->st_blksize; + } + if (!iounit) { + iounit = s->msize - P9_IOHDRSZ; + } + return iounit; +} + static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, V9fsStatDotl *v9lstat) { @@ -1273,7 +1292,7 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, v9lstat->st_gid = stbuf->st_gid; v9lstat->st_rdev = stbuf->st_rdev; v9lstat->st_size = stbuf->st_size; - v9lstat->st_blksize = stbuf->st_blksize; + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); v9lstat->st_blocks = stbuf->st_blocks; v9lstat->st_atime_sec = stbuf->st_atime; v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec; From b565bccb00afe8b73d529bbc3a38682996dac5c7 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Mon, 27 Sep 2021 17:45:00 +0200 Subject: [PATCH 0764/1334] 9pfs: deduplicate iounit code Remove redundant code that translates host fileystem's block size into 9p client (guest side) block size. Signed-off-by: Christian Schoenebeck Reviewed-by: Greg Kurz Message-Id: <129bb71d5119e61d335f1e3107e472e4beea223a.1632758315.git.qemu_oss@crudebyte.com> --- hw/9pfs/9p.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 708b030474..5c57344667 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1262,18 +1262,26 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path, #define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ -static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) +/** + * Convert host filesystem's block size into an appropriate block size for + * 9p client (guest OS side). The value returned suggests an "optimum" block + * size for 9p I/O, i.e. to maximize performance. + * + * @pdu: 9p client request + * @blksize: host filesystem's block size + */ +static int32_t blksize_to_iounit(const V9fsPDU *pdu, int32_t blksize) { int32_t iounit = 0; V9fsState *s = pdu->s; /* - * iounit should be multiples of st_blksize (host filesystem block size) + * iounit should be multiples of blksize (host filesystem block size) * as well as less than (client msize - P9_IOHDRSZ) */ - if (stbuf->st_blksize) { - iounit = stbuf->st_blksize; - iounit *= (s->msize - P9_IOHDRSZ) / stbuf->st_blksize; + if (blksize) { + iounit = blksize; + iounit *= (s->msize - P9_IOHDRSZ) / blksize; } if (!iounit) { iounit = s->msize - P9_IOHDRSZ; @@ -1281,6 +1289,11 @@ static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) return iounit; } +static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) +{ + return blksize_to_iounit(pdu, stbuf->st_blksize); +} + static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, V9fsStatDotl *v9lstat) { @@ -1899,23 +1912,9 @@ out_nofid: static int32_t coroutine_fn get_iounit(V9fsPDU *pdu, V9fsPath *path) { struct statfs stbuf; - int32_t iounit = 0; - V9fsState *s = pdu->s; + int err = v9fs_co_statfs(pdu, path, &stbuf); - /* - * iounit should be multiples of f_bsize (host filesystem block size - * and as well as less than (client msize - P9_IOHDRSZ)) - */ - if (!v9fs_co_statfs(pdu, path, &stbuf)) { - if (stbuf.f_bsize) { - iounit = stbuf.f_bsize; - iounit *= (s->msize - P9_IOHDRSZ) / stbuf.f_bsize; - } - } - if (!iounit) { - iounit = s->msize - P9_IOHDRSZ; - } - return iounit; + return blksize_to_iounit(pdu, (err >= 0) ? stbuf.f_bsize : 0); } static void coroutine_fn v9fs_open(void *opaque) From 04a7f9e55e0930b87805f7c97851eea4610e78fc Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Mon, 27 Sep 2021 17:50:36 +0200 Subject: [PATCH 0765/1334] 9pfs: simplify blksize_to_iounit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QEMU_ALIGN_DOWN() macro to reduce code and to make it more human readable. Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Christian Schoenebeck Reviewed-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé Message-Id: --- hw/9pfs/9p.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 5c57344667..e874899ef5 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1280,8 +1280,7 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, int32_t blksize) * as well as less than (client msize - P9_IOHDRSZ) */ if (blksize) { - iounit = blksize; - iounit *= (s->msize - P9_IOHDRSZ) / blksize; + iounit = QEMU_ALIGN_DOWN(s->msize - P9_IOHDRSZ, blksize); } if (!iounit) { iounit = s->msize - P9_IOHDRSZ; From 30e702abf6fa8a7f1e6ad11a75d6f3ab6fcb2155 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 1 Oct 2021 16:26:17 +0200 Subject: [PATCH 0766/1334] 9pfs: introduce P9Array Implements deep auto free of arrays while retaining common C-style squared bracket access. Main purpose of this API is to get rid of error prone individual array deallocation pathes in user code, i.e. turning something like this: void doSomething(size_t n) { Foo *foos = malloc(n * sizeof(Foo)); for (...) { foos[i].s = malloc(...); if (...) { goto out; } } out: if (...) { for (...) { /* deep deallocation */ free(foos[i].s); } /* array deallocation */ free(foos); } } into something more simple and safer like: void doSomething(size_t n) { P9ARRAY_REF(Foo) foos = NULL; P9ARRAY_NEW(Foo, foos, n); for (...) { foos[i].s = malloc(...); if (...) { return; /* array auto freed here */ } } /* array auto freed here */ } Unlike GArray, P9Array does not require special macros, function calls or struct member dereferencing to access the individual array elements: C-array = P9Array: vs. GArray: for (...) { | for (...) { ... = arr[i].m; | ... = g_array_index(arr, Foo, i).m; arr[i].m = ... ; | g_array_index(arr, Foo, i).m = ... ; } | } So existing C-style array code can be retained with only very little changes; basically limited to replacing array allocation call and of course removing individual array deallocation pathes. In this initial version P9Array only supports the concept of unique pointers, i.e. it does not support reference counting. The array (and all dynamically allocated memory of individual array elements) is auto freed once execution leaves the scope of the reference variable (unique pointer) associated with the array. Internally a flex array struct is used in combination with macros spanned over a continuous memory space for both the array's meta data (private) and the actual C-array user data (public): struct P9Array##scalar_type { size_t len; /* private, hidden from user code */ scalar_type first[]; /* public, directly exposed to user code */ }; Which has the advantage that the compiler automatically takes care about correct padding, alignment and overall size for all scalar data types on all systems and that the user space exposed pointer can directly be translated back and forth between user space C-array pointer and internal P9Array struct whenever needed, in a type-safe manner. This header file is released under MIT license, to allow this file being used in other C-projects as well. The common QEMU license GPL2+ might have construed a conflict for other projects. Signed-off-by: Christian Schoenebeck Message-Id: --- fsdev/p9array.h | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 fsdev/p9array.h diff --git a/fsdev/p9array.h b/fsdev/p9array.h new file mode 100644 index 0000000000..fff946a3d7 --- /dev/null +++ b/fsdev/p9array.h @@ -0,0 +1,154 @@ +/* + * P9Array - deep auto free C-array + * + * Copyright (c) 2021 Crudebyte + * + * Authors: + * Christian Schoenebeck + * + * 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. + */ +#ifndef QEMU_P9ARRAY_H +#define QEMU_P9ARRAY_H + +/** + * P9Array provides a mechanism to access arrays in common C-style (e.g. by + * square bracket [] operator) in conjunction with reference variables that + * perform deep auto free of the array when leaving the scope of the auto + * reference variable. That means not only is the array itself automatically + * freed, but also memory dynamically allocated by the individual array + * elements. + * + * Example: + * + * Consider the following user struct @c Foo which shall be used as scalar + * (element) type of an array: + * @code + * typedef struct Foo { + * int i; + * char *s; + * } Foo; + * @endcode + * and assume it has the following function to free memory allocated by @c Foo + * instances: + * @code + * void free_foo(Foo *foo) { + * free(foo->s); + * } + * @endcode + * Add the following to a shared header file: + * @code + * P9ARRAY_DECLARE_TYPE(Foo); + * @endcode + * and the following to a C unit file: + * @code + * P9ARRAY_DEFINE_TYPE(Foo, free_foo); + * @endcode + * Finally the array may then be used like this: + * @code + * void doSomething(size_t n) { + * P9ARRAY_REF(Foo) foos = NULL; + * P9ARRAY_NEW(Foo, foos, n); + * for (size_t i = 0; i < n; ++i) { + * foos[i].i = i; + * foos[i].s = calloc(4096, 1); + * snprintf(foos[i].s, 4096, "foo %d", i); + * if (...) { + * return; // array auto freed here + * } + * } + * // array auto freed here + * } + * @endcode + */ + +/** + * Declares an array type for the passed @a scalar_type. + * + * This is typically used from a shared header file. + * + * @param scalar_type - type of the individual array elements + */ +#define P9ARRAY_DECLARE_TYPE(scalar_type) \ + typedef struct P9Array##scalar_type { \ + size_t len; \ + scalar_type first[]; \ + } P9Array##scalar_type; \ + \ + void p9array_new_##scalar_type(scalar_type **auto_var, size_t len); \ + void p9array_auto_free_##scalar_type(scalar_type **auto_var); \ + +/** + * Defines an array type for the passed @a scalar_type and appropriate + * @a scalar_cleanup_func. + * + * This is typically used from a C unit file. + * + * @param scalar_type - type of the individual array elements + * @param scalar_cleanup_func - appropriate function to free memory dynamically + * allocated by individual array elements before + */ +#define P9ARRAY_DEFINE_TYPE(scalar_type, scalar_cleanup_func) \ + void p9array_new_##scalar_type(scalar_type **auto_var, size_t len) \ + { \ + p9array_auto_free_##scalar_type(auto_var); \ + P9Array##scalar_type *arr = g_malloc0(sizeof(P9Array##scalar_type) + \ + len * sizeof(scalar_type)); \ + arr->len = len; \ + *auto_var = &arr->first[0]; \ + } \ + \ + void p9array_auto_free_##scalar_type(scalar_type **auto_var) \ + { \ + scalar_type *first = (*auto_var); \ + if (!first) { \ + return; \ + } \ + P9Array##scalar_type *arr = (P9Array##scalar_type *) ( \ + ((char *)first) - offsetof(P9Array##scalar_type, first) \ + ); \ + for (size_t i = 0; i < arr->len; ++i) { \ + scalar_cleanup_func(&arr->first[i]); \ + } \ + g_free(arr); \ + } \ + +/** + * Used to declare a reference variable (unique pointer) for an array. After + * leaving the scope of the reference variable, the associated array is + * automatically freed. + * + * @param scalar_type - type of the individual array elements + */ +#define P9ARRAY_REF(scalar_type) \ + __attribute((__cleanup__(p9array_auto_free_##scalar_type))) scalar_type* + +/** + * Allocates a new array of passed @a scalar_type with @a len number of array + * elements and assigns the created array to the reference variable + * @a auto_var. + * + * @param scalar_type - type of the individual array elements + * @param auto_var - destination reference variable + * @param len - amount of array elements to be allocated immediately + */ +#define P9ARRAY_NEW(scalar_type, auto_var, len) \ + p9array_new_##scalar_type((&auto_var), len) + +#endif /* QEMU_P9ARRAY_H */ From c0451f0bc4210d262268ff51c053a9277f20f862 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 1 Oct 2021 16:27:13 +0200 Subject: [PATCH 0767/1334] fsdev/p9array.h: check scalar type in P9ARRAY_NEW() Make sure at compile time that the scalar type of the array requested to be created via P9ARRAY_NEW() matches the scalar type of the passed auto reference variable (unique pointer). Suggested-by: Richard Henderson Signed-off-by: Christian Schoenebeck Message-Id: --- fsdev/p9array.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fsdev/p9array.h b/fsdev/p9array.h index fff946a3d7..6aa25327ca 100644 --- a/fsdev/p9array.h +++ b/fsdev/p9array.h @@ -27,6 +27,8 @@ #ifndef QEMU_P9ARRAY_H #define QEMU_P9ARRAY_H +#include "qemu/compiler.h" + /** * P9Array provides a mechanism to access arrays in common C-style (e.g. by * square bracket [] operator) in conjunction with reference variables that @@ -149,6 +151,10 @@ * @param len - amount of array elements to be allocated immediately */ #define P9ARRAY_NEW(scalar_type, auto_var, len) \ + QEMU_BUILD_BUG_MSG( \ + !__builtin_types_compatible_p(scalar_type, typeof(*auto_var)), \ + "P9Array scalar type mismatch" \ + ); \ p9array_new_##scalar_type((&auto_var), len) #endif /* QEMU_P9ARRAY_H */ From 42bdeb04b6a4bf3e54f4d7f87193803268ba8255 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 1 Oct 2021 16:27:29 +0200 Subject: [PATCH 0768/1334] 9pfs: make V9fsString usable via P9Array API Signed-off-by: Christian Schoenebeck Message-Id: --- fsdev/9p-marshal.c | 2 ++ fsdev/9p-marshal.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/fsdev/9p-marshal.c b/fsdev/9p-marshal.c index a01bba6908..51881fe220 100644 --- a/fsdev/9p-marshal.c +++ b/fsdev/9p-marshal.c @@ -18,6 +18,8 @@ #include "9p-marshal.h" +P9ARRAY_DEFINE_TYPE(V9fsString, v9fs_string_free); + void v9fs_string_free(V9fsString *str) { g_free(str->data); diff --git a/fsdev/9p-marshal.h b/fsdev/9p-marshal.h index ceaf2f521e..f1abbe151c 100644 --- a/fsdev/9p-marshal.h +++ b/fsdev/9p-marshal.h @@ -1,10 +1,13 @@ #ifndef QEMU_9P_MARSHAL_H #define QEMU_9P_MARSHAL_H +#include "p9array.h" + typedef struct V9fsString { uint16_t size; char *data; } V9fsString; +P9ARRAY_DECLARE_TYPE(V9fsString); typedef struct V9fsQID { uint8_t type; From cc82fde9c7b4b598907914896ee6942fa866258c Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 1 Oct 2021 16:27:46 +0200 Subject: [PATCH 0769/1334] 9pfs: make V9fsPath usable via P9Array API Signed-off-by: Christian Schoenebeck Message-Id: <79a0ddf8375f6c95f0565ef155a1bf1e9387664f.1633097129.git.qemu_oss@crudebyte.com> --- fsdev/file-op-9p.h | 2 ++ hw/9pfs/9p.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index 42f677cf38..8fd89f0447 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -18,6 +18,7 @@ #include #include #include "qemu-fsdev-throttle.h" +#include "p9array.h" #define SM_LOCAL_MODE_BITS 0600 #define SM_LOCAL_DIR_MODE_BITS 0700 @@ -105,6 +106,7 @@ struct V9fsPath { uint16_t size; char *data; }; +P9ARRAY_DECLARE_TYPE(V9fsPath); typedef union V9fsFidOpenState V9fsFidOpenState; diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index e874899ef5..15bb16f466 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -50,6 +50,8 @@ enum { Oappend = 0x80, }; +P9ARRAY_DEFINE_TYPE(V9fsPath, v9fs_path_free); + static ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) { ssize_t ret; From 7e985780aaab93d2c5be9b62d8d386568dfb071e Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 1 Oct 2021 16:27:59 +0200 Subject: [PATCH 0770/1334] 9pfs: use P9Array in v9fs_walk() Signed-off-by: Christian Schoenebeck Message-Id: <90c65d1c1ca11c1b434bb981b1fc7966f7711c8f.1633097129.git.qemu_oss@crudebyte.com> --- hw/9pfs/9p.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 15bb16f466..15b3f4d385 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1738,13 +1738,14 @@ static void coroutine_fn v9fs_walk(void *opaque) int name_idx; g_autofree V9fsQID *qids = NULL; int i, err = 0; - V9fsPath dpath, path, *pathes = NULL; + V9fsPath dpath, path; + P9ARRAY_REF(V9fsPath) pathes = NULL; uint16_t nwnames; struct stat stbuf, fidst; g_autofree struct stat *stbufs = NULL; size_t offset = 7; int32_t fid, newfid; - V9fsString *wnames = NULL; + P9ARRAY_REF(V9fsString) wnames = NULL; V9fsFidState *fidp; V9fsFidState *newfidp = NULL; V9fsPDU *pdu = opaque; @@ -1765,10 +1766,10 @@ static void coroutine_fn v9fs_walk(void *opaque) goto out_nofid; } if (nwnames) { - wnames = g_new0(V9fsString, nwnames); + P9ARRAY_NEW(V9fsString, wnames, nwnames); qids = g_new0(V9fsQID, nwnames); stbufs = g_new0(struct stat, nwnames); - pathes = g_new0(V9fsPath, nwnames); + P9ARRAY_NEW(V9fsPath, pathes, nwnames); for (i = 0; i < nwnames; i++) { err = pdu_unmarshal(pdu, offset, "s", &wnames[i]); if (err < 0) { @@ -1900,14 +1901,6 @@ out: v9fs_path_free(&path); out_nofid: pdu_complete(pdu, err); - if (nwnames && nwnames <= P9_MAXWELEM) { - for (name_idx = 0; name_idx < nwnames; name_idx++) { - v9fs_string_free(&wnames[name_idx]); - v9fs_path_free(&pathes[name_idx]); - } - g_free(wnames); - g_free(pathes); - } } static int32_t coroutine_fn get_iounit(V9fsPDU *pdu, V9fsPath *path) From ea29331ba6c0e622e63424cc8495177ed7655a77 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Oct 2021 20:02:31 +0200 Subject: [PATCH 0771/1334] qapi: Improve input_type_enum()'s error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error message claims the parameter is invalid: $ qemu-system-x86_64 -object qom-type=nonexistent qemu-system-x86_64: -object qom-type=nonexistent: Invalid parameter 'nonexistent' What's wrong is actually the *value* 'nonexistent'. Improve the message to qemu-system-x86_64: -object qom-type=nonexistent: Parameter 'qom-type' does not accept value 'nonexistent' Fixes: https://gitlab.com/qemu-project/qemu/-/issues/608 Signed-off-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211020180231.434071-1-armbru@redhat.com> Reviewed-by: Kevin Wolf --- qapi/qapi-visit-core.c | 3 ++- tests/qemu-iotests/049.out | 6 +++--- tests/qemu-iotests/206.out | 2 +- tests/qemu-iotests/237.out | 6 +++--- tests/qemu-iotests/245 | 2 +- tests/qemu-iotests/287 | 2 +- tests/qemu-iotests/308 | 2 +- tests/unit/check-qom-proplist.c | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index a641adec51..7310f0a0ca 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -392,7 +392,8 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, value = qapi_enum_parse(lookup, enum_str, -1, NULL); if (value < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, enum_str); + error_setg(errp, "Parameter '%s' does not accept value '%s'", + name ? name : "null", enum_str); g_free(enum_str); return false; } diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 01f7b1f240..8719c91b48 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -174,11 +174,11 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.42 lazy_refcounts=off refcount_bits=16 -qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42' +qemu-img: TEST_DIR/t.qcow2: Parameter 'version' does not accept value '0.42' qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=foobar lazy_refcounts=off refcount_bits=16 -qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar' +qemu-img: TEST_DIR/t.qcow2: Parameter 'version' does not accept value 'foobar' == Check preallocation option == @@ -190,7 +190,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off prea qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=1234 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 -qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234' +qemu-img: TEST_DIR/t.qcow2: Parameter 'preallocation' does not accept value '1234' == Check encryption option == diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out index b68c443867..3593e8e9c2 100644 --- a/tests/qemu-iotests/206.out +++ b/tests/qemu-iotests/206.out @@ -192,7 +192,7 @@ Job failed: Could not resize image: Failed to grow the L1 table: File too large === Invalid version === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}} -{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}} +{"error": {"class": "GenericError", "desc": "Parameter 'version' does not accept value 'v1'"}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}} {"return": {}} diff --git a/tests/qemu-iotests/237.out b/tests/qemu-iotests/237.out index aa94986803..2f09ff5512 100644 --- a/tests/qemu-iotests/237.out +++ b/tests/qemu-iotests/237.out @@ -116,13 +116,13 @@ Job failed: Cannot find device='this doesn't exist' nor node-name='this doesn't == Invalid adapter types == {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}} -{"error": {"class": "GenericError", "desc": "Invalid parameter 'foo'"}} +{"error": {"class": "GenericError", "desc": "Parameter 'adapter-type' does not accept value 'foo'"}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}} -{"error": {"class": "GenericError", "desc": "Invalid parameter 'IDE'"}} +{"error": {"class": "GenericError", "desc": "Parameter 'adapter-type' does not accept value 'IDE'"}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}} -{"error": {"class": "GenericError", "desc": "Invalid parameter 'legacyesx'"}} +{"error": {"class": "GenericError", "desc": "Parameter 'adapter-type' does not accept value 'legacyesx'"}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}} {"error": {"class": "GenericError", "desc": "Invalid parameter type for 'options.adapter-type', expected: string"}} diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index 9b12b42eed..24ac43f70e 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -149,7 +149,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(opts, {'node-name': ''}, "Failed to find node with node-name=''") self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'options[0].node-name', expected: string") self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'") - self.reopen(opts, {'driver': ''}, "Invalid parameter ''") + self.reopen(opts, {'driver': ''}, "Parameter 'driver' does not accept value ''") self.reopen(opts, {'driver': None}, "Invalid parameter type for 'options[0].driver', expected: string") self.reopen(opts, {'file': 'not-found'}, "Cannot find device='' nor node-name='not-found'") self.reopen(opts, {'file': ''}, "Cannot find device='' nor node-name=''") diff --git a/tests/qemu-iotests/287 b/tests/qemu-iotests/287 index 22ce9ff0e4..2d5334e8bf 100755 --- a/tests/qemu-iotests/287 +++ b/tests/qemu-iotests/287 @@ -53,7 +53,7 @@ CLUSTER_SIZE=65536 # Check if we can run this test. output=$(_make_test_img -o 'compression_type=zstd' 64M; _cleanup_test_img) -if echo "$output" | grep -q "Invalid parameter 'zstd'"; then +if echo "$output" | grep -q "Parameter 'compression-type' does not accept value 'zstd'"; then _notrun "ZSTD is disabled" fi diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index 6b386bd523..2e3f8f4282 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -148,7 +148,7 @@ rmdir "$EXT_MP" 2>/dev/null rm -f "$EXT_MP" output=$(fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error) -if echo "$output" | grep -q "Invalid parameter 'fuse'"; then +if echo "$output" | grep -q "Parameter 'type' does not accept value 'fuse'"; then _notrun 'No FUSE support' fi diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index 48503e0dff..ed341088d3 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -488,7 +488,7 @@ static void test_dummy_badenum(void) g_assert(dobj == NULL); g_assert(err != NULL); g_assert_cmpstr(error_get_pretty(err), ==, - "Invalid parameter 'yeti'"); + "Parameter 'av' does not accept value 'yeti'"); g_assert(object_resolve_path_component(parent, "dummy0") == NULL); From 75ecee7262548d21a9e20c12f0b3b12f8a51d5c6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 25 Oct 2021 06:24:01 +0200 Subject: [PATCH 0772/1334] qapi: Enable enum member introspection to show more than name The next commit will add feature flags to enum members. There's a problem, though: query-qmp-schema shows an enum type's members as an array of member names (SchemaInfoEnum member @values). If it showed an array of objects with a name member, we could simply add more members to these objects. Since it's just strings, we can't. I can see three ways to correct this design mistake: 1. Do it the way we should have done it, plus compatibility goo. We want a ['SchemaInfoEnumMember'] member in SchemaInfoEnum. Since changing @values would be a compatibility break, add a new member @members instead. @values is now redundant. In my testing, output of qemu-system-x86_64's query-qmp-schema grows by 11% (18.5KiB). We can deprecate @values now and drop it later. This will break outmoded clients. Well-behaved clients such as libvirt are expected to break cleanly. 2. Like 1, but omit "boring" elements of @member, and empty @member. @values does not become redundant. @members augments it. Somewhat cumbersome, but output of query-qmp-schema grows only as we make enum members non-boring. There is nothing to deprecate here. 3. Versioned query-qmp-schema. query-qmp-schema provides either @values or @members. The QMP client can select which version it wants. There is no redundant output. We can deprecate old versions and eventually drop them. This will break outmoded clients. Breaking cleanly is easier than for 1. While 1 and 2 operate within the common rules for compatible evolution apply (section "Compatibility considerations" in docs/devel/qapi-code-gen.rst), 3 bypasses them. Attractive when operating within the rules is just too awkward. Not the case here. This commit implements 1. Libvirt developers prefer it. Deprecate @values in favour of @members. Since query-qmp-schema compatibility is pretty fundamental for management applications, an extended grace period is advised. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Tested-by: Peter Krempa Acked-by: Peter Krempa Message-Id: <20211025042405.3762351-2-armbru@redhat.com> Reviewed-by: John Snow --- docs/about/deprecated.rst | 6 ++++++ docs/devel/qapi-code-gen.rst | 15 +++++++++++---- qapi/introspect.json | 25 +++++++++++++++++++++++-- scripts/qapi/introspect.py | 18 ++++++++++++++---- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 0bed6ecb1d..be19317470 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -228,6 +228,12 @@ Use the more generic commands ``block-export-add`` and ``block-export-del`` instead. As part of this deprecation, where ``nbd-server-add`` used a single ``bitmap``, the new ``block-export-add`` uses a list of ``bitmaps``. +``query-qmp-schema`` return value member ``values`` (since 6.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Member ``values`` in return value elements with meta-type ``enum`` is +deprecated. Use ``members`` instead. + System accelerators ------------------- diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index b2569de486..d267889d2c 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1231,14 +1231,21 @@ Example: the SchemaInfo for ['str'] :: "element-type": "str" } The SchemaInfo for an enumeration type has meta-type "enum" and -variant member "values". The values are listed in no particular -order; clients must search the entire enum when learning whether a -particular value is supported. +variant member "members". + +"members" is a JSON array describing the enumeration values. Each +element is a JSON object with member "name" (the member's name). The +"members" array is in no particular order; clients must search the +entire array when learning whether a particular value is supported. Example: the SchemaInfo for MyEnum from section `Enumeration types`_ :: { "name": "MyEnum", "meta-type": "enum", - "values": [ "value1", "value2", "value3" ] } + "members": [ + { "name": "value1" }, + { "name": "value2" }, + { "name": "value3" } + ] } The SchemaInfo for a built-in type has the same name as the type in the QAPI schema (see section `Built-in Types`_), with one exception diff --git a/qapi/introspect.json b/qapi/introspect.json index 39bd303778..9683e884f8 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -142,14 +142,35 @@ # # Additional SchemaInfo members for meta-type 'enum'. # -# @values: the enumeration type's values, in no particular order. +# @members: the enum type's members, in no particular order +# (since 6.2). +# +# @values: the enumeration type's member names, in no particular order. +# Redundant with @members. Just for backward compatibility. +# +# Features: +# @deprecated: Member @values is deprecated. Use @members instead. # # Values of this type are JSON string on the wire. # # Since: 2.5 ## { 'struct': 'SchemaInfoEnum', - 'data': { 'values': ['str'] } } + 'data': { 'members': [ 'SchemaInfoEnumMember' ], + 'values': { 'type': [ 'str' ], + 'features': [ 'deprecated' ] } } } + +## +# @SchemaInfoEnumMember: +# +# An object member. +# +# @name: the member's name, as defined in the QAPI schema. +# +# Since: 6.2 +## +{ 'struct': 'SchemaInfoEnumMember', + 'data': { 'name': 'str' } } ## # @SchemaInfoArray: diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 4c079ee627..6334546363 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -68,6 +68,7 @@ JSONValue = Union[_Value, 'Annotated[_Value]'] # TypedDict constructs, so they are broadly typed here as simple # Python Dicts. SchemaInfo = Dict[str, object] +SchemaInfoEnumMember = Dict[str, object] SchemaInfoObject = Dict[str, object] SchemaInfoObjectVariant = Dict[str, object] SchemaInfoObjectMember = Dict[str, object] @@ -274,8 +275,16 @@ const QLitObject %(c_name)s = %(c_string)s; obj['features'] = self._gen_features(features) self._trees.append(Annotated(obj, ifcond, comment)) - def _gen_member(self, member: QAPISchemaObjectTypeMember - ) -> Annotated[SchemaInfoObjectMember]: + @staticmethod + def _gen_enum_member(member: QAPISchemaEnumMember + ) -> Annotated[SchemaInfoEnumMember]: + obj: SchemaInfoEnumMember = { + 'name': member.name, + } + return Annotated(obj, member.ifcond) + + def _gen_object_member(self, member: QAPISchemaObjectTypeMember + ) -> Annotated[SchemaInfoObjectMember]: obj: SchemaInfoObjectMember = { 'name': member.name, 'type': self._use_type(member.type) @@ -305,7 +314,8 @@ const QLitObject %(c_name)s = %(c_string)s; prefix: Optional[str]) -> None: self._gen_tree( name, 'enum', - {'values': [Annotated(m.name, m.ifcond) for m in members]}, + {'members': [self._gen_enum_member(m) for m in members], + 'values': [Annotated(m.name, m.ifcond) for m in members]}, ifcond, features ) @@ -322,7 +332,7 @@ const QLitObject %(c_name)s = %(c_string)s; members: List[QAPISchemaObjectTypeMember], variants: Optional[QAPISchemaVariants]) -> None: obj: SchemaInfoObject = { - 'members': [self._gen_member(m) for m in members] + 'members': [self._gen_object_member(m) for m in members] } if variants: obj['tag'] = variants.tag_member.name From b6c18755e41f7b40aad4c2c8188fb1719535699d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 25 Oct 2021 06:24:02 +0200 Subject: [PATCH 0773/1334] qapi: Add feature flags to enum members This is quite similar to commit 84ab008687 "qapi: Add feature flags to struct members", only for enums instead of structs. Special feature flag 'deprecated' is silently ignored there. This is okay only because it will be implemented shortly. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20211025042405.3762351-3-armbru@redhat.com> Reviewed-by: John Snow --- docs/devel/qapi-code-gen.rst | 16 +++++++++----- qapi/compat.json | 2 ++ qapi/introspect.json | 5 ++++- scripts/qapi/expr.py | 3 ++- scripts/qapi/introspect.py | 5 +++-- scripts/qapi/schema.py | 22 +++++++++++++++++-- tests/qapi-schema/doc-good.json | 5 ++++- tests/qapi-schema/doc-good.out | 3 +++ tests/qapi-schema/doc-good.txt | 3 +++ .../qapi-schema/enum-dict-member-unknown.err | 2 +- tests/qapi-schema/qapi-schema-test.json | 3 ++- tests/qapi-schema/qapi-schema-test.out | 1 + tests/qapi-schema/test-qapi.py | 1 + 13 files changed, 57 insertions(+), 14 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index d267889d2c..4071c9074a 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -200,7 +200,9 @@ Syntax:: '*if': COND, '*features': FEATURES } ENUM-VALUE = STRING - | { 'name': STRING, '*if': COND } + | { 'name': STRING, + '*if': COND, + '*features': FEATURES } Member 'enum' names the enum type. @@ -706,8 +708,10 @@ QEMU shows a certain behaviour. Special features ~~~~~~~~~~~~~~~~ -Feature "deprecated" marks a command, event, or struct member as -deprecated. It is not supported elsewhere so far. +Feature "deprecated" marks a command, event, enum value, or struct +member as deprecated. It is not supported elsewhere so far. +Interfaces so marked may be withdrawn in future releases in accordance +with QEMU's deprecation policy. Naming rules and reserved names @@ -1157,7 +1161,8 @@ and "variants". "members" is a JSON array describing the object's common members, if any. Each element is a JSON object with members "name" (the member's -name), "type" (the name of its type), and optionally "default". The +name), "type" (the name of its type), "features" (a JSON array of +feature strings), and "default". The latter two are optional. The member is optional if "default" is present. Currently, "default" can only have value null. Other values are reserved for future extensions. The "members" array is in no particular order; clients @@ -1234,7 +1239,8 @@ The SchemaInfo for an enumeration type has meta-type "enum" and variant member "members". "members" is a JSON array describing the enumeration values. Each -element is a JSON object with member "name" (the member's name). The +element is a JSON object with member "name" (the member's name), and +optionally "features" (a JSON array of feature strings). The "members" array is in no particular order; clients must search the entire array when learning whether a particular value is supported. diff --git a/qapi/compat.json b/qapi/compat.json index ae3afc22df..1d2b76f00c 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -42,6 +42,8 @@ # with feature 'deprecated'. We may want to extend it to cover # semantic aspects, CLI, and experimental features. # +# Limitation: not implemented for deprecated enumeration values. +# # @deprecated-input: how to handle deprecated input (default 'accept') # @deprecated-output: how to handle deprecated output (default 'accept') # diff --git a/qapi/introspect.json b/qapi/introspect.json index 9683e884f8..183148b2e9 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -167,10 +167,13 @@ # # @name: the member's name, as defined in the QAPI schema. # +# @features: names of features associated with the member, in no +# particular order. +# # Since: 6.2 ## { 'struct': 'SchemaInfoEnumMember', - 'data': { 'name': 'str' } } + 'data': { 'name': 'str', '*features': [ 'str' ] } } ## # @SchemaInfoArray: diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index 819ea6ad97..3cb389e875 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -472,7 +472,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None: for m in members] for member in members: source = "'data' member" - check_keys(member, info, source, ['name'], ['if']) + check_keys(member, info, source, ['name'], ['if', 'features']) member_name = member['name'] check_name_is_str(member_name, info, source) source = "%s '%s'" % (source, member_name) @@ -483,6 +483,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None: permit_upper=permissive, permit_underscore=permissive) check_if(member, info, source) + check_features(member.get('features'), info) def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None: diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 6334546363..67c7d89aae 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -275,12 +275,13 @@ const QLitObject %(c_name)s = %(c_string)s; obj['features'] = self._gen_features(features) self._trees.append(Annotated(obj, ifcond, comment)) - @staticmethod - def _gen_enum_member(member: QAPISchemaEnumMember + def _gen_enum_member(self, member: QAPISchemaEnumMember ) -> Annotated[SchemaInfoEnumMember]: obj: SchemaInfoEnumMember = { 'name': member.name, } + if member.features: + obj['features'] = self._gen_features(member.features) return Annotated(obj, member.ifcond) def _gen_object_member(self, member: QAPISchemaObjectTypeMember diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 004d7095ff..6d5f46509a 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -708,6 +708,19 @@ class QAPISchemaMember: class QAPISchemaEnumMember(QAPISchemaMember): role = 'value' + def __init__(self, name, info, ifcond=None, features=None): + super().__init__(name, info, ifcond) + for f in features or []: + assert isinstance(f, QAPISchemaFeature) + f.set_defined_in(name) + self.features = features or [] + + def connect_doc(self, doc): + super().connect_doc(doc) + if doc: + for f in self.features: + doc.connect_feature(f) + class QAPISchemaFeature(QAPISchemaMember): role = 'feature' @@ -980,9 +993,14 @@ class QAPISchema: QAPISchemaIfCond(f.get('if'))) for f in features] + def _make_enum_member(self, name, ifcond, features, info): + return QAPISchemaEnumMember(name, info, + QAPISchemaIfCond(ifcond), + self._make_features(features, info)) + def _make_enum_members(self, values, info): - return [QAPISchemaEnumMember(v['name'], info, - QAPISchemaIfCond(v.get('if'))) + return [self._make_enum_member(v['name'], v.get('if'), + v.get('features'), info) for v in values] def _make_array_type(self, element_type, info): diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 86dc25d2bd..74745fb405 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -58,11 +58,14 @@ # # Features: # @enum-feat: Also _one_ {and only} +# @enum-member-feat: a member feature # # @two is undocumented ## { 'enum': 'Enum', - 'data': [ { 'name': 'one', 'if': 'IFONE' }, 'two' ], + 'data': [ { 'name': 'one', 'if': 'IFONE', + 'features': [ 'enum-member-feat' ] }, + 'two' ], 'features': [ 'enum-feat' ], 'if': 'IFCOND' } diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 5a324e2627..9dd65b9d92 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -13,6 +13,7 @@ module doc-good.json enum Enum member one if IFONE + feature enum-member-feat member two if IFCOND feature enum-feat @@ -108,6 +109,8 @@ The _one_ {and only} feature=enum-feat Also _one_ {and only} + feature=enum-member-feat +a member feature section=None @two is undocumented doc symbol=Base diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 701402ee5e..b3b76bd43f 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -56,6 +56,9 @@ Features "enum-feat" Also _one_ {and only} +"enum-member-feat" + a member feature + "two" is undocumented diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err index f8617ea179..235cde0c49 100644 --- a/tests/qapi-schema/enum-dict-member-unknown.err +++ b/tests/qapi-schema/enum-dict-member-unknown.err @@ -1,3 +1,3 @@ enum-dict-member-unknown.json: In enum 'MyEnum': enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key' -Valid keys are 'if', 'name'. +Valid keys are 'features', 'if', 'name'. diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 2ec50109cb..b677ab861d 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -301,7 +301,8 @@ 'TEST_IF_COND_2'] } } ] } { 'enum': 'FeatureEnum1', - 'data': [ 'eins', 'zwei', 'drei' ], + 'data': [ 'eins', 'zwei', + { 'name': 'drei', 'features': [ 'deprecated' ] } ], 'features': [ 'feature1' ] } { 'union': 'FeatureUnion1', diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 9337adc9ea..16846dbeb8 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -341,6 +341,7 @@ enum FeatureEnum1 member eins member zwei member drei + feature deprecated feature feature1 object q_obj_FeatureUnion1-base member tag: FeatureEnum1 optional=False diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index c717a7a90b..2160cef082 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -37,6 +37,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): for m in members: print(' member %s' % m.name) self._print_if(m.ifcond, indent=8) + self._print_features(m.features, indent=8) self._print_if(ifcond) self._print_features(features) From ed29bb28f8b0b17e965efcc2535fc32e101e3ceb Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 25 Oct 2021 06:24:03 +0200 Subject: [PATCH 0774/1334] qapi: Move compat policy from QObject to generic visitor The next commit needs to access compat policy from the generic visitor core. Move it there from qobject input and output visitor. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20211025042405.3762351-4-armbru@redhat.com> --- include/qapi/qobject-input-visitor.h | 4 ---- include/qapi/qobject-output-visitor.h | 4 ---- include/qapi/visitor-impl.h | 3 +++ include/qapi/visitor.h | 9 +++++++++ qapi/qapi-visit-core.c | 9 +++++++++ qapi/qmp-dispatch.c | 4 ++-- qapi/qobject-input-visitor.c | 14 +------------- qapi/qobject-output-visitor.c | 14 +------------- 8 files changed, 25 insertions(+), 36 deletions(-) diff --git a/include/qapi/qobject-input-visitor.h b/include/qapi/qobject-input-visitor.h index 8d69388810..95985e25e5 100644 --- a/include/qapi/qobject-input-visitor.h +++ b/include/qapi/qobject-input-visitor.h @@ -15,7 +15,6 @@ #ifndef QOBJECT_INPUT_VISITOR_H #define QOBJECT_INPUT_VISITOR_H -#include "qapi/qapi-types-compat.h" #include "qapi/visitor.h" typedef struct QObjectInputVisitor QObjectInputVisitor; @@ -59,9 +58,6 @@ typedef struct QObjectInputVisitor QObjectInputVisitor; */ Visitor *qobject_input_visitor_new(QObject *obj); -void qobject_input_visitor_set_policy(Visitor *v, - CompatPolicyInput deprecated); - /* * Create a QObject input visitor for @obj for use with keyval_parse() * diff --git a/include/qapi/qobject-output-visitor.h b/include/qapi/qobject-output-visitor.h index f2a2f92a00..2b1726baf5 100644 --- a/include/qapi/qobject-output-visitor.h +++ b/include/qapi/qobject-output-visitor.h @@ -15,7 +15,6 @@ #define QOBJECT_OUTPUT_VISITOR_H #include "qapi/visitor.h" -#include "qapi/qapi-types-compat.h" typedef struct QObjectOutputVisitor QObjectOutputVisitor; @@ -54,7 +53,4 @@ typedef struct QObjectOutputVisitor QObjectOutputVisitor; */ Visitor *qobject_output_visitor_new(QObject **result); -void qobject_output_visitor_set_policy(Visitor *v, - CompatPolicyOutput deprecated); - #endif diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index 3b950f6e3d..72b6537bef 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -122,6 +122,9 @@ struct Visitor /* Must be set */ VisitorType type; + /* Optional */ + struct CompatPolicy compat_policy; + /* Must be set for output visitors, optional otherwise. */ void (*complete)(Visitor *v, void *opaque); diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index b3c9ef7a81..dcb96018a9 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -16,6 +16,7 @@ #define QAPI_VISITOR_H #include "qapi/qapi-builtin-types.h" +#include "qapi/qapi-types-compat.h" /* * The QAPI schema defines both a set of C data types, and a QMP wire @@ -477,6 +478,14 @@ bool visit_deprecated_accept(Visitor *v, const char *name, Error **errp); */ bool visit_deprecated(Visitor *v, const char *name); +/* + * Set policy for handling deprecated management interfaces. + * + * Intended use: call visit_set_policy(v, &compat_policy) when + * visiting management interface input or output. + */ +void visit_set_policy(Visitor *v, CompatPolicy *policy); + /* * Visit an enum value. * diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 7310f0a0ca..93fb154ef3 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -19,6 +19,10 @@ #include "qapi/visitor-impl.h" #include "trace.h" +/* Zero-initialization must result in default policy */ +QEMU_BUILD_BUG_ON(COMPAT_POLICY_INPUT_ACCEPT || COMPAT_POLICY_OUTPUT_ACCEPT); + + void visit_complete(Visitor *v, void *opaque) { assert(v->type != VISITOR_OUTPUT || v->complete); @@ -153,6 +157,11 @@ bool visit_deprecated(Visitor *v, const char *name) return true; } +void visit_set_policy(Visitor *v, CompatPolicy *policy) +{ + v->compat_policy = *policy; +} + bool visit_is_input(Visitor *v) { return v->type == VISITOR_INPUT; diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 59600210ce..7e943a0af5 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -32,7 +32,7 @@ Visitor *qobject_input_visitor_new_qmp(QObject *obj) { Visitor *v = qobject_input_visitor_new(obj); - qobject_input_visitor_set_policy(v, compat_policy.deprecated_input); + visit_set_policy(v, &compat_policy); return v; } @@ -40,7 +40,7 @@ Visitor *qobject_output_visitor_new_qmp(QObject **result) { Visitor *v = qobject_output_visitor_new(result); - qobject_output_visitor_set_policy(v, compat_policy.deprecated_output); + visit_set_policy(v, &compat_policy); return v; } diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 04b790412e..71b24a4429 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include -#include "qapi/compat-policy.h" #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" #include "qapi/visitor-impl.h" @@ -44,7 +43,6 @@ typedef struct StackObject { struct QObjectInputVisitor { Visitor visitor; - CompatPolicyInput deprecated_policy; /* Root of visit at visitor creation. */ QObject *root; @@ -667,9 +665,7 @@ static void qobject_input_optional(Visitor *v, const char *name, bool *present) static bool qobject_input_deprecated_accept(Visitor *v, const char *name, Error **errp) { - QObjectInputVisitor *qiv = to_qiv(v); - - switch (qiv->deprecated_policy) { + switch (v->compat_policy.deprecated_input) { case COMPAT_POLICY_INPUT_ACCEPT: return true; case COMPAT_POLICY_INPUT_REJECT: @@ -739,14 +735,6 @@ Visitor *qobject_input_visitor_new(QObject *obj) return &v->visitor; } -void qobject_input_visitor_set_policy(Visitor *v, - CompatPolicyInput deprecated) -{ - QObjectInputVisitor *qiv = to_qiv(v); - - qiv->deprecated_policy = deprecated; -} - Visitor *qobject_input_visitor_new_keyval(QObject *obj) { QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index e4873308d4..9b7f510036 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -13,7 +13,6 @@ */ #include "qemu/osdep.h" -#include "qapi/compat-policy.h" #include "qapi/qobject-output-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" @@ -32,7 +31,6 @@ typedef struct QStackEntry { struct QObjectOutputVisitor { Visitor visitor; - CompatPolicyOutput deprecated_policy; QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */ QObject *root; /* Root of the output visit */ @@ -212,9 +210,7 @@ static bool qobject_output_type_null(Visitor *v, const char *name, static bool qobject_output_deprecated(Visitor *v, const char *name) { - QObjectOutputVisitor *qov = to_qov(v); - - return qov->deprecated_policy != COMPAT_POLICY_OUTPUT_HIDE; + return v->compat_policy.deprecated_output != COMPAT_POLICY_OUTPUT_HIDE; } /* Finish building, and return the root object. @@ -275,11 +271,3 @@ Visitor *qobject_output_visitor_new(QObject **result) return &v->visitor; } - -void qobject_output_visitor_set_policy(Visitor *v, - CompatPolicyOutput deprecated) -{ - QObjectOutputVisitor *qov = to_qov(v); - - qov->deprecated_policy = deprecated; -} From aa2370444b62f8f9a809c024d0c41cb40658a5c3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 25 Oct 2021 06:24:04 +0200 Subject: [PATCH 0775/1334] qapi: Implement deprecated-input={reject,crash} for enum values This copies the code implementing the policy from qapi/qmp-dispatch.c to qapi/qobject-input-visitor.c. Tolerable, but if we acquire more copies, we should look into factoring them out. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Tested-by: Peter Krempa Acked-by: Peter Krempa Message-Id: <20211025042405.3762351-5-armbru@redhat.com> --- include/qapi/util.h | 6 +++++- qapi/compat.json | 3 ++- qapi/qapi-visit-core.c | 18 +++++++++++++++--- scripts/qapi/types.py | 17 ++++++++++++++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/include/qapi/util.h b/include/qapi/util.h index d7bfb30e25..257c600f99 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -11,9 +11,13 @@ #ifndef QAPI_UTIL_H #define QAPI_UTIL_H +/* QEnumLookup flags */ +#define QAPI_ENUM_DEPRECATED 1 + typedef struct QEnumLookup { const char *const *array; - int size; + const unsigned char *const flags; + const int size; } QEnumLookup; const char *qapi_enum_lookup(const QEnumLookup *lookup, int val); diff --git a/qapi/compat.json b/qapi/compat.json index 1d2b76f00c..74a8493d3d 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -42,7 +42,8 @@ # with feature 'deprecated'. We may want to extend it to cover # semantic aspects, CLI, and experimental features. # -# Limitation: not implemented for deprecated enumeration values. +# Limitation: deprecated-output policy @hide is not implemented for +# enumeration values. They behave the same as with policy @accept. # # @deprecated-input: how to handle deprecated input (default 'accept') # @deprecated-output: how to handle deprecated output (default 'accept') diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 93fb154ef3..617ef3fa46 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -393,7 +393,7 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, const QEnumLookup *lookup, Error **errp) { int64_t value; - char *enum_str; + g_autofree char *enum_str = NULL; if (!visit_type_str(v, name, &enum_str, errp)) { return false; @@ -403,11 +403,23 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, if (value < 0) { error_setg(errp, "Parameter '%s' does not accept value '%s'", name ? name : "null", enum_str); - g_free(enum_str); return false; } - g_free(enum_str); + if (lookup->flags && (lookup->flags[value] & QAPI_ENUM_DEPRECATED)) { + switch (v->compat_policy.deprecated_input) { + case COMPAT_POLICY_INPUT_ACCEPT: + break; + case COMPAT_POLICY_INPUT_REJECT: + error_setg(errp, "Deprecated value '%s' disabled by policy", + enum_str); + return false; + case COMPAT_POLICY_INPUT_CRASH: + default: + abort(); + } + } + *obj = value; return true; } diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 831294fe42..ab2441adc9 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -38,6 +38,8 @@ objects_seen = set() def gen_enum_lookup(name: str, members: List[QAPISchemaEnumMember], prefix: Optional[str] = None) -> str: + max_index = c_enum_const(name, '_MAX', prefix) + flags = '' ret = mcgen(''' const QEnumLookup %(c_name)s_lookup = { @@ -52,13 +54,26 @@ const QEnumLookup %(c_name)s_lookup = { ''', index=index, name=memb.name) ret += memb.ifcond.gen_endif() + if 'deprecated' in (f.name for f in memb.features): + flags += mcgen(''' + [%(index)s] = QAPI_ENUM_DEPRECATED, +''', + index=index) + + if flags: + ret += mcgen(''' + }, + .flags = (const unsigned char[%(max_index)s]) { +''', + max_index=max_index) + ret += flags ret += mcgen(''' }, .size = %(max_index)s }; ''', - max_index=c_enum_const(name, '_MAX', prefix)) + max_index=max_index) return ret From 1c46937358fc27a9e446d08c877389ee84d6767d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9trot?= Date: Mon, 25 Oct 2021 14:28:03 +0200 Subject: [PATCH 0776/1334] qemu/int128: Add int128_{not,xor} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addition of not and xor on 128-bit integers. Signed-off-by: Frédéric Pétrot Co-authored-by: Fabien Portas Message-Id: <20211025122818.168890-3-frederic.petrot@univ-grenoble-alpes.fr> [rth: Split out logical operations.] Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- include/qemu/int128.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/qemu/int128.h b/include/qemu/int128.h index 2ac0746426..b6d517aea4 100644 --- a/include/qemu/int128.h +++ b/include/qemu/int128.h @@ -58,6 +58,11 @@ static inline Int128 int128_exts64(int64_t a) return a; } +static inline Int128 int128_not(Int128 a) +{ + return ~a; +} + static inline Int128 int128_and(Int128 a, Int128 b) { return a & b; @@ -68,6 +73,11 @@ static inline Int128 int128_or(Int128 a, Int128 b) return a | b; } +static inline Int128 int128_xor(Int128 a, Int128 b) +{ + return a ^ b; +} + static inline Int128 int128_rshift(Int128 a, int n) { return a >> n; @@ -235,6 +245,11 @@ static inline Int128 int128_exts64(int64_t a) return int128_make128(a, (a < 0) ? -1 : 0); } +static inline Int128 int128_not(Int128 a) +{ + return int128_make128(~a.lo, ~a.hi); +} + static inline Int128 int128_and(Int128 a, Int128 b) { return int128_make128(a.lo & b.lo, a.hi & b.hi); @@ -245,6 +260,11 @@ static inline Int128 int128_or(Int128 a, Int128 b) return int128_make128(a.lo | b.lo, a.hi | b.hi); } +static inline Int128 int128_xor(Int128 a, Int128 b) +{ + return int128_make128(a.lo ^ b.lo, a.hi ^ b.hi); +} + static inline Int128 int128_rshift(Int128 a, int n) { int64_t h; From 9276a31c3484ff236a958a1e2a38beefb0eb7ebb Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Mon, 25 Oct 2021 16:11:36 -0300 Subject: [PATCH 0777/1334] host-utils: move checks out of divu128/divs128 In preparation for changing the divu128/divs128 implementations to allow for quotients larger than 64 bits, move the div-by-zero and overflow checks to the callers. Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211025191154.350831-2-luis.pires@eldorado.org.br> Signed-off-by: Richard Henderson --- include/hw/clock.h | 5 +++-- include/qemu/host-utils.h | 34 ++++++++++++--------------------- target/ppc/int_helper.c | 14 +++++++++----- util/host-utils.c | 40 ++++++++++++++++++--------------------- 4 files changed, 42 insertions(+), 51 deletions(-) diff --git a/include/hw/clock.h b/include/hw/clock.h index 11f67fb970..7443e6c4ab 100644 --- a/include/hw/clock.h +++ b/include/hw/clock.h @@ -324,8 +324,9 @@ static inline uint64_t clock_ns_to_ticks(const Clock *clk, uint64_t ns) return 0; } /* - * Ignore divu128() return value as we've caught div-by-zero and don't - * need different behaviour for overflow. + * BUG: when CONFIG_INT128 is not defined, the current implementation of + * divu128 does not return a valid truncated quotient, so the result will + * be wrong. */ divu128(&lo, &hi, clk->period); return lo; diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index ca9f3f021b..e82e6239af 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -52,36 +52,26 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) return (__int128_t)a * b / c; } -static inline int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) +static inline void divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) { - if (divisor == 0) { - return 1; - } else { - __uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow; - __uint128_t result = dividend / divisor; - *plow = result; - *phigh = dividend % divisor; - return result > UINT64_MAX; - } + __uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow; + __uint128_t result = dividend / divisor; + *plow = result; + *phigh = dividend % divisor; } -static inline int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) +static inline void divs128(int64_t *plow, int64_t *phigh, int64_t divisor) { - if (divisor == 0) { - return 1; - } else { - __int128_t dividend = ((__int128_t)*phigh << 64) | (uint64_t)*plow; - __int128_t result = dividend / divisor; - *plow = result; - *phigh = dividend % divisor; - return result != *plow; - } + __int128_t dividend = ((__int128_t)*phigh << 64) | (uint64_t)*plow; + __int128_t result = dividend / divisor; + *plow = result; + *phigh = dividend % divisor; } #else void muls64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b); void mulu64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b); -int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); -int divs128(int64_t *plow, int64_t *phigh, int64_t divisor); +void divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); +void divs128(int64_t *plow, int64_t *phigh, int64_t divisor); static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) { diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index f5dac3aa87..510faf24cf 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -104,10 +104,11 @@ uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) uint64_t rt = 0; int overflow = 0; - overflow = divu128(&rt, &ra, rb); - - if (unlikely(overflow)) { + if (unlikely(rb == 0 || ra >= rb)) { + overflow = 1; rt = 0; /* Undefined */ + } else { + divu128(&rt, &ra, rb); } if (oe) { @@ -122,10 +123,13 @@ uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) int64_t rt = 0; int64_t ra = (int64_t)rau; int64_t rb = (int64_t)rbu; - int overflow = divs128(&rt, &ra, rb); + int overflow = 0; - if (unlikely(overflow)) { + if (unlikely(rb == 0 || uabs64(ra) >= uabs64(rb))) { + overflow = 1; rt = 0; /* Undefined */ + } else { + divs128(&rt, &ra, rb); } if (oe) { diff --git a/util/host-utils.c b/util/host-utils.c index a789a11b46..701a371843 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -86,24 +86,23 @@ void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) *phigh = rh; } -/* Unsigned 128x64 division. Returns 1 if overflow (divide by zero or */ -/* quotient exceeds 64 bits). Otherwise returns quotient via plow and */ -/* remainder via phigh. */ -int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) +/* + * Unsigned 128-by-64 division. Returns quotient via plow and + * remainder via phigh. + * The result must fit in 64 bits (plow) - otherwise, the result + * is undefined. + * This function will cause a division by zero if passed a zero divisor. + */ +void divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) { uint64_t dhi = *phigh; uint64_t dlo = *plow; unsigned i; uint64_t carry = 0; - if (divisor == 0) { - return 1; - } else if (dhi == 0) { + if (divisor == 0 || dhi == 0) { *plow = dlo / divisor; *phigh = dlo % divisor; - return 0; - } else if (dhi >= divisor) { - return 1; } else { for (i = 0; i < 64; i++) { @@ -120,15 +119,20 @@ int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) *plow = dlo; *phigh = dhi; - return 0; } } -int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) +/* + * Signed 128-by-64 division. Returns quotient via plow and + * remainder via phigh. + * The result must fit in 64 bits (plow) - otherwise, the result + * is undefined. + * This function will cause a division by zero if passed a zero divisor. + */ +void divs128(int64_t *plow, int64_t *phigh, int64_t divisor) { int sgn_dvdnd = *phigh < 0; int sgn_divsr = divisor < 0; - int overflow = 0; if (sgn_dvdnd) { *plow = ~(*plow); @@ -145,19 +149,11 @@ int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) divisor = 0 - divisor; } - overflow = divu128((uint64_t *)plow, (uint64_t *)phigh, (uint64_t)divisor); + divu128((uint64_t *)plow, (uint64_t *)phigh, (uint64_t)divisor); if (sgn_dvdnd ^ sgn_divsr) { *plow = 0 - *plow; } - - if (!overflow) { - if ((*plow < 0) ^ (sgn_dvdnd ^ sgn_divsr)) { - overflow = 1; - } - } - - return overflow; } #endif From 8ac2d6c526d9ea0c89c8aa7046ca56e1b1b9d130 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Mon, 25 Oct 2021 16:11:37 -0300 Subject: [PATCH 0778/1334] host-utils: move udiv_qrnnd() to host-utils Move udiv_qrnnd() from include/fpu/softfloat-macros.h to host-utils, so it can be reused by divu128(). Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211025191154.350831-3-luis.pires@eldorado.org.br> Signed-off-by: Richard Henderson --- include/fpu/softfloat-macros.h | 82 ---------------------------------- include/qemu/host-utils.h | 81 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 82 deletions(-) diff --git a/include/fpu/softfloat-macros.h b/include/fpu/softfloat-macros.h index 81c3fe8256..f35cdbfa63 100644 --- a/include/fpu/softfloat-macros.h +++ b/include/fpu/softfloat-macros.h @@ -8,7 +8,6 @@ * so some portions are provided under: * the SoftFloat-2a license * the BSD license - * GPL-v2-or-later * * Any future contributions to this file after December 1st 2014 will be * taken to be licensed under the Softfloat-2a license unless specifically @@ -75,10 +74,6 @@ this code that are retained. * THE POSSIBILITY OF SUCH DAMAGE. */ -/* Portions of this work are licensed under the terms of the GNU GPL, - * version 2 or later. See the COPYING file in the top-level directory. - */ - #ifndef FPU_SOFTFLOAT_MACROS_H #define FPU_SOFTFLOAT_MACROS_H @@ -585,83 +580,6 @@ static inline uint64_t estimateDiv128To64(uint64_t a0, uint64_t a1, uint64_t b) } -/* From the GNU Multi Precision Library - longlong.h __udiv_qrnnd - * (https://gmplib.org/repo/gmp/file/tip/longlong.h) - * - * Licensed under the GPLv2/LGPLv3 - */ -static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, - uint64_t n0, uint64_t d) -{ -#if defined(__x86_64__) - uint64_t q; - asm("divq %4" : "=a"(q), "=d"(*r) : "0"(n0), "1"(n1), "rm"(d)); - return q; -#elif defined(__s390x__) && !defined(__clang__) - /* Need to use a TImode type to get an even register pair for DLGR. */ - unsigned __int128 n = (unsigned __int128)n1 << 64 | n0; - asm("dlgr %0, %1" : "+r"(n) : "r"(d)); - *r = n >> 64; - return n; -#elif defined(_ARCH_PPC64) && defined(_ARCH_PWR7) - /* From Power ISA 2.06, programming note for divdeu. */ - uint64_t q1, q2, Q, r1, r2, R; - asm("divdeu %0,%2,%4; divdu %1,%3,%4" - : "=&r"(q1), "=r"(q2) - : "r"(n1), "r"(n0), "r"(d)); - r1 = -(q1 * d); /* low part of (n1<<64) - (q1 * d) */ - r2 = n0 - (q2 * d); - Q = q1 + q2; - R = r1 + r2; - if (R >= d || R < r2) { /* overflow implies R > d */ - Q += 1; - R -= d; - } - *r = R; - return Q; -#else - uint64_t d0, d1, q0, q1, r1, r0, m; - - d0 = (uint32_t)d; - d1 = d >> 32; - - r1 = n1 % d1; - q1 = n1 / d1; - m = q1 * d0; - r1 = (r1 << 32) | (n0 >> 32); - if (r1 < m) { - q1 -= 1; - r1 += d; - if (r1 >= d) { - if (r1 < m) { - q1 -= 1; - r1 += d; - } - } - } - r1 -= m; - - r0 = r1 % d1; - q0 = r1 / d1; - m = q0 * d0; - r0 = (r0 << 32) | (uint32_t)n0; - if (r0 < m) { - q0 -= 1; - r0 += d; - if (r0 >= d) { - if (r0 < m) { - q0 -= 1; - r0 += d; - } - } - } - r0 -= m; - - *r = r0; - return (q1 << 32) | q0; -#endif -} - /*---------------------------------------------------------------------------- | Returns an approximation to the square root of the 32-bit significand given | by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index e82e6239af..08a17e16e5 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -23,6 +23,10 @@ * THE SOFTWARE. */ +/* Portions of this work are licensed under the terms of the GNU GPL, + * version 2 or later. See the COPYING file in the top-level directory. + */ + #ifndef HOST_UTILS_H #define HOST_UTILS_H @@ -726,4 +730,81 @@ void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift); */ void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow); +/* From the GNU Multi Precision Library - longlong.h __udiv_qrnnd + * (https://gmplib.org/repo/gmp/file/tip/longlong.h) + * + * Licensed under the GPLv2/LGPLv3 + */ +static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, + uint64_t n0, uint64_t d) +{ +#if defined(__x86_64__) + uint64_t q; + asm("divq %4" : "=a"(q), "=d"(*r) : "0"(n0), "1"(n1), "rm"(d)); + return q; +#elif defined(__s390x__) && !defined(__clang__) + /* Need to use a TImode type to get an even register pair for DLGR. */ + unsigned __int128 n = (unsigned __int128)n1 << 64 | n0; + asm("dlgr %0, %1" : "+r"(n) : "r"(d)); + *r = n >> 64; + return n; +#elif defined(_ARCH_PPC64) && defined(_ARCH_PWR7) + /* From Power ISA 2.06, programming note for divdeu. */ + uint64_t q1, q2, Q, r1, r2, R; + asm("divdeu %0,%2,%4; divdu %1,%3,%4" + : "=&r"(q1), "=r"(q2) + : "r"(n1), "r"(n0), "r"(d)); + r1 = -(q1 * d); /* low part of (n1<<64) - (q1 * d) */ + r2 = n0 - (q2 * d); + Q = q1 + q2; + R = r1 + r2; + if (R >= d || R < r2) { /* overflow implies R > d */ + Q += 1; + R -= d; + } + *r = R; + return Q; +#else + uint64_t d0, d1, q0, q1, r1, r0, m; + + d0 = (uint32_t)d; + d1 = d >> 32; + + r1 = n1 % d1; + q1 = n1 / d1; + m = q1 * d0; + r1 = (r1 << 32) | (n0 >> 32); + if (r1 < m) { + q1 -= 1; + r1 += d; + if (r1 >= d) { + if (r1 < m) { + q1 -= 1; + r1 += d; + } + } + } + r1 -= m; + + r0 = r1 % d1; + q0 = r1 / d1; + m = q0 * d0; + r0 = (r0 << 32) | (uint32_t)n0; + if (r0 < m) { + q0 -= 1; + r0 += d; + if (r0 >= d) { + if (r0 < m) { + q0 -= 1; + r0 += d; + } + } + } + r0 -= m; + + *r = r0; + return (q1 << 32) | q0; +#endif +} + #endif From 40f3e79a862554553811d0681c05e00a4705e91c Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Mon, 25 Oct 2021 16:11:38 -0300 Subject: [PATCH 0779/1334] host-utils: add 128-bit quotient support to divu128/divs128 These will be used to implement new decimal floating point instructions from Power ISA 3.1. The remainder is now returned directly by divu128/divs128, freeing up phigh to receive the high 64 bits of the quotient. Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211025191154.350831-4-luis.pires@eldorado.org.br> Signed-off-by: Richard Henderson --- include/hw/clock.h | 6 +- include/qemu/host-utils.h | 20 ++++-- target/ppc/int_helper.c | 9 +-- util/host-utils.c | 133 +++++++++++++++++++++++++------------- 4 files changed, 108 insertions(+), 60 deletions(-) diff --git a/include/hw/clock.h b/include/hw/clock.h index 7443e6c4ab..5c927cee7f 100644 --- a/include/hw/clock.h +++ b/include/hw/clock.h @@ -323,11 +323,7 @@ static inline uint64_t clock_ns_to_ticks(const Clock *clk, uint64_t ns) if (clk->period == 0) { return 0; } - /* - * BUG: when CONFIG_INT128 is not defined, the current implementation of - * divu128 does not return a valid truncated quotient, so the result will - * be wrong. - */ + divu128(&lo, &hi, clk->period); return lo; } diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 08a17e16e5..a3a7ced78d 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -56,26 +56,32 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) return (__int128_t)a * b / c; } -static inline void divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) +static inline uint64_t divu128(uint64_t *plow, uint64_t *phigh, + uint64_t divisor) { __uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow; __uint128_t result = dividend / divisor; + *plow = result; - *phigh = dividend % divisor; + *phigh = result >> 64; + return dividend % divisor; } -static inline void divs128(int64_t *plow, int64_t *phigh, int64_t divisor) +static inline int64_t divs128(uint64_t *plow, int64_t *phigh, + int64_t divisor) { - __int128_t dividend = ((__int128_t)*phigh << 64) | (uint64_t)*plow; + __int128_t dividend = ((__int128_t)*phigh << 64) | *plow; __int128_t result = dividend / divisor; + *plow = result; - *phigh = dividend % divisor; + *phigh = result >> 64; + return dividend % divisor; } #else void muls64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b); void mulu64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b); -void divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); -void divs128(int64_t *plow, int64_t *phigh, int64_t divisor); +uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); +int64_t divs128(uint64_t *plow, int64_t *phigh, int64_t divisor); static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) { diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 510faf24cf..eeb7781a9e 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -120,7 +120,7 @@ uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) { - int64_t rt = 0; + uint64_t rt = 0; int64_t ra = (int64_t)rau; int64_t rb = (int64_t)rbu; int overflow = 0; @@ -2506,6 +2506,7 @@ uint32_t helper_bcdcfsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) int cr; uint64_t lo_value; uint64_t hi_value; + uint64_t rem; ppc_avr_t ret = { .u64 = { 0, 0 } }; if (b->VsrSD(0) < 0) { @@ -2541,10 +2542,10 @@ uint32_t helper_bcdcfsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) * In that case, we leave r unchanged. */ } else { - divu128(&lo_value, &hi_value, 1000000000000000ULL); + rem = divu128(&lo_value, &hi_value, 1000000000000000ULL); - for (i = 1; i < 16; hi_value /= 10, i++) { - bcd_put_digit(&ret, hi_value % 10, i); + for (i = 1; i < 16; rem /= 10, i++) { + bcd_put_digit(&ret, rem % 10, i); } for (; i < 32; lo_value /= 10, i++) { diff --git a/util/host-utils.c b/util/host-utils.c index 701a371843..bcc772b8ec 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -87,72 +87,117 @@ void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) } /* - * Unsigned 128-by-64 division. Returns quotient via plow and - * remainder via phigh. - * The result must fit in 64 bits (plow) - otherwise, the result - * is undefined. - * This function will cause a division by zero if passed a zero divisor. + * Unsigned 128-by-64 division. + * Returns the remainder. + * Returns quotient via plow and phigh. + * Also returns the remainder via the function return value. */ -void divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) +uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) { uint64_t dhi = *phigh; uint64_t dlo = *plow; - unsigned i; - uint64_t carry = 0; + uint64_t rem, dhighest; + int sh; if (divisor == 0 || dhi == 0) { *plow = dlo / divisor; - *phigh = dlo % divisor; + *phigh = 0; + return dlo % divisor; } else { + sh = clz64(divisor); - for (i = 0; i < 64; i++) { - carry = dhi >> 63; - dhi = (dhi << 1) | (dlo >> 63); - if (carry || (dhi >= divisor)) { - dhi -= divisor; - carry = 1; - } else { - carry = 0; + if (dhi < divisor) { + if (sh != 0) { + /* normalize the divisor, shifting the dividend accordingly */ + divisor <<= sh; + dhi = (dhi << sh) | (dlo >> (64 - sh)); + dlo <<= sh; } - dlo = (dlo << 1) | carry; + + *phigh = 0; + *plow = udiv_qrnnd(&rem, dhi, dlo, divisor); + } else { + if (sh != 0) { + /* normalize the divisor, shifting the dividend accordingly */ + divisor <<= sh; + dhighest = dhi >> (64 - sh); + dhi = (dhi << sh) | (dlo >> (64 - sh)); + dlo <<= sh; + + *phigh = udiv_qrnnd(&dhi, dhighest, dhi, divisor); + } else { + /** + * dhi >= divisor + * Since the MSB of divisor is set (sh == 0), + * (dhi - divisor) < divisor + * + * Thus, the high part of the quotient is 1, and we can + * calculate the low part with a single call to udiv_qrnnd + * after subtracting divisor from dhi + */ + dhi -= divisor; + *phigh = 1; + } + + *plow = udiv_qrnnd(&rem, dhi, dlo, divisor); } - *plow = dlo; - *phigh = dhi; + /* + * since the dividend/divisor might have been normalized, + * the remainder might also have to be shifted back + */ + return rem >> sh; } } /* - * Signed 128-by-64 division. Returns quotient via plow and - * remainder via phigh. - * The result must fit in 64 bits (plow) - otherwise, the result - * is undefined. - * This function will cause a division by zero if passed a zero divisor. + * Signed 128-by-64 division. + * Returns quotient via plow and phigh. + * Also returns the remainder via the function return value. */ -void divs128(int64_t *plow, int64_t *phigh, int64_t divisor) +int64_t divs128(uint64_t *plow, int64_t *phigh, int64_t divisor) { - int sgn_dvdnd = *phigh < 0; - int sgn_divsr = divisor < 0; + bool neg_quotient = false, neg_remainder = false; + uint64_t unsig_hi = *phigh, unsig_lo = *plow; + uint64_t rem; - if (sgn_dvdnd) { - *plow = ~(*plow); - *phigh = ~(*phigh); - if (*plow == (int64_t)-1) { + if (*phigh < 0) { + neg_quotient = !neg_quotient; + neg_remainder = !neg_remainder; + + if (unsig_lo == 0) { + unsig_hi = -unsig_hi; + } else { + unsig_hi = ~unsig_hi; + unsig_lo = -unsig_lo; + } + } + + if (divisor < 0) { + neg_quotient = !neg_quotient; + + divisor = -divisor; + } + + rem = divu128(&unsig_lo, &unsig_hi, (uint64_t)divisor); + + if (neg_quotient) { + if (unsig_lo == 0) { + *phigh = -unsig_hi; *plow = 0; - (*phigh)++; - } else { - (*plow)++; - } + } else { + *phigh = ~unsig_hi; + *plow = -unsig_lo; + } + } else { + *phigh = unsig_hi; + *plow = unsig_lo; } - if (sgn_divsr) { - divisor = 0 - divisor; - } - - divu128((uint64_t *)plow, (uint64_t *)phigh, (uint64_t)divisor); - - if (sgn_dvdnd ^ sgn_divsr) { - *plow = 0 - *plow; + if (neg_remainder) { + return -rem; + } else { + return rem; } } #endif From 023462978a1fb7b5a3f7ea74236a05d253d0b6e6 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Mon, 25 Oct 2021 16:11:39 -0300 Subject: [PATCH 0780/1334] host-utils: add unit tests for divu128/divs128 Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211025191154.350831-5-luis.pires@eldorado.org.br> Signed-off-by: Richard Henderson --- tests/unit/meson.build | 1 + tests/unit/test-div128.c | 197 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 tests/unit/test-div128.c diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 7c297d7e5c..5ac2d9e943 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -23,6 +23,7 @@ tests = { # all code tested by test-x86-cpuid is inside topology.h 'test-x86-cpuid': [], 'test-cutils': [], + 'test-div128': [], 'test-shift128': [], 'test-mul64': [], # all code tested by test-int128 is inside int128.h diff --git a/tests/unit/test-div128.c b/tests/unit/test-div128.c new file mode 100644 index 0000000000..0bc25fe4a8 --- /dev/null +++ b/tests/unit/test-div128.c @@ -0,0 +1,197 @@ +/* + * Test 128-bit division functions + * + * Copyright (c) 2021 Instituto de Pesquisas Eldorado (eldorado.org.br) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" + +typedef struct { + uint64_t high; + uint64_t low; + uint64_t rhigh; + uint64_t rlow; + uint64_t divisor; + uint64_t remainder; +} test_data_unsigned; + +typedef struct { + int64_t high; + uint64_t low; + int64_t rhigh; + uint64_t rlow; + int64_t divisor; + int64_t remainder; +} test_data_signed; + +static const test_data_unsigned test_table_unsigned[] = { + /* Dividend fits in 64 bits */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, + 0x0000000000000000ULL, 0x0000000000000000ULL, + 0x0000000000000001ULL, 0x0000000000000000ULL}, + { 0x0000000000000000ULL, 0x0000000000000001ULL, + 0x0000000000000000ULL, 0x0000000000000001ULL, + 0x0000000000000001ULL, 0x0000000000000000ULL}, + { 0x0000000000000000ULL, 0x0000000000000003ULL, + 0x0000000000000000ULL, 0x0000000000000001ULL, + 0x0000000000000002ULL, 0x0000000000000001ULL}, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000001ULL, 0x0000000000000000ULL}, + { 0x0000000000000000ULL, 0xa000000000000000ULL, + 0x0000000000000000ULL, 0x0000000000000002ULL, + 0x4000000000000000ULL, 0x2000000000000000ULL}, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000000ULL, 0x0000000000000001ULL, + 0x8000000000000000ULL, 0x0000000000000000ULL}, + + /* Dividend > 64 bits, with MSB 0 */ + { 0x123456789abcdefeULL, 0xefedcba987654321ULL, + 0x123456789abcdefeULL, 0xefedcba987654321ULL, + 0x0000000000000001ULL, 0x0000000000000000ULL}, + { 0x123456789abcdefeULL, 0xefedcba987654321ULL, + 0x0000000000000001ULL, 0x000000000000000dULL, + 0x123456789abcdefeULL, 0x03456789abcdf03bULL}, + { 0x123456789abcdefeULL, 0xefedcba987654321ULL, + 0x0123456789abcdefULL, 0xeefedcba98765432ULL, + 0x0000000000000010ULL, 0x0000000000000001ULL}, + + /* Dividend > 64 bits, with MSB 1 */ + { 0xfeeddccbbaa99887ULL, 0x766554433221100fULL, + 0xfeeddccbbaa99887ULL, 0x766554433221100fULL, + 0x0000000000000001ULL, 0x0000000000000000ULL}, + { 0xfeeddccbbaa99887ULL, 0x766554433221100fULL, + 0x0000000000000001ULL, 0x0000000000000000ULL, + 0xfeeddccbbaa99887ULL, 0x766554433221100fULL}, + { 0xfeeddccbbaa99887ULL, 0x766554433221100fULL, + 0x0feeddccbbaa9988ULL, 0x7766554433221100ULL, + 0x0000000000000010ULL, 0x000000000000000fULL}, + { 0xfeeddccbbaa99887ULL, 0x766554433221100fULL, + 0x000000000000000eULL, 0x00f0f0f0f0f0f35aULL, + 0x123456789abcdefeULL, 0x0f8922bc55ef90c3ULL}, + + /** + * Divisor == 64 bits, with MSB 1 + * and high 64 bits of dividend >= divisor + * (for testing normalization) + */ + { 0xfeeddccbbaa99887ULL, 0x766554433221100fULL, + 0x0000000000000001ULL, 0x0000000000000000ULL, + 0xfeeddccbbaa99887ULL, 0x766554433221100fULL}, + { 0xfeeddccbbaa99887ULL, 0x766554433221100fULL, + 0x0000000000000001ULL, 0xfddbb9977553310aULL, + 0x8000000000000001ULL, 0x78899aabbccddf05ULL}, + + /* Dividend > 64 bits, divisor almost as big */ + { 0x0000000000000001ULL, 0x23456789abcdef01ULL, + 0x0000000000000000ULL, 0x000000000000000fULL, + 0x123456789abcdefeULL, 0x123456789abcde1fULL}, +}; + +static const test_data_signed test_table_signed[] = { + /* Positive dividend, positive/negative divisors */ + { 0x0000000000000000LL, 0x0000000000bc614eULL, + 0x0000000000000000LL, 0x0000000000bc614eULL, + 0x0000000000000001LL, 0x0000000000000000LL}, + { 0x0000000000000000LL, 0x0000000000bc614eULL, + 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0xffffffffffffffffLL, 0x0000000000000000LL}, + { 0x0000000000000000LL, 0x0000000000bc614eULL, + 0x0000000000000000LL, 0x00000000005e30a7ULL, + 0x0000000000000002LL, 0x0000000000000000LL}, + { 0x0000000000000000LL, 0x0000000000bc614eULL, + 0xffffffffffffffffLL, 0xffffffffffa1cf59ULL, + 0xfffffffffffffffeLL, 0x0000000000000000LL}, + { 0x0000000000000000LL, 0x0000000000bc614eULL, + 0x0000000000000000LL, 0x0000000000178c29ULL, + 0x0000000000000008LL, 0x0000000000000006LL}, + { 0x0000000000000000LL, 0x0000000000bc614eULL, + 0xffffffffffffffffLL, 0xffffffffffe873d7ULL, + 0xfffffffffffffff8LL, 0x0000000000000006LL}, + { 0x0000000000000000LL, 0x0000000000bc614eULL, + 0x0000000000000000LL, 0x000000000000550dULL, + 0x0000000000000237LL, 0x0000000000000183LL}, + { 0x0000000000000000LL, 0x0000000000bc614eULL, + 0xffffffffffffffffLL, 0xffffffffffffaaf3ULL, + 0xfffffffffffffdc9LL, 0x0000000000000183LL}, + + /* Negative dividend, positive/negative divisors */ + { 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0x0000000000000001LL, 0x0000000000000000LL}, + { 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0x0000000000000000LL, 0x0000000000bc614eULL, + 0xffffffffffffffffLL, 0x0000000000000000LL}, + { 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0xffffffffffffffffLL, 0xffffffffffa1cf59ULL, + 0x0000000000000002LL, 0x0000000000000000LL}, + { 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0x0000000000000000LL, 0x00000000005e30a7ULL, + 0xfffffffffffffffeLL, 0x0000000000000000LL}, + { 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0xffffffffffffffffLL, 0xffffffffffe873d7ULL, + 0x0000000000000008LL, 0xfffffffffffffffaLL}, + { 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0x0000000000000000LL, 0x0000000000178c29ULL, + 0xfffffffffffffff8LL, 0xfffffffffffffffaLL}, + { 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0xffffffffffffffffLL, 0xffffffffffffaaf3ULL, + 0x0000000000000237LL, 0xfffffffffffffe7dLL}, + { 0xffffffffffffffffLL, 0xffffffffff439eb2ULL, + 0x0000000000000000LL, 0x000000000000550dULL, + 0xfffffffffffffdc9LL, 0xfffffffffffffe7dLL}, +}; + +static void test_divu128(void) +{ + int i; + uint64_t rem; + test_data_unsigned tmp; + + for (i = 0; i < ARRAY_SIZE(test_table_unsigned); ++i) { + tmp = test_table_unsigned[i]; + + rem = divu128(&tmp.low, &tmp.high, tmp.divisor); + g_assert_cmpuint(tmp.low, ==, tmp.rlow); + g_assert_cmpuint(tmp.high, ==, tmp.rhigh); + g_assert_cmpuint(rem, ==, tmp.remainder); + } +} + +static void test_divs128(void) +{ + int i; + int64_t rem; + test_data_signed tmp; + + for (i = 0; i < ARRAY_SIZE(test_table_signed); ++i) { + tmp = test_table_signed[i]; + + rem = divs128(&tmp.low, &tmp.high, tmp.divisor); + g_assert_cmpuint(tmp.low, ==, tmp.rlow); + g_assert_cmpuint(tmp.high, ==, tmp.rhigh); + g_assert_cmpuint(rem, ==, tmp.remainder); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/host-utils/test_divu128", test_divu128); + g_test_add_func("/host-utils/test_divs128", test_divs128); + return g_test_run(); +} From b1fde411d0f3dd146ba9864f3a475e7100a14a3b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 23 Aug 2021 13:07:49 -0700 Subject: [PATCH 0781/1334] tcg/optimize: Rename "mask" to "z_mask" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for tracking different masks by renaming this one. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 142 +++++++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 70 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index c239c3bd07..148e360fc6 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -41,7 +41,7 @@ typedef struct TempOptInfo { TCGTemp *prev_copy; TCGTemp *next_copy; uint64_t val; - uint64_t mask; + uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */ } TempOptInfo; static inline TempOptInfo *ts_info(TCGTemp *ts) @@ -81,7 +81,7 @@ static void reset_ts(TCGTemp *ts) ti->next_copy = ts; ti->prev_copy = ts; ti->is_const = false; - ti->mask = -1; + ti->z_mask = -1; } static void reset_temp(TCGArg arg) @@ -111,14 +111,14 @@ static void init_ts_info(TCGTempSet *temps_used, TCGTemp *ts) if (ts->kind == TEMP_CONST) { ti->is_const = true; ti->val = ts->val; - ti->mask = ts->val; + ti->z_mask = ts->val; if (TCG_TARGET_REG_BITS > 32 && ts->type == TCG_TYPE_I32) { /* High bits of a 32-bit quantity are garbage. */ - ti->mask |= ~0xffffffffull; + ti->z_mask |= ~0xffffffffull; } } else { ti->is_const = false; - ti->mask = -1; + ti->z_mask = -1; } } @@ -186,7 +186,7 @@ static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg dst, TCGArg src) const TCGOpDef *def; TempOptInfo *di; TempOptInfo *si; - uint64_t mask; + uint64_t z_mask; TCGOpcode new_op; if (ts_are_copies(dst_ts, src_ts)) { @@ -210,12 +210,12 @@ static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg dst, TCGArg src) op->args[0] = dst; op->args[1] = src; - mask = si->mask; + z_mask = si->z_mask; if (TCG_TARGET_REG_BITS > 32 && new_op == INDEX_op_mov_i32) { /* High bits of the destination are now garbage. */ - mask |= ~0xffffffffull; + z_mask |= ~0xffffffffull; } - di->mask = mask; + di->z_mask = z_mask; if (src_ts->type == dst_ts->type) { TempOptInfo *ni = ts_info(si->next_copy); @@ -621,7 +621,7 @@ void tcg_optimize(TCGContext *s) } QTAILQ_FOREACH_SAFE(op, &s->ops, link, op_next) { - uint64_t mask, partmask, affected, tmp; + uint64_t z_mask, partmask, affected, tmp; int nb_oargs, nb_iargs; TCGOpcode opc = op->opc; const TCGOpDef *def = &tcg_op_defs[opc]; @@ -855,170 +855,172 @@ void tcg_optimize(TCGContext *s) /* Simplify using known-zero bits. Currently only ops with a single output argument is supported. */ - mask = -1; + z_mask = -1; affected = -1; switch (opc) { CASE_OP_32_64(ext8s): - if ((arg_info(op->args[1])->mask & 0x80) != 0) { + if ((arg_info(op->args[1])->z_mask & 0x80) != 0) { break; } QEMU_FALLTHROUGH; CASE_OP_32_64(ext8u): - mask = 0xff; + z_mask = 0xff; goto and_const; CASE_OP_32_64(ext16s): - if ((arg_info(op->args[1])->mask & 0x8000) != 0) { + if ((arg_info(op->args[1])->z_mask & 0x8000) != 0) { break; } QEMU_FALLTHROUGH; CASE_OP_32_64(ext16u): - mask = 0xffff; + z_mask = 0xffff; goto and_const; case INDEX_op_ext32s_i64: - if ((arg_info(op->args[1])->mask & 0x80000000) != 0) { + if ((arg_info(op->args[1])->z_mask & 0x80000000) != 0) { break; } QEMU_FALLTHROUGH; case INDEX_op_ext32u_i64: - mask = 0xffffffffU; + z_mask = 0xffffffffU; goto and_const; CASE_OP_32_64(and): - mask = arg_info(op->args[2])->mask; + z_mask = arg_info(op->args[2])->z_mask; if (arg_is_const(op->args[2])) { and_const: - affected = arg_info(op->args[1])->mask & ~mask; + affected = arg_info(op->args[1])->z_mask & ~z_mask; } - mask = arg_info(op->args[1])->mask & mask; + z_mask = arg_info(op->args[1])->z_mask & z_mask; break; case INDEX_op_ext_i32_i64: - if ((arg_info(op->args[1])->mask & 0x80000000) != 0) { + if ((arg_info(op->args[1])->z_mask & 0x80000000) != 0) { break; } QEMU_FALLTHROUGH; case INDEX_op_extu_i32_i64: /* We do not compute affected as it is a size changing op. */ - mask = (uint32_t)arg_info(op->args[1])->mask; + z_mask = (uint32_t)arg_info(op->args[1])->z_mask; break; CASE_OP_32_64(andc): /* Known-zeros does not imply known-ones. Therefore unless op->args[2] is constant, we can't infer anything from it. */ if (arg_is_const(op->args[2])) { - mask = ~arg_info(op->args[2])->mask; + z_mask = ~arg_info(op->args[2])->z_mask; goto and_const; } /* But we certainly know nothing outside args[1] may be set. */ - mask = arg_info(op->args[1])->mask; + z_mask = arg_info(op->args[1])->z_mask; break; case INDEX_op_sar_i32: if (arg_is_const(op->args[2])) { tmp = arg_info(op->args[2])->val & 31; - mask = (int32_t)arg_info(op->args[1])->mask >> tmp; + z_mask = (int32_t)arg_info(op->args[1])->z_mask >> tmp; } break; case INDEX_op_sar_i64: if (arg_is_const(op->args[2])) { tmp = arg_info(op->args[2])->val & 63; - mask = (int64_t)arg_info(op->args[1])->mask >> tmp; + z_mask = (int64_t)arg_info(op->args[1])->z_mask >> tmp; } break; case INDEX_op_shr_i32: if (arg_is_const(op->args[2])) { tmp = arg_info(op->args[2])->val & 31; - mask = (uint32_t)arg_info(op->args[1])->mask >> tmp; + z_mask = (uint32_t)arg_info(op->args[1])->z_mask >> tmp; } break; case INDEX_op_shr_i64: if (arg_is_const(op->args[2])) { tmp = arg_info(op->args[2])->val & 63; - mask = (uint64_t)arg_info(op->args[1])->mask >> tmp; + z_mask = (uint64_t)arg_info(op->args[1])->z_mask >> tmp; } break; case INDEX_op_extrl_i64_i32: - mask = (uint32_t)arg_info(op->args[1])->mask; + z_mask = (uint32_t)arg_info(op->args[1])->z_mask; break; case INDEX_op_extrh_i64_i32: - mask = (uint64_t)arg_info(op->args[1])->mask >> 32; + z_mask = (uint64_t)arg_info(op->args[1])->z_mask >> 32; break; CASE_OP_32_64(shl): if (arg_is_const(op->args[2])) { tmp = arg_info(op->args[2])->val & (TCG_TARGET_REG_BITS - 1); - mask = arg_info(op->args[1])->mask << tmp; + z_mask = arg_info(op->args[1])->z_mask << tmp; } break; CASE_OP_32_64(neg): /* Set to 1 all bits to the left of the rightmost. */ - mask = -(arg_info(op->args[1])->mask - & -arg_info(op->args[1])->mask); + z_mask = -(arg_info(op->args[1])->z_mask + & -arg_info(op->args[1])->z_mask); break; CASE_OP_32_64(deposit): - mask = deposit64(arg_info(op->args[1])->mask, - op->args[3], op->args[4], - arg_info(op->args[2])->mask); + z_mask = deposit64(arg_info(op->args[1])->z_mask, + op->args[3], op->args[4], + arg_info(op->args[2])->z_mask); break; CASE_OP_32_64(extract): - mask = extract64(arg_info(op->args[1])->mask, - op->args[2], op->args[3]); + z_mask = extract64(arg_info(op->args[1])->z_mask, + op->args[2], op->args[3]); if (op->args[2] == 0) { - affected = arg_info(op->args[1])->mask & ~mask; + affected = arg_info(op->args[1])->z_mask & ~z_mask; } break; CASE_OP_32_64(sextract): - mask = sextract64(arg_info(op->args[1])->mask, - op->args[2], op->args[3]); - if (op->args[2] == 0 && (tcg_target_long)mask >= 0) { - affected = arg_info(op->args[1])->mask & ~mask; + z_mask = sextract64(arg_info(op->args[1])->z_mask, + op->args[2], op->args[3]); + if (op->args[2] == 0 && (tcg_target_long)z_mask >= 0) { + affected = arg_info(op->args[1])->z_mask & ~z_mask; } break; CASE_OP_32_64(or): CASE_OP_32_64(xor): - mask = arg_info(op->args[1])->mask | arg_info(op->args[2])->mask; + z_mask = arg_info(op->args[1])->z_mask + | arg_info(op->args[2])->z_mask; break; case INDEX_op_clz_i32: case INDEX_op_ctz_i32: - mask = arg_info(op->args[2])->mask | 31; + z_mask = arg_info(op->args[2])->z_mask | 31; break; case INDEX_op_clz_i64: case INDEX_op_ctz_i64: - mask = arg_info(op->args[2])->mask | 63; + z_mask = arg_info(op->args[2])->z_mask | 63; break; case INDEX_op_ctpop_i32: - mask = 32 | 31; + z_mask = 32 | 31; break; case INDEX_op_ctpop_i64: - mask = 64 | 63; + z_mask = 64 | 63; break; CASE_OP_32_64(setcond): case INDEX_op_setcond2_i32: - mask = 1; + z_mask = 1; break; CASE_OP_32_64(movcond): - mask = arg_info(op->args[3])->mask | arg_info(op->args[4])->mask; + z_mask = arg_info(op->args[3])->z_mask + | arg_info(op->args[4])->z_mask; break; CASE_OP_32_64(ld8u): - mask = 0xff; + z_mask = 0xff; break; CASE_OP_32_64(ld16u): - mask = 0xffff; + z_mask = 0xffff; break; case INDEX_op_ld32u_i64: - mask = 0xffffffffu; + z_mask = 0xffffffffu; break; CASE_OP_32_64(qemu_ld): @@ -1026,43 +1028,43 @@ void tcg_optimize(TCGContext *s) MemOpIdx oi = op->args[nb_oargs + nb_iargs]; MemOp mop = get_memop(oi); if (!(mop & MO_SIGN)) { - mask = (2ULL << ((8 << (mop & MO_SIZE)) - 1)) - 1; + z_mask = (2ULL << ((8 << (mop & MO_SIZE)) - 1)) - 1; } } break; CASE_OP_32_64(bswap16): - mask = arg_info(op->args[1])->mask; - if (mask <= 0xffff) { + z_mask = arg_info(op->args[1])->z_mask; + if (z_mask <= 0xffff) { op->args[2] |= TCG_BSWAP_IZ; } - mask = bswap16(mask); + z_mask = bswap16(z_mask); switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { case TCG_BSWAP_OZ: break; case TCG_BSWAP_OS: - mask = (int16_t)mask; + z_mask = (int16_t)z_mask; break; default: /* undefined high bits */ - mask |= MAKE_64BIT_MASK(16, 48); + z_mask |= MAKE_64BIT_MASK(16, 48); break; } break; case INDEX_op_bswap32_i64: - mask = arg_info(op->args[1])->mask; - if (mask <= 0xffffffffu) { + z_mask = arg_info(op->args[1])->z_mask; + if (z_mask <= 0xffffffffu) { op->args[2] |= TCG_BSWAP_IZ; } - mask = bswap32(mask); + z_mask = bswap32(z_mask); switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { case TCG_BSWAP_OZ: break; case TCG_BSWAP_OS: - mask = (int32_t)mask; + z_mask = (int32_t)z_mask; break; default: /* undefined high bits */ - mask |= MAKE_64BIT_MASK(32, 32); + z_mask |= MAKE_64BIT_MASK(32, 32); break; } break; @@ -1074,9 +1076,9 @@ void tcg_optimize(TCGContext *s) /* 32-bit ops generate 32-bit results. For the result is zero test below, we can ignore high bits, but for further optimizations we need to record that the high bits contain garbage. */ - partmask = mask; + partmask = z_mask; if (!(def->flags & TCG_OPF_64BIT)) { - mask |= ~(tcg_target_ulong)0xffffffffu; + z_mask |= ~(tcg_target_ulong)0xffffffffu; partmask &= 0xffffffffu; affected &= 0xffffffffu; } @@ -1472,7 +1474,7 @@ void tcg_optimize(TCGContext *s) vs the high word of the input. */ do_setcond_high: reset_temp(op->args[0]); - arg_info(op->args[0])->mask = 1; + arg_info(op->args[0])->z_mask = 1; op->opc = INDEX_op_setcond_i32; op->args[1] = op->args[2]; op->args[2] = op->args[4]; @@ -1498,7 +1500,7 @@ void tcg_optimize(TCGContext *s) } do_setcond_low: reset_temp(op->args[0]); - arg_info(op->args[0])->mask = 1; + arg_info(op->args[0])->z_mask = 1; op->opc = INDEX_op_setcond_i32; op->args[2] = op->args[3]; op->args[3] = op->args[5]; @@ -1543,7 +1545,7 @@ void tcg_optimize(TCGContext *s) /* Default case: we know nothing about operation (or were unable to compute the operation result) so no propagation is done. We trash everything if the operation is the end of a basic - block, otherwise we only trash the output args. "mask" is + block, otherwise we only trash the output args. "z_mask" is the non-zero bits mask for the first output arg. */ if (def->flags & TCG_OPF_BB_END) { memset(&temps_used, 0, sizeof(temps_used)); @@ -1554,7 +1556,7 @@ void tcg_optimize(TCGContext *s) /* Save the corresponding known-zero bits mask for the first output argument (only one supported so far). */ if (i == 0) { - arg_info(op->args[i])->mask = mask; + arg_info(op->args[i])->z_mask = z_mask; } } } From 3b3f847d754d7f243b2a04775f37c1b324ca1a12 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 23 Aug 2021 22:06:31 -0700 Subject: [PATCH 0782/1334] tcg/optimize: Split out OptContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide what will become a larger context for splitting the very large tcg_optimize function. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 77 ++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 148e360fc6..b76991215e 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -44,6 +44,10 @@ typedef struct TempOptInfo { uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */ } TempOptInfo; +typedef struct OptContext { + TCGTempSet temps_used; +} OptContext; + static inline TempOptInfo *ts_info(TCGTemp *ts) { return ts->state_ptr; @@ -90,15 +94,15 @@ static void reset_temp(TCGArg arg) } /* Initialize and activate a temporary. */ -static void init_ts_info(TCGTempSet *temps_used, TCGTemp *ts) +static void init_ts_info(OptContext *ctx, TCGTemp *ts) { size_t idx = temp_idx(ts); TempOptInfo *ti; - if (test_bit(idx, temps_used->l)) { + if (test_bit(idx, ctx->temps_used.l)) { return; } - set_bit(idx, temps_used->l); + set_bit(idx, ctx->temps_used.l); ti = ts->state_ptr; if (ti == NULL) { @@ -122,9 +126,9 @@ static void init_ts_info(TCGTempSet *temps_used, TCGTemp *ts) } } -static void init_arg_info(TCGTempSet *temps_used, TCGArg arg) +static void init_arg_info(OptContext *ctx, TCGArg arg) { - init_ts_info(temps_used, arg_temp(arg)); + init_ts_info(ctx, arg_temp(arg)); } static TCGTemp *find_better_copy(TCGContext *s, TCGTemp *ts) @@ -229,7 +233,7 @@ static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg dst, TCGArg src) } } -static void tcg_opt_gen_movi(TCGContext *s, TCGTempSet *temps_used, +static void tcg_opt_gen_movi(TCGContext *s, OptContext *ctx, TCGOp *op, TCGArg dst, uint64_t val) { const TCGOpDef *def = &tcg_op_defs[op->opc]; @@ -246,7 +250,7 @@ static void tcg_opt_gen_movi(TCGContext *s, TCGTempSet *temps_used, /* Convert movi to mov with constant temp. */ tv = tcg_constant_internal(type, val); - init_ts_info(temps_used, tv); + init_ts_info(ctx, tv); tcg_opt_gen_mov(s, op, dst, temp_arg(tv)); } @@ -605,7 +609,7 @@ void tcg_optimize(TCGContext *s) { int nb_temps, nb_globals, i; TCGOp *op, *op_next, *prev_mb = NULL; - TCGTempSet temps_used; + OptContext ctx = {}; /* Array VALS has an element for each temp. If this temp holds a constant then its value is kept in VALS' element. @@ -615,7 +619,6 @@ void tcg_optimize(TCGContext *s) nb_temps = s->nb_temps; nb_globals = s->nb_globals; - memset(&temps_used, 0, sizeof(temps_used)); for (i = 0; i < nb_temps; ++i) { s->temps[i].state_ptr = NULL; } @@ -634,14 +637,14 @@ void tcg_optimize(TCGContext *s) for (i = 0; i < nb_oargs + nb_iargs; i++) { TCGTemp *ts = arg_temp(op->args[i]); if (ts) { - init_ts_info(&temps_used, ts); + init_ts_info(&ctx, ts); } } } else { nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; for (i = 0; i < nb_oargs + nb_iargs; i++) { - init_arg_info(&temps_used, op->args[i]); + init_arg_info(&ctx, op->args[i]); } } @@ -720,7 +723,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(rotr): if (arg_is_const(op->args[1]) && arg_info(op->args[1])->val == 0) { - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], 0); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], 0); continue; } break; @@ -1085,7 +1088,7 @@ void tcg_optimize(TCGContext *s) if (partmask == 0) { tcg_debug_assert(nb_oargs == 1); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], 0); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], 0); continue; } if (affected == 0) { @@ -1102,7 +1105,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(mulsh): if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == 0) { - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], 0); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], 0); continue; } break; @@ -1129,7 +1132,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(sub): CASE_OP_32_64_VEC(xor): if (args_are_copies(op->args[1], op->args[2])) { - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], 0); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], 0); continue; } break; @@ -1149,7 +1152,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1])) { tmp = arg_info(op->args[1])->val; tmp = dup_const(TCGOP_VECE(op), tmp); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1157,7 +1160,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_dup2_vec: assert(TCG_TARGET_REG_BITS == 32); if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], + tcg_opt_gen_movi(s, &ctx, op, op->args[0], deposit64(arg_info(op->args[1])->val, 32, 32, arg_info(op->args[2])->val)); break; @@ -1183,7 +1186,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_extrh_i64_i32: if (arg_is_const(op->args[1])) { tmp = do_constant_folding(opc, arg_info(op->args[1])->val, 0); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1194,7 +1197,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1])) { tmp = do_constant_folding(opc, arg_info(op->args[1])->val, op->args[2]); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1224,7 +1227,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { tmp = do_constant_folding(opc, arg_info(op->args[1])->val, arg_info(op->args[2])->val); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1235,7 +1238,7 @@ void tcg_optimize(TCGContext *s) TCGArg v = arg_info(op->args[1])->val; if (v != 0) { tmp = do_constant_folding(opc, v, 0); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); } else { tcg_opt_gen_mov(s, op, op->args[0], op->args[2]); } @@ -1248,7 +1251,7 @@ void tcg_optimize(TCGContext *s) tmp = deposit64(arg_info(op->args[1])->val, op->args[3], op->args[4], arg_info(op->args[2])->val); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1257,7 +1260,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1])) { tmp = extract64(arg_info(op->args[1])->val, op->args[2], op->args[3]); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1266,7 +1269,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1])) { tmp = sextract64(arg_info(op->args[1])->val, op->args[2], op->args[3]); - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1283,7 +1286,7 @@ void tcg_optimize(TCGContext *s) tmp = (int32_t)(((uint32_t)v1 >> shr) | ((uint32_t)v2 << (32 - shr))); } - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1292,7 +1295,7 @@ void tcg_optimize(TCGContext *s) tmp = do_constant_folding_cond(opc, op->args[1], op->args[2], op->args[3]); if (tmp != 2) { - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); break; } goto do_default; @@ -1302,7 +1305,7 @@ void tcg_optimize(TCGContext *s) op->args[1], op->args[2]); if (tmp != 2) { if (tmp) { - memset(&temps_used, 0, sizeof(temps_used)); + memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); op->opc = INDEX_op_br; op->args[0] = op->args[3]; } else { @@ -1358,8 +1361,8 @@ void tcg_optimize(TCGContext *s) rl = op->args[0]; rh = op->args[1]; - tcg_opt_gen_movi(s, &temps_used, op, rl, (int32_t)a); - tcg_opt_gen_movi(s, &temps_used, op2, rh, (int32_t)(a >> 32)); + tcg_opt_gen_movi(s, &ctx, op, rl, (int32_t)a); + tcg_opt_gen_movi(s, &ctx, op2, rh, (int32_t)(a >> 32)); break; } goto do_default; @@ -1374,8 +1377,8 @@ void tcg_optimize(TCGContext *s) rl = op->args[0]; rh = op->args[1]; - tcg_opt_gen_movi(s, &temps_used, op, rl, (int32_t)r); - tcg_opt_gen_movi(s, &temps_used, op2, rh, (int32_t)(r >> 32)); + tcg_opt_gen_movi(s, &ctx, op, rl, (int32_t)r); + tcg_opt_gen_movi(s, &ctx, op2, rh, (int32_t)(r >> 32)); break; } goto do_default; @@ -1386,7 +1389,7 @@ void tcg_optimize(TCGContext *s) if (tmp != 2) { if (tmp) { do_brcond_true: - memset(&temps_used, 0, sizeof(temps_used)); + memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); op->opc = INDEX_op_br; op->args[0] = op->args[5]; } else { @@ -1402,7 +1405,7 @@ void tcg_optimize(TCGContext *s) /* Simplify LT/GE comparisons vs zero to a single compare vs the high word of the input. */ do_brcond_high: - memset(&temps_used, 0, sizeof(temps_used)); + memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); op->opc = INDEX_op_brcond_i32; op->args[0] = op->args[1]; op->args[1] = op->args[3]; @@ -1428,7 +1431,7 @@ void tcg_optimize(TCGContext *s) goto do_default; } do_brcond_low: - memset(&temps_used, 0, sizeof(temps_used)); + memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); op->opc = INDEX_op_brcond_i32; op->args[1] = op->args[2]; op->args[2] = op->args[4]; @@ -1463,7 +1466,7 @@ void tcg_optimize(TCGContext *s) op->args[5]); if (tmp != 2) { do_setcond_const: - tcg_opt_gen_movi(s, &temps_used, op, op->args[0], tmp); + tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); } else if ((op->args[5] == TCG_COND_LT || op->args[5] == TCG_COND_GE) && arg_is_const(op->args[3]) @@ -1533,7 +1536,7 @@ void tcg_optimize(TCGContext *s) if (!(tcg_call_flags(op) & (TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_WRITE_GLOBALS))) { for (i = 0; i < nb_globals; i++) { - if (test_bit(i, temps_used.l)) { + if (test_bit(i, ctx.temps_used.l)) { reset_ts(&s->temps[i]); } } @@ -1548,7 +1551,7 @@ void tcg_optimize(TCGContext *s) block, otherwise we only trash the output args. "z_mask" is the non-zero bits mask for the first output arg. */ if (def->flags & TCG_OPF_BB_END) { - memset(&temps_used, 0, sizeof(temps_used)); + memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); } else { do_reset_output: for (i = 0; i < nb_oargs; i++) { From b10f38339bda94bd960d6936ca6047b34be8eb31 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 23 Aug 2021 22:30:17 -0700 Subject: [PATCH 0783/1334] tcg/optimize: Remove do_default label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break the final cleanup clause out of the main switch statement. When fully folding an opcode to mov/movi, use "continue" to process the next opcode, else break to fall into the final cleanup. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 190 ++++++++++++++++++++++++------------------------- 1 file changed, 94 insertions(+), 96 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index b76991215e..a37efff4d0 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1146,16 +1146,16 @@ void tcg_optimize(TCGContext *s) switch (opc) { CASE_OP_32_64_VEC(mov): tcg_opt_gen_mov(s, op, op->args[0], op->args[1]); - break; + continue; case INDEX_op_dup_vec: if (arg_is_const(op->args[1])) { tmp = arg_info(op->args[1])->val; tmp = dup_const(TCGOP_VECE(op), tmp); tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; case INDEX_op_dup2_vec: assert(TCG_TARGET_REG_BITS == 32); @@ -1163,13 +1163,13 @@ void tcg_optimize(TCGContext *s) tcg_opt_gen_movi(s, &ctx, op, op->args[0], deposit64(arg_info(op->args[1])->val, 32, 32, arg_info(op->args[2])->val)); - break; + continue; } else if (args_are_copies(op->args[1], op->args[2])) { op->opc = INDEX_op_dup_vec; TCGOP_VECE(op) = MO_32; nb_iargs = 1; } - goto do_default; + break; CASE_OP_32_64(not): CASE_OP_32_64(neg): @@ -1187,9 +1187,9 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1])) { tmp = do_constant_folding(opc, arg_info(op->args[1])->val, 0); tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; CASE_OP_32_64(bswap16): CASE_OP_32_64(bswap32): @@ -1198,9 +1198,9 @@ void tcg_optimize(TCGContext *s) tmp = do_constant_folding(opc, arg_info(op->args[1])->val, op->args[2]); tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; CASE_OP_32_64(add): CASE_OP_32_64(sub): @@ -1228,9 +1228,9 @@ void tcg_optimize(TCGContext *s) tmp = do_constant_folding(opc, arg_info(op->args[1])->val, arg_info(op->args[2])->val); tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; CASE_OP_32_64(clz): CASE_OP_32_64(ctz): @@ -1242,9 +1242,9 @@ void tcg_optimize(TCGContext *s) } else { tcg_opt_gen_mov(s, op, op->args[0], op->args[2]); } - break; + continue; } - goto do_default; + break; CASE_OP_32_64(deposit): if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { @@ -1252,27 +1252,27 @@ void tcg_optimize(TCGContext *s) op->args[3], op->args[4], arg_info(op->args[2])->val); tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; CASE_OP_32_64(extract): if (arg_is_const(op->args[1])) { tmp = extract64(arg_info(op->args[1])->val, op->args[2], op->args[3]); tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; CASE_OP_32_64(sextract): if (arg_is_const(op->args[1])) { tmp = sextract64(arg_info(op->args[1])->val, op->args[2], op->args[3]); tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; CASE_OP_32_64(extract2): if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { @@ -1287,40 +1287,40 @@ void tcg_optimize(TCGContext *s) ((uint32_t)v2 << (32 - shr))); } tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; CASE_OP_32_64(setcond): tmp = do_constant_folding_cond(opc, op->args[1], op->args[2], op->args[3]); if (tmp != 2) { tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - break; + continue; } - goto do_default; + break; CASE_OP_32_64(brcond): tmp = do_constant_folding_cond(opc, op->args[0], op->args[1], op->args[2]); - if (tmp != 2) { - if (tmp) { - memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); - op->opc = INDEX_op_br; - op->args[0] = op->args[3]; - } else { - tcg_op_remove(s, op); - } + switch (tmp) { + case 0: + tcg_op_remove(s, op); + continue; + case 1: + memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); + op->opc = opc = INDEX_op_br; + op->args[0] = op->args[3]; break; } - goto do_default; + break; CASE_OP_32_64(movcond): tmp = do_constant_folding_cond(opc, op->args[1], op->args[2], op->args[5]); if (tmp != 2) { tcg_opt_gen_mov(s, op, op->args[0], op->args[4-tmp]); - break; + continue; } if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { uint64_t tv = arg_info(op->args[3])->val; @@ -1330,7 +1330,7 @@ void tcg_optimize(TCGContext *s) if (fv == 1 && tv == 0) { cond = tcg_invert_cond(cond); } else if (!(tv == 1 && fv == 0)) { - goto do_default; + break; } op->args[3] = cond; op->opc = opc = (opc == INDEX_op_movcond_i32 @@ -1338,7 +1338,7 @@ void tcg_optimize(TCGContext *s) : INDEX_op_setcond_i64); nb_iargs = 2; } - goto do_default; + break; case INDEX_op_add2_i32: case INDEX_op_sub2_i32: @@ -1363,9 +1363,9 @@ void tcg_optimize(TCGContext *s) rh = op->args[1]; tcg_opt_gen_movi(s, &ctx, op, rl, (int32_t)a); tcg_opt_gen_movi(s, &ctx, op2, rh, (int32_t)(a >> 32)); - break; + continue; } - goto do_default; + break; case INDEX_op_mulu2_i32: if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { @@ -1379,39 +1379,40 @@ void tcg_optimize(TCGContext *s) rh = op->args[1]; tcg_opt_gen_movi(s, &ctx, op, rl, (int32_t)r); tcg_opt_gen_movi(s, &ctx, op2, rh, (int32_t)(r >> 32)); - break; + continue; } - goto do_default; + break; case INDEX_op_brcond2_i32: tmp = do_constant_folding_cond2(&op->args[0], &op->args[2], op->args[4]); - if (tmp != 2) { - if (tmp) { - do_brcond_true: - memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); - op->opc = INDEX_op_br; - op->args[0] = op->args[5]; - } else { + if (tmp == 0) { do_brcond_false: - tcg_op_remove(s, op); - } - } else if ((op->args[4] == TCG_COND_LT - || op->args[4] == TCG_COND_GE) - && arg_is_const(op->args[2]) - && arg_info(op->args[2])->val == 0 - && arg_is_const(op->args[3]) - && arg_info(op->args[3])->val == 0) { + tcg_op_remove(s, op); + continue; + } + if (tmp == 1) { + do_brcond_true: + op->opc = opc = INDEX_op_br; + op->args[0] = op->args[5]; + break; + } + if ((op->args[4] == TCG_COND_LT || op->args[4] == TCG_COND_GE) + && arg_is_const(op->args[2]) + && arg_info(op->args[2])->val == 0 + && arg_is_const(op->args[3]) + && arg_info(op->args[3])->val == 0) { /* Simplify LT/GE comparisons vs zero to a single compare vs the high word of the input. */ do_brcond_high: - memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); - op->opc = INDEX_op_brcond_i32; + op->opc = opc = INDEX_op_brcond_i32; op->args[0] = op->args[1]; op->args[1] = op->args[3]; op->args[2] = op->args[4]; op->args[3] = op->args[5]; - } else if (op->args[4] == TCG_COND_EQ) { + break; + } + if (op->args[4] == TCG_COND_EQ) { /* Simplify EQ comparisons where one of the pairs can be simplified. */ tmp = do_constant_folding_cond(INDEX_op_brcond_i32, @@ -1428,7 +1429,7 @@ void tcg_optimize(TCGContext *s) if (tmp == 0) { goto do_brcond_false; } else if (tmp != 1) { - goto do_default; + break; } do_brcond_low: memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); @@ -1436,7 +1437,9 @@ void tcg_optimize(TCGContext *s) op->args[1] = op->args[2]; op->args[2] = op->args[4]; op->args[3] = op->args[5]; - } else if (op->args[4] == TCG_COND_NE) { + break; + } + if (op->args[4] == TCG_COND_NE) { /* Simplify NE comparisons where one of the pairs can be simplified. */ tmp = do_constant_folding_cond(INDEX_op_brcond_i32, @@ -1455,9 +1458,6 @@ void tcg_optimize(TCGContext *s) } else if (tmp == 1) { goto do_brcond_true; } - goto do_default; - } else { - goto do_default; } break; @@ -1467,12 +1467,13 @@ void tcg_optimize(TCGContext *s) if (tmp != 2) { do_setcond_const: tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); - } else if ((op->args[5] == TCG_COND_LT - || op->args[5] == TCG_COND_GE) - && arg_is_const(op->args[3]) - && arg_info(op->args[3])->val == 0 - && arg_is_const(op->args[4]) - && arg_info(op->args[4])->val == 0) { + continue; + } + if ((op->args[5] == TCG_COND_LT || op->args[5] == TCG_COND_GE) + && arg_is_const(op->args[3]) + && arg_info(op->args[3])->val == 0 + && arg_is_const(op->args[4]) + && arg_info(op->args[4])->val == 0) { /* Simplify LT/GE comparisons vs zero to a single compare vs the high word of the input. */ do_setcond_high: @@ -1482,7 +1483,9 @@ void tcg_optimize(TCGContext *s) op->args[1] = op->args[2]; op->args[2] = op->args[4]; op->args[3] = op->args[5]; - } else if (op->args[5] == TCG_COND_EQ) { + break; + } + if (op->args[5] == TCG_COND_EQ) { /* Simplify EQ comparisons where one of the pairs can be simplified. */ tmp = do_constant_folding_cond(INDEX_op_setcond_i32, @@ -1499,7 +1502,7 @@ void tcg_optimize(TCGContext *s) if (tmp == 0) { goto do_setcond_high; } else if (tmp != 1) { - goto do_default; + break; } do_setcond_low: reset_temp(op->args[0]); @@ -1507,7 +1510,9 @@ void tcg_optimize(TCGContext *s) op->opc = INDEX_op_setcond_i32; op->args[2] = op->args[3]; op->args[3] = op->args[5]; - } else if (op->args[5] == TCG_COND_NE) { + break; + } + if (op->args[5] == TCG_COND_NE) { /* Simplify NE comparisons where one of the pairs can be simplified. */ tmp = do_constant_folding_cond(INDEX_op_setcond_i32, @@ -1526,14 +1531,21 @@ void tcg_optimize(TCGContext *s) } else if (tmp == 1) { goto do_setcond_const; } - goto do_default; - } else { - goto do_default; } break; - case INDEX_op_call: - if (!(tcg_call_flags(op) + default: + break; + } + + /* Some of the folding above can change opc. */ + opc = op->opc; + def = &tcg_op_defs[opc]; + if (def->flags & TCG_OPF_BB_END) { + memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); + } else { + if (opc == INDEX_op_call && + !(tcg_call_flags(op) & (TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_WRITE_GLOBALS))) { for (i = 0; i < nb_globals; i++) { if (test_bit(i, ctx.temps_used.l)) { @@ -1541,29 +1553,15 @@ void tcg_optimize(TCGContext *s) } } } - goto do_reset_output; - default: - do_default: - /* Default case: we know nothing about operation (or were unable - to compute the operation result) so no propagation is done. - We trash everything if the operation is the end of a basic - block, otherwise we only trash the output args. "z_mask" is - the non-zero bits mask for the first output arg. */ - if (def->flags & TCG_OPF_BB_END) { - memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); - } else { - do_reset_output: - for (i = 0; i < nb_oargs; i++) { - reset_temp(op->args[i]); - /* Save the corresponding known-zero bits mask for the - first output argument (only one supported so far). */ - if (i == 0) { - arg_info(op->args[i])->z_mask = z_mask; - } + for (i = 0; i < nb_oargs; i++) { + reset_temp(op->args[i]); + /* Save the corresponding known-zero bits mask for the + first output argument (only one supported so far). */ + if (i == 0) { + arg_info(op->args[i])->z_mask = z_mask; } } - break; } /* Eliminate duplicate and redundant fence instructions. */ From dc84988a5f4147b8c1f90ed4cdcf5c57f06749cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 07:13:45 -0700 Subject: [PATCH 0784/1334] tcg/optimize: Change tcg_opt_gen_{mov,movi} interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adjust the interface to take the OptContext parameter instead of TCGContext or both. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 67 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index a37efff4d0..627a5b39f6 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -45,6 +45,7 @@ typedef struct TempOptInfo { } TempOptInfo; typedef struct OptContext { + TCGContext *tcg; TCGTempSet temps_used; } OptContext; @@ -183,7 +184,7 @@ static bool args_are_copies(TCGArg arg1, TCGArg arg2) return ts_are_copies(arg_temp(arg1), arg_temp(arg2)); } -static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg dst, TCGArg src) +static void tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) { TCGTemp *dst_ts = arg_temp(dst); TCGTemp *src_ts = arg_temp(src); @@ -194,7 +195,7 @@ static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg dst, TCGArg src) TCGOpcode new_op; if (ts_are_copies(dst_ts, src_ts)) { - tcg_op_remove(s, op); + tcg_op_remove(ctx->tcg, op); return; } @@ -233,8 +234,8 @@ static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg dst, TCGArg src) } } -static void tcg_opt_gen_movi(TCGContext *s, OptContext *ctx, - TCGOp *op, TCGArg dst, uint64_t val) +static void tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, + TCGArg dst, uint64_t val) { const TCGOpDef *def = &tcg_op_defs[op->opc]; TCGType type; @@ -251,7 +252,7 @@ static void tcg_opt_gen_movi(TCGContext *s, OptContext *ctx, /* Convert movi to mov with constant temp. */ tv = tcg_constant_internal(type, val); init_ts_info(ctx, tv); - tcg_opt_gen_mov(s, op, dst, temp_arg(tv)); + tcg_opt_gen_mov(ctx, op, dst, temp_arg(tv)); } static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) @@ -609,7 +610,7 @@ void tcg_optimize(TCGContext *s) { int nb_temps, nb_globals, i; TCGOp *op, *op_next, *prev_mb = NULL; - OptContext ctx = {}; + OptContext ctx = { .tcg = s }; /* Array VALS has an element for each temp. If this temp holds a constant then its value is kept in VALS' element. @@ -723,7 +724,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(rotr): if (arg_is_const(op->args[1]) && arg_info(op->args[1])->val == 0) { - tcg_opt_gen_movi(s, &ctx, op, op->args[0], 0); + tcg_opt_gen_movi(&ctx, op, op->args[0], 0); continue; } break; @@ -838,7 +839,7 @@ void tcg_optimize(TCGContext *s) if (!arg_is_const(op->args[1]) && arg_is_const(op->args[2]) && arg_info(op->args[2])->val == 0) { - tcg_opt_gen_mov(s, op, op->args[0], op->args[1]); + tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); continue; } break; @@ -848,7 +849,7 @@ void tcg_optimize(TCGContext *s) if (!arg_is_const(op->args[1]) && arg_is_const(op->args[2]) && arg_info(op->args[2])->val == -1) { - tcg_opt_gen_mov(s, op, op->args[0], op->args[1]); + tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); continue; } break; @@ -1088,12 +1089,12 @@ void tcg_optimize(TCGContext *s) if (partmask == 0) { tcg_debug_assert(nb_oargs == 1); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], 0); + tcg_opt_gen_movi(&ctx, op, op->args[0], 0); continue; } if (affected == 0) { tcg_debug_assert(nb_oargs == 1); - tcg_opt_gen_mov(s, op, op->args[0], op->args[1]); + tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); continue; } @@ -1105,7 +1106,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(mulsh): if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == 0) { - tcg_opt_gen_movi(s, &ctx, op, op->args[0], 0); + tcg_opt_gen_movi(&ctx, op, op->args[0], 0); continue; } break; @@ -1118,7 +1119,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(or): CASE_OP_32_64_VEC(and): if (args_are_copies(op->args[1], op->args[2])) { - tcg_opt_gen_mov(s, op, op->args[0], op->args[1]); + tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); continue; } break; @@ -1132,7 +1133,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(sub): CASE_OP_32_64_VEC(xor): if (args_are_copies(op->args[1], op->args[2])) { - tcg_opt_gen_movi(s, &ctx, op, op->args[0], 0); + tcg_opt_gen_movi(&ctx, op, op->args[0], 0); continue; } break; @@ -1145,14 +1146,14 @@ void tcg_optimize(TCGContext *s) allocator where needed and possible. Also detect copies. */ switch (opc) { CASE_OP_32_64_VEC(mov): - tcg_opt_gen_mov(s, op, op->args[0], op->args[1]); + tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); continue; case INDEX_op_dup_vec: if (arg_is_const(op->args[1])) { tmp = arg_info(op->args[1])->val; tmp = dup_const(TCGOP_VECE(op), tmp); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1160,7 +1161,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_dup2_vec: assert(TCG_TARGET_REG_BITS == 32); if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - tcg_opt_gen_movi(s, &ctx, op, op->args[0], + tcg_opt_gen_movi(&ctx, op, op->args[0], deposit64(arg_info(op->args[1])->val, 32, 32, arg_info(op->args[2])->val)); continue; @@ -1186,7 +1187,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_extrh_i64_i32: if (arg_is_const(op->args[1])) { tmp = do_constant_folding(opc, arg_info(op->args[1])->val, 0); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1197,7 +1198,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1])) { tmp = do_constant_folding(opc, arg_info(op->args[1])->val, op->args[2]); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1227,7 +1228,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { tmp = do_constant_folding(opc, arg_info(op->args[1])->val, arg_info(op->args[2])->val); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1238,9 +1239,9 @@ void tcg_optimize(TCGContext *s) TCGArg v = arg_info(op->args[1])->val; if (v != 0) { tmp = do_constant_folding(opc, v, 0); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); } else { - tcg_opt_gen_mov(s, op, op->args[0], op->args[2]); + tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[2]); } continue; } @@ -1251,7 +1252,7 @@ void tcg_optimize(TCGContext *s) tmp = deposit64(arg_info(op->args[1])->val, op->args[3], op->args[4], arg_info(op->args[2])->val); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1260,7 +1261,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1])) { tmp = extract64(arg_info(op->args[1])->val, op->args[2], op->args[3]); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1269,7 +1270,7 @@ void tcg_optimize(TCGContext *s) if (arg_is_const(op->args[1])) { tmp = sextract64(arg_info(op->args[1])->val, op->args[2], op->args[3]); - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1286,7 +1287,7 @@ void tcg_optimize(TCGContext *s) tmp = (int32_t)(((uint32_t)v1 >> shr) | ((uint32_t)v2 << (32 - shr))); } - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1295,7 +1296,7 @@ void tcg_optimize(TCGContext *s) tmp = do_constant_folding_cond(opc, op->args[1], op->args[2], op->args[3]); if (tmp != 2) { - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } break; @@ -1319,7 +1320,7 @@ void tcg_optimize(TCGContext *s) tmp = do_constant_folding_cond(opc, op->args[1], op->args[2], op->args[5]); if (tmp != 2) { - tcg_opt_gen_mov(s, op, op->args[0], op->args[4-tmp]); + tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[4-tmp]); continue; } if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { @@ -1361,8 +1362,8 @@ void tcg_optimize(TCGContext *s) rl = op->args[0]; rh = op->args[1]; - tcg_opt_gen_movi(s, &ctx, op, rl, (int32_t)a); - tcg_opt_gen_movi(s, &ctx, op2, rh, (int32_t)(a >> 32)); + tcg_opt_gen_movi(&ctx, op, rl, (int32_t)a); + tcg_opt_gen_movi(&ctx, op2, rh, (int32_t)(a >> 32)); continue; } break; @@ -1377,8 +1378,8 @@ void tcg_optimize(TCGContext *s) rl = op->args[0]; rh = op->args[1]; - tcg_opt_gen_movi(s, &ctx, op, rl, (int32_t)r); - tcg_opt_gen_movi(s, &ctx, op2, rh, (int32_t)(r >> 32)); + tcg_opt_gen_movi(&ctx, op, rl, (int32_t)r); + tcg_opt_gen_movi(&ctx, op2, rh, (int32_t)(r >> 32)); continue; } break; @@ -1466,7 +1467,7 @@ void tcg_optimize(TCGContext *s) op->args[5]); if (tmp != 2) { do_setcond_const: - tcg_opt_gen_movi(s, &ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); continue; } if ((op->args[5] == TCG_COND_LT || op->args[5] == TCG_COND_GE) From d0ed5151b11b12e9e2ca3c9adde2fd4444588948 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 07:38:39 -0700 Subject: [PATCH 0785/1334] tcg/optimize: Move prev_mb into OptContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will expose the variable to subroutines that will be broken out of tcg_optimize. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 627a5b39f6..b875d76354 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -46,6 +46,7 @@ typedef struct TempOptInfo { typedef struct OptContext { TCGContext *tcg; + TCGOp *prev_mb; TCGTempSet temps_used; } OptContext; @@ -609,7 +610,7 @@ static bool swap_commutative2(TCGArg *p1, TCGArg *p2) void tcg_optimize(TCGContext *s) { int nb_temps, nb_globals, i; - TCGOp *op, *op_next, *prev_mb = NULL; + TCGOp *op, *op_next; OptContext ctx = { .tcg = s }; /* Array VALS has an element for each temp. @@ -1566,7 +1567,7 @@ void tcg_optimize(TCGContext *s) } /* Eliminate duplicate and redundant fence instructions. */ - if (prev_mb) { + if (ctx.prev_mb) { switch (opc) { case INDEX_op_mb: /* Merge two barriers of the same type into one, @@ -1580,7 +1581,7 @@ void tcg_optimize(TCGContext *s) * barrier. This is stricter than specified but for * the purposes of TCG is better than not optimizing. */ - prev_mb->args[0] |= op->args[0]; + ctx.prev_mb->args[0] |= op->args[0]; tcg_op_remove(s, op); break; @@ -1597,11 +1598,11 @@ void tcg_optimize(TCGContext *s) case INDEX_op_qemu_st_i64: case INDEX_op_call: /* Opcodes that touch guest memory stop the optimization. */ - prev_mb = NULL; + ctx.prev_mb = NULL; break; } } else if (opc == INDEX_op_mb) { - prev_mb = op; + ctx.prev_mb = op; } } } From e2577ea24f9974ab0fb2a2b255203bad0c878f91 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 08:00:48 -0700 Subject: [PATCH 0786/1334] tcg/optimize: Split out init_arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There was no real reason for calls to have separate code here. Unify init for calls vs non-calls using the call path, which handles TCG_CALL_DUMMY_ARG. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index b875d76354..019c5aaf81 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -128,11 +128,6 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) } } -static void init_arg_info(OptContext *ctx, TCGArg arg) -{ - init_ts_info(ctx, arg_temp(arg)); -} - static TCGTemp *find_better_copy(TCGContext *s, TCGTemp *ts) { TCGTemp *i, *g, *l; @@ -606,6 +601,16 @@ static bool swap_commutative2(TCGArg *p1, TCGArg *p2) return false; } +static void init_arguments(OptContext *ctx, TCGOp *op, int nb_args) +{ + for (int i = 0; i < nb_args; i++) { + TCGTemp *ts = arg_temp(op->args[i]); + if (ts) { + init_ts_info(ctx, ts); + } + } +} + /* Propagate constants and copies, fold constant expressions. */ void tcg_optimize(TCGContext *s) { @@ -636,19 +641,11 @@ void tcg_optimize(TCGContext *s) if (opc == INDEX_op_call) { nb_oargs = TCGOP_CALLO(op); nb_iargs = TCGOP_CALLI(op); - for (i = 0; i < nb_oargs + nb_iargs; i++) { - TCGTemp *ts = arg_temp(op->args[i]); - if (ts) { - init_ts_info(&ctx, ts); - } - } } else { nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; - for (i = 0; i < nb_oargs + nb_iargs; i++) { - init_arg_info(&ctx, op->args[i]); - } } + init_arguments(&ctx, op, nb_oargs + nb_iargs); /* Do copy propagation */ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { From 8774dded029c96130aacf6e6bb71b70cf271b8df Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 08:04:47 -0700 Subject: [PATCH 0787/1334] tcg/optimize: Split out copy_propagate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Continue splitting tcg_optimize. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 019c5aaf81..fad6f5de1f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -611,6 +611,19 @@ static void init_arguments(OptContext *ctx, TCGOp *op, int nb_args) } } +static void copy_propagate(OptContext *ctx, TCGOp *op, + int nb_oargs, int nb_iargs) +{ + TCGContext *s = ctx->tcg; + + for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + TCGTemp *ts = arg_temp(op->args[i]); + if (ts && ts_is_copy(ts)) { + op->args[i] = temp_arg(find_better_copy(s, ts)); + } + } +} + /* Propagate constants and copies, fold constant expressions. */ void tcg_optimize(TCGContext *s) { @@ -646,14 +659,7 @@ void tcg_optimize(TCGContext *s) nb_iargs = def->nb_iargs; } init_arguments(&ctx, op, nb_oargs + nb_iargs); - - /* Do copy propagation */ - for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { - TCGTemp *ts = arg_temp(op->args[i]); - if (ts && ts_is_copy(ts)) { - op->args[i] = temp_arg(find_better_copy(s, ts)); - } - } + copy_propagate(&ctx, op, nb_oargs, nb_iargs); /* For commutative operations make constant second argument */ switch (opc) { From 5cf32be7d8f385d1599655fdb9de87996243f33b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 08:17:08 -0700 Subject: [PATCH 0788/1334] tcg/optimize: Split out fold_call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calls are special in that they have a variable number of arguments, and need to be able to clobber globals. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 63 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index fad6f5de1f..74b9aa025a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -624,10 +624,42 @@ static void copy_propagate(OptContext *ctx, TCGOp *op, } } +static bool fold_call(OptContext *ctx, TCGOp *op) +{ + TCGContext *s = ctx->tcg; + int nb_oargs = TCGOP_CALLO(op); + int nb_iargs = TCGOP_CALLI(op); + int flags, i; + + init_arguments(ctx, op, nb_oargs + nb_iargs); + copy_propagate(ctx, op, nb_oargs, nb_iargs); + + /* If the function reads or writes globals, reset temp data. */ + flags = tcg_call_flags(op); + if (!(flags & (TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_WRITE_GLOBALS))) { + int nb_globals = s->nb_globals; + + for (i = 0; i < nb_globals; i++) { + if (test_bit(i, ctx->temps_used.l)) { + reset_ts(&ctx->tcg->temps[i]); + } + } + } + + /* Reset temp data for outputs. */ + for (i = 0; i < nb_oargs; i++) { + reset_temp(op->args[i]); + } + + /* Stop optimizing MB across calls. */ + ctx->prev_mb = NULL; + return true; +} + /* Propagate constants and copies, fold constant expressions. */ void tcg_optimize(TCGContext *s) { - int nb_temps, nb_globals, i; + int nb_temps, i; TCGOp *op, *op_next; OptContext ctx = { .tcg = s }; @@ -637,8 +669,6 @@ void tcg_optimize(TCGContext *s) available through the doubly linked circular list. */ nb_temps = s->nb_temps; - nb_globals = s->nb_globals; - for (i = 0; i < nb_temps; ++i) { s->temps[i].state_ptr = NULL; } @@ -647,17 +677,17 @@ void tcg_optimize(TCGContext *s) uint64_t z_mask, partmask, affected, tmp; int nb_oargs, nb_iargs; TCGOpcode opc = op->opc; - const TCGOpDef *def = &tcg_op_defs[opc]; + const TCGOpDef *def; - /* Count the arguments, and initialize the temps that are - going to be used */ + /* Calls are special. */ if (opc == INDEX_op_call) { - nb_oargs = TCGOP_CALLO(op); - nb_iargs = TCGOP_CALLI(op); - } else { - nb_oargs = def->nb_oargs; - nb_iargs = def->nb_iargs; + fold_call(&ctx, op); + continue; } + + def = &tcg_op_defs[opc]; + nb_oargs = def->nb_oargs; + nb_iargs = def->nb_iargs; init_arguments(&ctx, op, nb_oargs + nb_iargs); copy_propagate(&ctx, op, nb_oargs, nb_iargs); @@ -1549,16 +1579,6 @@ void tcg_optimize(TCGContext *s) if (def->flags & TCG_OPF_BB_END) { memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); } else { - if (opc == INDEX_op_call && - !(tcg_call_flags(op) - & (TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_WRITE_GLOBALS))) { - for (i = 0; i < nb_globals; i++) { - if (test_bit(i, ctx.temps_used.l)) { - reset_ts(&s->temps[i]); - } - } - } - for (i = 0; i < nb_oargs; i++) { reset_temp(op->args[i]); /* Save the corresponding known-zero bits mask for the @@ -1599,7 +1619,6 @@ void tcg_optimize(TCGContext *s) case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st8_i32: case INDEX_op_qemu_st_i64: - case INDEX_op_call: /* Opcodes that touch guest memory stop the optimization. */ ctx.prev_mb = NULL; break; From ec5d4cbeef2749c9137daeac1b7f67735510675b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 08:20:27 -0700 Subject: [PATCH 0789/1334] tcg/optimize: Drop nb_oargs, nb_iargs locals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than try to keep these up-to-date across folding, re-read nb_oargs at the end, after re-reading the opcode. A couple of asserts need dropping, but that will take care of itself as we split the function further. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 74b9aa025a..77cdffaaef 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -675,7 +675,6 @@ void tcg_optimize(TCGContext *s) QTAILQ_FOREACH_SAFE(op, &s->ops, link, op_next) { uint64_t z_mask, partmask, affected, tmp; - int nb_oargs, nb_iargs; TCGOpcode opc = op->opc; const TCGOpDef *def; @@ -686,10 +685,8 @@ void tcg_optimize(TCGContext *s) } def = &tcg_op_defs[opc]; - nb_oargs = def->nb_oargs; - nb_iargs = def->nb_iargs; - init_arguments(&ctx, op, nb_oargs + nb_iargs); - copy_propagate(&ctx, op, nb_oargs, nb_iargs); + init_arguments(&ctx, op, def->nb_oargs + def->nb_iargs); + copy_propagate(&ctx, op, def->nb_oargs, def->nb_iargs); /* For commutative operations make constant second argument */ switch (opc) { @@ -1063,7 +1060,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(qemu_ld): { - MemOpIdx oi = op->args[nb_oargs + nb_iargs]; + MemOpIdx oi = op->args[def->nb_oargs + def->nb_iargs]; MemOp mop = get_memop(oi); if (!(mop & MO_SIGN)) { z_mask = (2ULL << ((8 << (mop & MO_SIZE)) - 1)) - 1; @@ -1122,12 +1119,10 @@ void tcg_optimize(TCGContext *s) } if (partmask == 0) { - tcg_debug_assert(nb_oargs == 1); tcg_opt_gen_movi(&ctx, op, op->args[0], 0); continue; } if (affected == 0) { - tcg_debug_assert(nb_oargs == 1); tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); continue; } @@ -1202,7 +1197,6 @@ void tcg_optimize(TCGContext *s) } else if (args_are_copies(op->args[1], op->args[2])) { op->opc = INDEX_op_dup_vec; TCGOP_VECE(op) = MO_32; - nb_iargs = 1; } break; @@ -1371,7 +1365,6 @@ void tcg_optimize(TCGContext *s) op->opc = opc = (opc == INDEX_op_movcond_i32 ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64); - nb_iargs = 2; } break; @@ -1579,6 +1572,7 @@ void tcg_optimize(TCGContext *s) if (def->flags & TCG_OPF_BB_END) { memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); } else { + int nb_oargs = def->nb_oargs; for (i = 0; i < nb_oargs; i++) { reset_temp(op->args[i]); /* Save the corresponding known-zero bits mask for the From 8d57bf1e82da9c1a71c16b12545a9e8d347690f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 08:34:27 -0700 Subject: [PATCH 0790/1334] tcg/optimize: Change fail return for do_constant_folding_cond* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return -1 instead of 2 for failure, so that we can use comparisons against 0 for all cases. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 145 +++++++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 77cdffaaef..19c01687b4 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -502,10 +502,12 @@ static bool do_constant_folding_cond_eq(TCGCond c) } } -/* Return 2 if the condition can't be simplified, and the result - of the condition (0 or 1) if it can */ -static TCGArg do_constant_folding_cond(TCGOpcode op, TCGArg x, - TCGArg y, TCGCond c) +/* + * Return -1 if the condition can't be simplified, + * and the result of the condition (0 or 1) if it can. + */ +static int do_constant_folding_cond(TCGOpcode op, TCGArg x, + TCGArg y, TCGCond c) { uint64_t xv = arg_info(x)->val; uint64_t yv = arg_info(y)->val; @@ -527,15 +529,17 @@ static TCGArg do_constant_folding_cond(TCGOpcode op, TCGArg x, case TCG_COND_GEU: return 1; default: - return 2; + return -1; } } - return 2; + return -1; } -/* Return 2 if the condition can't be simplified, and the result - of the condition (0 or 1) if it can */ -static TCGArg do_constant_folding_cond2(TCGArg *p1, TCGArg *p2, TCGCond c) +/* + * Return -1 if the condition can't be simplified, + * and the result of the condition (0 or 1) if it can. + */ +static int do_constant_folding_cond2(TCGArg *p1, TCGArg *p2, TCGCond c) { TCGArg al = p1[0], ah = p1[1]; TCGArg bl = p2[0], bh = p2[1]; @@ -565,7 +569,7 @@ static TCGArg do_constant_folding_cond2(TCGArg *p1, TCGArg *p2, TCGCond c) if (args_are_copies(al, bl) && args_are_copies(ah, bh)) { return do_constant_folding_cond_eq(c); } - return 2; + return -1; } static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) @@ -1321,22 +1325,21 @@ void tcg_optimize(TCGContext *s) break; CASE_OP_32_64(setcond): - tmp = do_constant_folding_cond(opc, op->args[1], - op->args[2], op->args[3]); - if (tmp != 2) { - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); + i = do_constant_folding_cond(opc, op->args[1], + op->args[2], op->args[3]); + if (i >= 0) { + tcg_opt_gen_movi(&ctx, op, op->args[0], i); continue; } break; CASE_OP_32_64(brcond): - tmp = do_constant_folding_cond(opc, op->args[0], - op->args[1], op->args[2]); - switch (tmp) { - case 0: + i = do_constant_folding_cond(opc, op->args[0], + op->args[1], op->args[2]); + if (i == 0) { tcg_op_remove(s, op); continue; - case 1: + } else if (i > 0) { memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); op->opc = opc = INDEX_op_br; op->args[0] = op->args[3]; @@ -1345,10 +1348,10 @@ void tcg_optimize(TCGContext *s) break; CASE_OP_32_64(movcond): - tmp = do_constant_folding_cond(opc, op->args[1], - op->args[2], op->args[5]); - if (tmp != 2) { - tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[4-tmp]); + i = do_constant_folding_cond(opc, op->args[1], + op->args[2], op->args[5]); + if (i >= 0) { + tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[4 - i]); continue; } if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { @@ -1412,14 +1415,14 @@ void tcg_optimize(TCGContext *s) break; case INDEX_op_brcond2_i32: - tmp = do_constant_folding_cond2(&op->args[0], &op->args[2], - op->args[4]); - if (tmp == 0) { + i = do_constant_folding_cond2(&op->args[0], &op->args[2], + op->args[4]); + if (i == 0) { do_brcond_false: tcg_op_remove(s, op); continue; } - if (tmp == 1) { + if (i > 0) { do_brcond_true: op->opc = opc = INDEX_op_br; op->args[0] = op->args[5]; @@ -1443,20 +1446,20 @@ void tcg_optimize(TCGContext *s) if (op->args[4] == TCG_COND_EQ) { /* Simplify EQ comparisons where one of the pairs can be simplified. */ - tmp = do_constant_folding_cond(INDEX_op_brcond_i32, - op->args[0], op->args[2], - TCG_COND_EQ); - if (tmp == 0) { + i = do_constant_folding_cond(INDEX_op_brcond_i32, + op->args[0], op->args[2], + TCG_COND_EQ); + if (i == 0) { goto do_brcond_false; - } else if (tmp == 1) { + } else if (i > 0) { goto do_brcond_high; } - tmp = do_constant_folding_cond(INDEX_op_brcond_i32, - op->args[1], op->args[3], - TCG_COND_EQ); - if (tmp == 0) { + i = do_constant_folding_cond(INDEX_op_brcond_i32, + op->args[1], op->args[3], + TCG_COND_EQ); + if (i == 0) { goto do_brcond_false; - } else if (tmp != 1) { + } else if (i < 0) { break; } do_brcond_low: @@ -1470,31 +1473,31 @@ void tcg_optimize(TCGContext *s) if (op->args[4] == TCG_COND_NE) { /* Simplify NE comparisons where one of the pairs can be simplified. */ - tmp = do_constant_folding_cond(INDEX_op_brcond_i32, - op->args[0], op->args[2], - TCG_COND_NE); - if (tmp == 0) { + i = do_constant_folding_cond(INDEX_op_brcond_i32, + op->args[0], op->args[2], + TCG_COND_NE); + if (i == 0) { goto do_brcond_high; - } else if (tmp == 1) { + } else if (i > 0) { goto do_brcond_true; } - tmp = do_constant_folding_cond(INDEX_op_brcond_i32, - op->args[1], op->args[3], - TCG_COND_NE); - if (tmp == 0) { + i = do_constant_folding_cond(INDEX_op_brcond_i32, + op->args[1], op->args[3], + TCG_COND_NE); + if (i == 0) { goto do_brcond_low; - } else if (tmp == 1) { + } else if (i > 0) { goto do_brcond_true; } } break; case INDEX_op_setcond2_i32: - tmp = do_constant_folding_cond2(&op->args[1], &op->args[3], - op->args[5]); - if (tmp != 2) { + i = do_constant_folding_cond2(&op->args[1], &op->args[3], + op->args[5]); + if (i >= 0) { do_setcond_const: - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); + tcg_opt_gen_movi(&ctx, op, op->args[0], i); continue; } if ((op->args[5] == TCG_COND_LT || op->args[5] == TCG_COND_GE) @@ -1516,20 +1519,20 @@ void tcg_optimize(TCGContext *s) if (op->args[5] == TCG_COND_EQ) { /* Simplify EQ comparisons where one of the pairs can be simplified. */ - tmp = do_constant_folding_cond(INDEX_op_setcond_i32, - op->args[1], op->args[3], - TCG_COND_EQ); - if (tmp == 0) { + i = do_constant_folding_cond(INDEX_op_setcond_i32, + op->args[1], op->args[3], + TCG_COND_EQ); + if (i == 0) { goto do_setcond_const; - } else if (tmp == 1) { + } else if (i > 0) { goto do_setcond_high; } - tmp = do_constant_folding_cond(INDEX_op_setcond_i32, - op->args[2], op->args[4], - TCG_COND_EQ); - if (tmp == 0) { + i = do_constant_folding_cond(INDEX_op_setcond_i32, + op->args[2], op->args[4], + TCG_COND_EQ); + if (i == 0) { goto do_setcond_high; - } else if (tmp != 1) { + } else if (i < 0) { break; } do_setcond_low: @@ -1543,20 +1546,20 @@ void tcg_optimize(TCGContext *s) if (op->args[5] == TCG_COND_NE) { /* Simplify NE comparisons where one of the pairs can be simplified. */ - tmp = do_constant_folding_cond(INDEX_op_setcond_i32, - op->args[1], op->args[3], - TCG_COND_NE); - if (tmp == 0) { + i = do_constant_folding_cond(INDEX_op_setcond_i32, + op->args[1], op->args[3], + TCG_COND_NE); + if (i == 0) { goto do_setcond_high; - } else if (tmp == 1) { + } else if (i > 0) { goto do_setcond_const; } - tmp = do_constant_folding_cond(INDEX_op_setcond_i32, - op->args[2], op->args[4], - TCG_COND_NE); - if (tmp == 0) { + i = do_constant_folding_cond(INDEX_op_setcond_i32, + op->args[2], op->args[4], + TCG_COND_NE); + if (i == 0) { goto do_setcond_low; - } else if (tmp == 1) { + } else if (i > 0) { goto do_setcond_const; } } From 6b99d5bf388655b340e93412bf60f8bff90e5870 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 10:57:56 -0700 Subject: [PATCH 0791/1334] tcg/optimize: Return true from tcg_opt_gen_{mov,movi} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow callers to tail call to these functions and return true indicating processing complete. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 19c01687b4..066e635f73 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -180,7 +180,7 @@ static bool args_are_copies(TCGArg arg1, TCGArg arg2) return ts_are_copies(arg_temp(arg1), arg_temp(arg2)); } -static void tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) +static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) { TCGTemp *dst_ts = arg_temp(dst); TCGTemp *src_ts = arg_temp(src); @@ -192,7 +192,7 @@ static void tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) if (ts_are_copies(dst_ts, src_ts)) { tcg_op_remove(ctx->tcg, op); - return; + return true; } reset_ts(dst_ts); @@ -228,9 +228,10 @@ static void tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) di->is_const = si->is_const; di->val = si->val; } + return true; } -static void tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, +static bool tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, TCGArg dst, uint64_t val) { const TCGOpDef *def = &tcg_op_defs[op->opc]; @@ -248,7 +249,7 @@ static void tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, /* Convert movi to mov with constant temp. */ tv = tcg_constant_internal(type, val); init_ts_info(ctx, tv); - tcg_opt_gen_mov(ctx, op, dst, temp_arg(tv)); + return tcg_opt_gen_mov(ctx, op, dst, temp_arg(tv)); } static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) From 137f1f4429965d9a702fae9fc89f2604449a24d3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 08:49:25 -0700 Subject: [PATCH 0792/1334] tcg/optimize: Split out finish_folding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy z_mask into OptContext, for writeback to the first output within the new function. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 066e635f73..368457f4a2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -48,6 +48,9 @@ typedef struct OptContext { TCGContext *tcg; TCGOp *prev_mb; TCGTempSet temps_used; + + /* In flight values from optimization. */ + uint64_t z_mask; } OptContext; static inline TempOptInfo *ts_info(TCGTemp *ts) @@ -629,6 +632,34 @@ static void copy_propagate(OptContext *ctx, TCGOp *op, } } +static void finish_folding(OptContext *ctx, TCGOp *op) +{ + const TCGOpDef *def = &tcg_op_defs[op->opc]; + int i, nb_oargs; + + /* + * For an opcode that ends a BB, reset all temp data. + * We do no cross-BB optimization. + */ + if (def->flags & TCG_OPF_BB_END) { + memset(&ctx->temps_used, 0, sizeof(ctx->temps_used)); + ctx->prev_mb = NULL; + return; + } + + nb_oargs = def->nb_oargs; + for (i = 0; i < nb_oargs; i++) { + reset_temp(op->args[i]); + /* + * Save the corresponding known-zero bits mask for the + * first output argument (only one supported so far). + */ + if (i == 0) { + arg_info(op->args[i])->z_mask = ctx->z_mask; + } + } +} + static bool fold_call(OptContext *ctx, TCGOp *op) { TCGContext *s = ctx->tcg; @@ -1122,6 +1153,7 @@ void tcg_optimize(TCGContext *s) partmask &= 0xffffffffu; affected &= 0xffffffffu; } + ctx.z_mask = z_mask; if (partmask == 0) { tcg_opt_gen_movi(&ctx, op, op->args[0], 0); @@ -1570,22 +1602,7 @@ void tcg_optimize(TCGContext *s) break; } - /* Some of the folding above can change opc. */ - opc = op->opc; - def = &tcg_op_defs[opc]; - if (def->flags & TCG_OPF_BB_END) { - memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); - } else { - int nb_oargs = def->nb_oargs; - for (i = 0; i < nb_oargs; i++) { - reset_temp(op->args[i]); - /* Save the corresponding known-zero bits mask for the - first output argument (only one supported so far). */ - if (i == 0) { - arg_info(op->args[i])->z_mask = z_mask; - } - } - } + finish_folding(&ctx, op); /* Eliminate duplicate and redundant fence instructions. */ if (ctx.prev_mb) { From 404a148d891bf18fc564fa94b00970bbc2c0feec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 11:08:21 -0700 Subject: [PATCH 0793/1334] tcg/optimize: Use a boolean to avoid a mass of continues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 368457f4a2..699476e2f1 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -713,6 +713,7 @@ void tcg_optimize(TCGContext *s) uint64_t z_mask, partmask, affected, tmp; TCGOpcode opc = op->opc; const TCGOpDef *def; + bool done = false; /* Calls are special. */ if (opc == INDEX_op_call) { @@ -1212,8 +1213,8 @@ void tcg_optimize(TCGContext *s) allocator where needed and possible. Also detect copies. */ switch (opc) { CASE_OP_32_64_VEC(mov): - tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); - continue; + done = tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); + break; case INDEX_op_dup_vec: if (arg_is_const(op->args[1])) { @@ -1602,7 +1603,9 @@ void tcg_optimize(TCGContext *s) break; } - finish_folding(&ctx, op); + if (!done) { + finish_folding(&ctx, op); + } /* Eliminate duplicate and redundant fence instructions. */ if (ctx.prev_mb) { From 3eefdf2b5885dca36eefa200862c661654519162 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 11:06:43 -0700 Subject: [PATCH 0794/1334] tcg/optimize: Split out fold_mb, fold_qemu_{ld,st} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This puts the separate mb optimization into the same framework as the others. While fold_qemu_{ld,st} are currently identical, that won't last as more code gets moved. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 89 +++++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 699476e2f1..159a5a9ee5 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -692,6 +692,44 @@ static bool fold_call(OptContext *ctx, TCGOp *op) return true; } +static bool fold_mb(OptContext *ctx, TCGOp *op) +{ + /* Eliminate duplicate and redundant fence instructions. */ + if (ctx->prev_mb) { + /* + * Merge two barriers of the same type into one, + * or a weaker barrier into a stronger one, + * or two weaker barriers into a stronger one. + * mb X; mb Y => mb X|Y + * mb; strl => mb; st + * ldaq; mb => ld; mb + * ldaq; strl => ld; mb; st + * Other combinations are also merged into a strong + * barrier. This is stricter than specified but for + * the purposes of TCG is better than not optimizing. + */ + ctx->prev_mb->args[0] |= op->args[0]; + tcg_op_remove(ctx->tcg, op); + } else { + ctx->prev_mb = op; + } + return true; +} + +static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) +{ + /* Opcodes that touch guest memory stop the mb optimization. */ + ctx->prev_mb = NULL; + return false; +} + +static bool fold_qemu_st(OptContext *ctx, TCGOp *op) +{ + /* Opcodes that touch guest memory stop the mb optimization. */ + ctx->prev_mb = NULL; + return false; +} + /* Propagate constants and copies, fold constant expressions. */ void tcg_optimize(TCGContext *s) { @@ -1599,6 +1637,19 @@ void tcg_optimize(TCGContext *s) } break; + case INDEX_op_mb: + done = fold_mb(&ctx, op); + break; + case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld_i64: + done = fold_qemu_ld(&ctx, op); + break; + case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st8_i32: + case INDEX_op_qemu_st_i64: + done = fold_qemu_st(&ctx, op); + break; + default: break; } @@ -1606,43 +1657,5 @@ void tcg_optimize(TCGContext *s) if (!done) { finish_folding(&ctx, op); } - - /* Eliminate duplicate and redundant fence instructions. */ - if (ctx.prev_mb) { - switch (opc) { - case INDEX_op_mb: - /* Merge two barriers of the same type into one, - * or a weaker barrier into a stronger one, - * or two weaker barriers into a stronger one. - * mb X; mb Y => mb X|Y - * mb; strl => mb; st - * ldaq; mb => ld; mb - * ldaq; strl => ld; mb; st - * Other combinations are also merged into a strong - * barrier. This is stricter than specified but for - * the purposes of TCG is better than not optimizing. - */ - ctx.prev_mb->args[0] |= op->args[0]; - tcg_op_remove(s, op); - break; - - default: - /* Opcodes that end the block stop the optimization. */ - if ((def->flags & TCG_OPF_BB_END) == 0) { - break; - } - /* fallthru */ - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st8_i32: - case INDEX_op_qemu_st_i64: - /* Opcodes that touch guest memory stop the optimization. */ - ctx.prev_mb = NULL; - break; - } - } else if (opc == INDEX_op_mb) { - ctx.prev_mb = op; - } } } From 2f9f08ba43df3ea76124f66ae3fe99a96a0a072f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 12:03:48 -0700 Subject: [PATCH 0795/1334] tcg/optimize: Split out fold_const{1,2} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out a whole bunch of placeholder functions, which are currently identical. That won't last as more code gets moved. Use CASE_32_64_VEC for some logical operators that previously missed the addition of vectors. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 271 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 219 insertions(+), 52 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 159a5a9ee5..5c3f8e8fcd 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -660,6 +660,60 @@ static void finish_folding(OptContext *ctx, TCGOp *op) } } +/* + * The fold_* functions return true when processing is complete, + * usually by folding the operation to a constant or to a copy, + * and calling tcg_opt_gen_{mov,movi}. They may do other things, + * like collect information about the value produced, for use in + * optimizing a subsequent operation. + * + * These first fold_* functions are all helpers, used by other + * folders for more specific operations. + */ + +static bool fold_const1(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1])) { + uint64_t t; + + t = arg_info(op->args[1])->val; + t = do_constant_folding(op->opc, t, 0); + return tcg_opt_gen_movi(ctx, op, op->args[0], t); + } + return false; +} + +static bool fold_const2(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { + uint64_t t1 = arg_info(op->args[1])->val; + uint64_t t2 = arg_info(op->args[2])->val; + + t1 = do_constant_folding(op->opc, t1, t2); + return tcg_opt_gen_movi(ctx, op, op->args[0], t1); + } + return false; +} + +/* + * These outermost fold_ functions are sorted alphabetically. + */ + +static bool fold_add(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_and(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_andc(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + static bool fold_call(OptContext *ctx, TCGOp *op) { TCGContext *s = ctx->tcg; @@ -692,6 +746,31 @@ static bool fold_call(OptContext *ctx, TCGOp *op) return true; } +static bool fold_ctpop(OptContext *ctx, TCGOp *op) +{ + return fold_const1(ctx, op); +} + +static bool fold_divide(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_eqv(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_exts(OptContext *ctx, TCGOp *op) +{ + return fold_const1(ctx, op); +} + +static bool fold_extu(OptContext *ctx, TCGOp *op) +{ + return fold_const1(ctx, op); +} + static bool fold_mb(OptContext *ctx, TCGOp *op) { /* Eliminate duplicate and redundant fence instructions. */ @@ -716,6 +795,46 @@ static bool fold_mb(OptContext *ctx, TCGOp *op) return true; } +static bool fold_mul(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_nand(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_neg(OptContext *ctx, TCGOp *op) +{ + return fold_const1(ctx, op); +} + +static bool fold_nor(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_not(OptContext *ctx, TCGOp *op) +{ + return fold_const1(ctx, op); +} + +static bool fold_or(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_orc(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) { /* Opcodes that touch guest memory stop the mb optimization. */ @@ -730,6 +849,26 @@ static bool fold_qemu_st(OptContext *ctx, TCGOp *op) return false; } +static bool fold_remainder(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_shift(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_sub(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + +static bool fold_xor(OptContext *ctx, TCGOp *op) +{ + return fold_const2(ctx, op); +} + /* Propagate constants and copies, fold constant expressions. */ void tcg_optimize(TCGContext *s) { @@ -1276,26 +1415,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(not): - CASE_OP_32_64(neg): - CASE_OP_32_64(ext8s): - CASE_OP_32_64(ext8u): - CASE_OP_32_64(ext16s): - CASE_OP_32_64(ext16u): - CASE_OP_32_64(ctpop): - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: - case INDEX_op_extu_i32_i64: - case INDEX_op_extrl_i64_i32: - case INDEX_op_extrh_i64_i32: - if (arg_is_const(op->args[1])) { - tmp = do_constant_folding(opc, arg_info(op->args[1])->val, 0); - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - continue; - } - break; - CASE_OP_32_64(bswap16): CASE_OP_32_64(bswap32): case INDEX_op_bswap64_i64: @@ -1307,36 +1426,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(add): - CASE_OP_32_64(sub): - CASE_OP_32_64(mul): - CASE_OP_32_64(or): - CASE_OP_32_64(and): - CASE_OP_32_64(xor): - CASE_OP_32_64(shl): - CASE_OP_32_64(shr): - CASE_OP_32_64(sar): - CASE_OP_32_64(rotl): - CASE_OP_32_64(rotr): - CASE_OP_32_64(andc): - CASE_OP_32_64(orc): - CASE_OP_32_64(eqv): - CASE_OP_32_64(nand): - CASE_OP_32_64(nor): - CASE_OP_32_64(muluh): - CASE_OP_32_64(mulsh): - CASE_OP_32_64(div): - CASE_OP_32_64(divu): - CASE_OP_32_64(rem): - CASE_OP_32_64(remu): - if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - tmp = do_constant_folding(opc, arg_info(op->args[1])->val, - arg_info(op->args[2])->val); - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - continue; - } - break; - CASE_OP_32_64(clz): CASE_OP_32_64(ctz): if (arg_is_const(op->args[1])) { @@ -1637,9 +1726,73 @@ void tcg_optimize(TCGContext *s) } break; + default: + break; + + /* ---------------------------------------------------------- */ + /* Sorted alphabetically by opcode as much as possible. */ + + CASE_OP_32_64_VEC(add): + done = fold_add(&ctx, op); + break; + CASE_OP_32_64_VEC(and): + done = fold_and(&ctx, op); + break; + CASE_OP_32_64_VEC(andc): + done = fold_andc(&ctx, op); + break; + CASE_OP_32_64(ctpop): + done = fold_ctpop(&ctx, op); + break; + CASE_OP_32_64(div): + CASE_OP_32_64(divu): + done = fold_divide(&ctx, op); + break; + CASE_OP_32_64(eqv): + done = fold_eqv(&ctx, op); + break; + CASE_OP_32_64(ext8s): + CASE_OP_32_64(ext16s): + case INDEX_op_ext32s_i64: + case INDEX_op_ext_i32_i64: + done = fold_exts(&ctx, op); + break; + CASE_OP_32_64(ext8u): + CASE_OP_32_64(ext16u): + case INDEX_op_ext32u_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: + case INDEX_op_extrh_i64_i32: + done = fold_extu(&ctx, op); + break; case INDEX_op_mb: done = fold_mb(&ctx, op); break; + CASE_OP_32_64(mul): + done = fold_mul(&ctx, op); + break; + CASE_OP_32_64(mulsh): + CASE_OP_32_64(muluh): + done = fold_mul_highpart(&ctx, op); + break; + CASE_OP_32_64(nand): + done = fold_nand(&ctx, op); + break; + CASE_OP_32_64(neg): + done = fold_neg(&ctx, op); + break; + CASE_OP_32_64(nor): + done = fold_nor(&ctx, op); + break; + CASE_OP_32_64_VEC(not): + done = fold_not(&ctx, op); + break; + CASE_OP_32_64_VEC(or): + done = fold_or(&ctx, op); + break; + CASE_OP_32_64_VEC(orc): + done = fold_orc(&ctx, op); + break; case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: done = fold_qemu_ld(&ctx, op); @@ -1649,8 +1802,22 @@ void tcg_optimize(TCGContext *s) case INDEX_op_qemu_st_i64: done = fold_qemu_st(&ctx, op); break; - - default: + CASE_OP_32_64(rem): + CASE_OP_32_64(remu): + done = fold_remainder(&ctx, op); + break; + CASE_OP_32_64(rotl): + CASE_OP_32_64(rotr): + CASE_OP_32_64(sar): + CASE_OP_32_64(shl): + CASE_OP_32_64(shr): + done = fold_shift(&ctx, op); + break; + CASE_OP_32_64_VEC(sub): + done = fold_sub(&ctx, op); + break; + CASE_OP_32_64_VEC(xor): + done = fold_xor(&ctx, op); break; } From bc47b1aa5be9f90cdbf7fbb01717e4259f717bb9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 09:09:35 -0700 Subject: [PATCH 0796/1334] tcg/optimize: Split out fold_setcond2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce some code duplication by folding the NE and EQ cases. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 145 ++++++++++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 5c3f8e8fcd..80e43deb8e 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -854,6 +854,75 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_setcond2(OptContext *ctx, TCGOp *op) +{ + TCGCond cond = op->args[5]; + int i = do_constant_folding_cond2(&op->args[1], &op->args[3], cond); + int inv = 0; + + if (i >= 0) { + goto do_setcond_const; + } + + switch (cond) { + case TCG_COND_LT: + case TCG_COND_GE: + /* + * Simplify LT/GE comparisons vs zero to a single compare + * vs the high word of the input. + */ + if (arg_is_const(op->args[3]) && arg_info(op->args[3])->val == 0 && + arg_is_const(op->args[4]) && arg_info(op->args[4])->val == 0) { + goto do_setcond_high; + } + break; + + case TCG_COND_NE: + inv = 1; + QEMU_FALLTHROUGH; + case TCG_COND_EQ: + /* + * Simplify EQ/NE comparisons where one of the pairs + * can be simplified. + */ + i = do_constant_folding_cond(INDEX_op_setcond_i32, op->args[1], + op->args[3], cond); + switch (i ^ inv) { + case 0: + goto do_setcond_const; + case 1: + goto do_setcond_high; + } + + i = do_constant_folding_cond(INDEX_op_setcond_i32, op->args[2], + op->args[4], cond); + switch (i ^ inv) { + case 0: + goto do_setcond_const; + case 1: + op->args[2] = op->args[3]; + op->args[3] = cond; + op->opc = INDEX_op_setcond_i32; + break; + } + break; + + default: + break; + + do_setcond_high: + op->args[1] = op->args[2]; + op->args[2] = op->args[4]; + op->args[3] = cond; + op->opc = INDEX_op_setcond_i32; + break; + } + return false; + + do_setcond_const: + return tcg_opt_gen_movi(ctx, op, op->args[0], i); +} + static bool fold_shift(OptContext *ctx, TCGOp *op) { return fold_const2(ctx, op); @@ -1653,79 +1722,6 @@ void tcg_optimize(TCGContext *s) } break; - case INDEX_op_setcond2_i32: - i = do_constant_folding_cond2(&op->args[1], &op->args[3], - op->args[5]); - if (i >= 0) { - do_setcond_const: - tcg_opt_gen_movi(&ctx, op, op->args[0], i); - continue; - } - if ((op->args[5] == TCG_COND_LT || op->args[5] == TCG_COND_GE) - && arg_is_const(op->args[3]) - && arg_info(op->args[3])->val == 0 - && arg_is_const(op->args[4]) - && arg_info(op->args[4])->val == 0) { - /* Simplify LT/GE comparisons vs zero to a single compare - vs the high word of the input. */ - do_setcond_high: - reset_temp(op->args[0]); - arg_info(op->args[0])->z_mask = 1; - op->opc = INDEX_op_setcond_i32; - op->args[1] = op->args[2]; - op->args[2] = op->args[4]; - op->args[3] = op->args[5]; - break; - } - if (op->args[5] == TCG_COND_EQ) { - /* Simplify EQ comparisons where one of the pairs - can be simplified. */ - i = do_constant_folding_cond(INDEX_op_setcond_i32, - op->args[1], op->args[3], - TCG_COND_EQ); - if (i == 0) { - goto do_setcond_const; - } else if (i > 0) { - goto do_setcond_high; - } - i = do_constant_folding_cond(INDEX_op_setcond_i32, - op->args[2], op->args[4], - TCG_COND_EQ); - if (i == 0) { - goto do_setcond_high; - } else if (i < 0) { - break; - } - do_setcond_low: - reset_temp(op->args[0]); - arg_info(op->args[0])->z_mask = 1; - op->opc = INDEX_op_setcond_i32; - op->args[2] = op->args[3]; - op->args[3] = op->args[5]; - break; - } - if (op->args[5] == TCG_COND_NE) { - /* Simplify NE comparisons where one of the pairs - can be simplified. */ - i = do_constant_folding_cond(INDEX_op_setcond_i32, - op->args[1], op->args[3], - TCG_COND_NE); - if (i == 0) { - goto do_setcond_high; - } else if (i > 0) { - goto do_setcond_const; - } - i = do_constant_folding_cond(INDEX_op_setcond_i32, - op->args[2], op->args[4], - TCG_COND_NE); - if (i == 0) { - goto do_setcond_low; - } else if (i > 0) { - goto do_setcond_const; - } - } - break; - default: break; @@ -1813,6 +1809,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(shr): done = fold_shift(&ctx, op); break; + case INDEX_op_setcond2_i32: + done = fold_setcond2(&ctx, op); + break; CASE_OP_32_64_VEC(sub): done = fold_sub(&ctx, op); break; From 764d2aba08c35f2462371b01c27fb032447afcd8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 09:22:11 -0700 Subject: [PATCH 0797/1334] tcg/optimize: Split out fold_brcond2 Reduce some code duplication by folding the NE and EQ cases. Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 159 +++++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 78 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 80e43deb8e..c9db14f1d0 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -714,6 +714,84 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_brcond2(OptContext *ctx, TCGOp *op) +{ + TCGCond cond = op->args[4]; + int i = do_constant_folding_cond2(&op->args[0], &op->args[2], cond); + TCGArg label = op->args[5]; + int inv = 0; + + if (i >= 0) { + goto do_brcond_const; + } + + switch (cond) { + case TCG_COND_LT: + case TCG_COND_GE: + /* + * Simplify LT/GE comparisons vs zero to a single compare + * vs the high word of the input. + */ + if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == 0 && + arg_is_const(op->args[3]) && arg_info(op->args[3])->val == 0) { + goto do_brcond_high; + } + break; + + case TCG_COND_NE: + inv = 1; + QEMU_FALLTHROUGH; + case TCG_COND_EQ: + /* + * Simplify EQ/NE comparisons where one of the pairs + * can be simplified. + */ + i = do_constant_folding_cond(INDEX_op_brcond_i32, op->args[0], + op->args[2], cond); + switch (i ^ inv) { + case 0: + goto do_brcond_const; + case 1: + goto do_brcond_high; + } + + i = do_constant_folding_cond(INDEX_op_brcond_i32, op->args[1], + op->args[3], cond); + switch (i ^ inv) { + case 0: + goto do_brcond_const; + case 1: + op->opc = INDEX_op_brcond_i32; + op->args[1] = op->args[2]; + op->args[2] = cond; + op->args[3] = label; + break; + } + break; + + default: + break; + + do_brcond_high: + op->opc = INDEX_op_brcond_i32; + op->args[0] = op->args[1]; + op->args[1] = op->args[3]; + op->args[2] = cond; + op->args[3] = label; + break; + + do_brcond_const: + if (i == 0) { + tcg_op_remove(ctx->tcg, op); + return true; + } + op->opc = INDEX_op_br; + op->args[0] = label; + break; + } + return false; +} + static bool fold_call(OptContext *ctx, TCGOp *op) { TCGContext *s = ctx->tcg; @@ -1644,84 +1722,6 @@ void tcg_optimize(TCGContext *s) } break; - case INDEX_op_brcond2_i32: - i = do_constant_folding_cond2(&op->args[0], &op->args[2], - op->args[4]); - if (i == 0) { - do_brcond_false: - tcg_op_remove(s, op); - continue; - } - if (i > 0) { - do_brcond_true: - op->opc = opc = INDEX_op_br; - op->args[0] = op->args[5]; - break; - } - if ((op->args[4] == TCG_COND_LT || op->args[4] == TCG_COND_GE) - && arg_is_const(op->args[2]) - && arg_info(op->args[2])->val == 0 - && arg_is_const(op->args[3]) - && arg_info(op->args[3])->val == 0) { - /* Simplify LT/GE comparisons vs zero to a single compare - vs the high word of the input. */ - do_brcond_high: - op->opc = opc = INDEX_op_brcond_i32; - op->args[0] = op->args[1]; - op->args[1] = op->args[3]; - op->args[2] = op->args[4]; - op->args[3] = op->args[5]; - break; - } - if (op->args[4] == TCG_COND_EQ) { - /* Simplify EQ comparisons where one of the pairs - can be simplified. */ - i = do_constant_folding_cond(INDEX_op_brcond_i32, - op->args[0], op->args[2], - TCG_COND_EQ); - if (i == 0) { - goto do_brcond_false; - } else if (i > 0) { - goto do_brcond_high; - } - i = do_constant_folding_cond(INDEX_op_brcond_i32, - op->args[1], op->args[3], - TCG_COND_EQ); - if (i == 0) { - goto do_brcond_false; - } else if (i < 0) { - break; - } - do_brcond_low: - memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); - op->opc = INDEX_op_brcond_i32; - op->args[1] = op->args[2]; - op->args[2] = op->args[4]; - op->args[3] = op->args[5]; - break; - } - if (op->args[4] == TCG_COND_NE) { - /* Simplify NE comparisons where one of the pairs - can be simplified. */ - i = do_constant_folding_cond(INDEX_op_brcond_i32, - op->args[0], op->args[2], - TCG_COND_NE); - if (i == 0) { - goto do_brcond_high; - } else if (i > 0) { - goto do_brcond_true; - } - i = do_constant_folding_cond(INDEX_op_brcond_i32, - op->args[1], op->args[3], - TCG_COND_NE); - if (i == 0) { - goto do_brcond_low; - } else if (i > 0) { - goto do_brcond_true; - } - } - break; - default: break; @@ -1737,6 +1737,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(andc): done = fold_andc(&ctx, op); break; + case INDEX_op_brcond2_i32: + done = fold_brcond2(&ctx, op); + break; CASE_OP_32_64(ctpop): done = fold_ctpop(&ctx, op); break; From 079b08040e0c4eed96b3c19fe37cf17a46294d2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 09:30:59 -0700 Subject: [PATCH 0798/1334] tcg/optimize: Split out fold_brcond MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index c9db14f1d0..24ba6d2830 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -714,6 +714,22 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_brcond(OptContext *ctx, TCGOp *op) +{ + TCGCond cond = op->args[2]; + int i = do_constant_folding_cond(op->opc, op->args[0], op->args[1], cond); + + if (i == 0) { + tcg_op_remove(ctx->tcg, op); + return true; + } + if (i > 0) { + op->opc = INDEX_op_br; + op->args[0] = op->args[3]; + } + return false; +} + static bool fold_brcond2(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[4]; @@ -1641,20 +1657,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(brcond): - i = do_constant_folding_cond(opc, op->args[0], - op->args[1], op->args[2]); - if (i == 0) { - tcg_op_remove(s, op); - continue; - } else if (i > 0) { - memset(&ctx.temps_used, 0, sizeof(ctx.temps_used)); - op->opc = opc = INDEX_op_br; - op->args[0] = op->args[3]; - break; - } - break; - CASE_OP_32_64(movcond): i = do_constant_folding_cond(opc, op->args[1], op->args[2], op->args[5]); @@ -1737,6 +1739,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(andc): done = fold_andc(&ctx, op); break; + CASE_OP_32_64(brcond): + done = fold_brcond(&ctx, op); + break; case INDEX_op_brcond2_i32: done = fold_brcond2(&ctx, op); break; From c63ff55cc5d27b81e6a0924bd1f9abea262069a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 09:35:30 -0700 Subject: [PATCH 0799/1334] tcg/optimize: Split out fold_setcond MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 24ba6d2830..f79cb44944 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -948,6 +948,17 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_setcond(OptContext *ctx, TCGOp *op) +{ + TCGCond cond = op->args[3]; + int i = do_constant_folding_cond(op->opc, op->args[1], op->args[2], cond); + + if (i >= 0) { + return tcg_opt_gen_movi(ctx, op, op->args[0], i); + } + return false; +} + static bool fold_setcond2(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[5]; @@ -1648,15 +1659,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(setcond): - i = do_constant_folding_cond(opc, op->args[1], - op->args[2], op->args[3]); - if (i >= 0) { - tcg_opt_gen_movi(&ctx, op, op->args[0], i); - continue; - } - break; - CASE_OP_32_64(movcond): i = do_constant_folding_cond(opc, op->args[1], op->args[2], op->args[5]); @@ -1817,6 +1819,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(shr): done = fold_shift(&ctx, op); break; + CASE_OP_32_64(setcond): + done = fold_setcond(&ctx, op); + break; case INDEX_op_setcond2_i32: done = fold_setcond2(&ctx, op); break; From 6b8ac0d1498cf6638e3d50c33e06e5b3b0e7a2d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 10:24:12 -0700 Subject: [PATCH 0800/1334] tcg/optimize: Split out fold_mulu2_i32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f79cb44944..805522f99d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -899,6 +899,24 @@ static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_mulu2_i32(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { + uint32_t a = arg_info(op->args[2])->val; + uint32_t b = arg_info(op->args[3])->val; + uint64_t r = (uint64_t)a * b; + TCGArg rl, rh; + TCGOp *op2 = tcg_op_insert_before(ctx->tcg, op, INDEX_op_mov_i32); + + rl = op->args[0]; + rh = op->args[1]; + tcg_opt_gen_movi(ctx, op, rl, (int32_t)r); + tcg_opt_gen_movi(ctx, op2, rh, (int32_t)(r >> 32)); + return true; + } + return false; +} + static bool fold_nand(OptContext *ctx, TCGOp *op) { return fold_const2(ctx, op); @@ -1710,22 +1728,6 @@ void tcg_optimize(TCGContext *s) } break; - case INDEX_op_mulu2_i32: - if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { - uint32_t a = arg_info(op->args[2])->val; - uint32_t b = arg_info(op->args[3])->val; - uint64_t r = (uint64_t)a * b; - TCGArg rl, rh; - TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_mov_i32); - - rl = op->args[0]; - rh = op->args[1]; - tcg_opt_gen_movi(&ctx, op, rl, (int32_t)r); - tcg_opt_gen_movi(&ctx, op2, rh, (int32_t)(r >> 32)); - continue; - } - break; - default: break; @@ -1781,6 +1783,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(muluh): done = fold_mul_highpart(&ctx, op); break; + case INDEX_op_mulu2_i32: + done = fold_mulu2_i32(&ctx, op); + break; CASE_OP_32_64(nand): done = fold_nand(&ctx, op); break; From e3f7dc216744aceb46fb67be1476b518bc48c5ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 10:30:38 -0700 Subject: [PATCH 0801/1334] tcg/optimize: Split out fold_addsub2_i32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two additional helpers, fold_add2_i32 and fold_sub2_i32 which will not be simple wrappers forever. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 70 +++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 805522f99d..9d1d045363 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -704,6 +704,39 @@ static bool fold_add(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_addsub2_i32(OptContext *ctx, TCGOp *op, bool add) +{ + if (arg_is_const(op->args[2]) && arg_is_const(op->args[3]) && + arg_is_const(op->args[4]) && arg_is_const(op->args[5])) { + uint32_t al = arg_info(op->args[2])->val; + uint32_t ah = arg_info(op->args[3])->val; + uint32_t bl = arg_info(op->args[4])->val; + uint32_t bh = arg_info(op->args[5])->val; + uint64_t a = ((uint64_t)ah << 32) | al; + uint64_t b = ((uint64_t)bh << 32) | bl; + TCGArg rl, rh; + TCGOp *op2 = tcg_op_insert_before(ctx->tcg, op, INDEX_op_mov_i32); + + if (add) { + a += b; + } else { + a -= b; + } + + rl = op->args[0]; + rh = op->args[1]; + tcg_opt_gen_movi(ctx, op, rl, (int32_t)a); + tcg_opt_gen_movi(ctx, op2, rh, (int32_t)(a >> 32)); + return true; + } + return false; +} + +static bool fold_add2_i32(OptContext *ctx, TCGOp *op) +{ + return fold_addsub2_i32(ctx, op, true); +} + static bool fold_and(OptContext *ctx, TCGOp *op) { return fold_const2(ctx, op); @@ -1056,6 +1089,11 @@ static bool fold_sub(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_sub2_i32(OptContext *ctx, TCGOp *op) +{ + return fold_addsub2_i32(ctx, op, false); +} + static bool fold_xor(OptContext *ctx, TCGOp *op) { return fold_const2(ctx, op); @@ -1701,32 +1739,6 @@ void tcg_optimize(TCGContext *s) } break; - case INDEX_op_add2_i32: - case INDEX_op_sub2_i32: - if (arg_is_const(op->args[2]) && arg_is_const(op->args[3]) - && arg_is_const(op->args[4]) && arg_is_const(op->args[5])) { - uint32_t al = arg_info(op->args[2])->val; - uint32_t ah = arg_info(op->args[3])->val; - uint32_t bl = arg_info(op->args[4])->val; - uint32_t bh = arg_info(op->args[5])->val; - uint64_t a = ((uint64_t)ah << 32) | al; - uint64_t b = ((uint64_t)bh << 32) | bl; - TCGArg rl, rh; - TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_mov_i32); - - if (opc == INDEX_op_add2_i32) { - a += b; - } else { - a -= b; - } - - rl = op->args[0]; - rh = op->args[1]; - tcg_opt_gen_movi(&ctx, op, rl, (int32_t)a); - tcg_opt_gen_movi(&ctx, op2, rh, (int32_t)(a >> 32)); - continue; - } - break; default: break; @@ -1737,6 +1749,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(add): done = fold_add(&ctx, op); break; + case INDEX_op_add2_i32: + done = fold_add2_i32(&ctx, op); + break; CASE_OP_32_64_VEC(and): done = fold_and(&ctx, op); break; @@ -1833,6 +1848,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(sub): done = fold_sub(&ctx, op); break; + case INDEX_op_sub2_i32: + done = fold_sub2_i32(&ctx, op); + break; CASE_OP_32_64_VEC(xor): done = fold_xor(&ctx, op); break; From 0c310a3005b59a1e3667654b9c88ddaa32eaf166 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 10:37:24 -0700 Subject: [PATCH 0802/1334] tcg/optimize: Split out fold_movcond MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 56 ++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 9d1d045363..110b3d1cc2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -922,6 +922,34 @@ static bool fold_mb(OptContext *ctx, TCGOp *op) return true; } +static bool fold_movcond(OptContext *ctx, TCGOp *op) +{ + TCGOpcode opc = op->opc; + TCGCond cond = op->args[5]; + int i = do_constant_folding_cond(opc, op->args[1], op->args[2], cond); + + if (i >= 0) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[4 - i]); + } + + if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { + uint64_t tv = arg_info(op->args[3])->val; + uint64_t fv = arg_info(op->args[4])->val; + + opc = (opc == INDEX_op_movcond_i32 + ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64); + + if (tv == 1 && fv == 0) { + op->opc = opc; + op->args[3] = cond; + } else if (fv == 1 && tv == 0) { + op->opc = opc; + op->args[3] = tcg_invert_cond(cond); + } + } + return false; +} + static bool fold_mul(OptContext *ctx, TCGOp *op) { return fold_const2(ctx, op); @@ -1715,31 +1743,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(movcond): - i = do_constant_folding_cond(opc, op->args[1], - op->args[2], op->args[5]); - if (i >= 0) { - tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[4 - i]); - continue; - } - if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { - uint64_t tv = arg_info(op->args[3])->val; - uint64_t fv = arg_info(op->args[4])->val; - TCGCond cond = op->args[5]; - - if (fv == 1 && tv == 0) { - cond = tcg_invert_cond(cond); - } else if (!(tv == 1 && fv == 0)) { - break; - } - op->args[3] = cond; - op->opc = opc = (opc == INDEX_op_movcond_i32 - ? INDEX_op_setcond_i32 - : INDEX_op_setcond_i64); - } - break; - - default: break; @@ -1791,6 +1794,9 @@ void tcg_optimize(TCGContext *s) case INDEX_op_mb: done = fold_mb(&ctx, op); break; + CASE_OP_32_64(movcond): + done = fold_movcond(&ctx, op); + break; CASE_OP_32_64(mul): done = fold_mul(&ctx, op); break; From dcd08996c9420a0d22399e0cc53117d2043a02bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 10:41:39 -0700 Subject: [PATCH 0803/1334] tcg/optimize: Split out fold_extract2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 110b3d1cc2..faedbdbfb8 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -888,6 +888,25 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_extract2(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { + uint64_t v1 = arg_info(op->args[1])->val; + uint64_t v2 = arg_info(op->args[2])->val; + int shr = op->args[3]; + + if (op->opc == INDEX_op_extract2_i64) { + v1 >>= shr; + v2 <<= 64 - shr; + } else { + v1 = (uint32_t)v1 >> shr; + v2 = (int32_t)v2 << (32 - shr); + } + return tcg_opt_gen_movi(ctx, op, op->args[0], v1 | v2); + } + return false; +} + static bool fold_exts(OptContext *ctx, TCGOp *op) { return fold_const1(ctx, op); @@ -1726,23 +1745,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(extract2): - if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - uint64_t v1 = arg_info(op->args[1])->val; - uint64_t v2 = arg_info(op->args[2])->val; - int shr = op->args[3]; - - if (opc == INDEX_op_extract2_i64) { - tmp = (v1 >> shr) | (v2 << (64 - shr)); - } else { - tmp = (int32_t)(((uint32_t)v1 >> shr) | - ((uint32_t)v2 << (32 - shr))); - } - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - continue; - } - break; - default: break; @@ -1777,6 +1779,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(eqv): done = fold_eqv(&ctx, op); break; + CASE_OP_32_64(extract2): + done = fold_extract2(&ctx, op); + break; CASE_OP_32_64(ext8s): CASE_OP_32_64(ext16s): case INDEX_op_ext32s_i64: From b6617c8821548350a490c14894a4fe0dba07fc73 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 10:44:53 -0700 Subject: [PATCH 0804/1334] tcg/optimize: Split out fold_extract, fold_sextract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index faedbdbfb8..3bd5f043c8 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -888,6 +888,18 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_extract(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1])) { + uint64_t t; + + t = arg_info(op->args[1])->val; + t = extract64(t, op->args[2], op->args[3]); + return tcg_opt_gen_movi(ctx, op, op->args[0], t); + } + return false; +} + static bool fold_extract2(OptContext *ctx, TCGOp *op) { if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { @@ -1126,6 +1138,18 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) return tcg_opt_gen_movi(ctx, op, op->args[0], i); } +static bool fold_sextract(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1])) { + uint64_t t; + + t = arg_info(op->args[1])->val; + t = sextract64(t, op->args[2], op->args[3]); + return tcg_opt_gen_movi(ctx, op, op->args[0], t); + } + return false; +} + static bool fold_shift(OptContext *ctx, TCGOp *op) { return fold_const2(ctx, op); @@ -1727,24 +1751,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(extract): - if (arg_is_const(op->args[1])) { - tmp = extract64(arg_info(op->args[1])->val, - op->args[2], op->args[3]); - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - continue; - } - break; - - CASE_OP_32_64(sextract): - if (arg_is_const(op->args[1])) { - tmp = sextract64(arg_info(op->args[1])->val, - op->args[2], op->args[3]); - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - continue; - } - break; - default: break; @@ -1779,6 +1785,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(eqv): done = fold_eqv(&ctx, op); break; + CASE_OP_32_64(extract): + done = fold_extract(&ctx, op); + break; CASE_OP_32_64(extract2): done = fold_extract2(&ctx, op); break; @@ -1856,6 +1865,9 @@ void tcg_optimize(TCGContext *s) case INDEX_op_setcond2_i32: done = fold_setcond2(&ctx, op); break; + CASE_OP_32_64(sextract): + done = fold_sextract(&ctx, op); + break; CASE_OP_32_64_VEC(sub): done = fold_sub(&ctx, op); break; From 1b1907b8460467743fd23cef918fac9dd7858441 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 10:47:04 -0700 Subject: [PATCH 0805/1334] tcg/optimize: Split out fold_deposit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 3bd5f043c8..2c57d08760 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -878,6 +878,18 @@ static bool fold_ctpop(OptContext *ctx, TCGOp *op) return fold_const1(ctx, op); } +static bool fold_deposit(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { + uint64_t t1 = arg_info(op->args[1])->val; + uint64_t t2 = arg_info(op->args[2])->val; + + t1 = deposit64(t1, op->args[3], op->args[4], t2); + return tcg_opt_gen_movi(ctx, op, op->args[0], t1); + } + return false; +} + static bool fold_divide(OptContext *ctx, TCGOp *op) { return fold_const2(ctx, op); @@ -1741,16 +1753,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(deposit): - if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - tmp = deposit64(arg_info(op->args[1])->val, - op->args[3], op->args[4], - arg_info(op->args[2])->val); - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - continue; - } - break; - default: break; @@ -1778,6 +1780,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(ctpop): done = fold_ctpop(&ctx, op); break; + CASE_OP_32_64(deposit): + done = fold_deposit(&ctx, op); + break; CASE_OP_32_64(div): CASE_OP_32_64(divu): done = fold_divide(&ctx, op); From 30dd0bfeb5385342a7f216e661d9b69f6ec7182e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 10:51:34 -0700 Subject: [PATCH 0806/1334] tcg/optimize: Split out fold_count_zeros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 2c57d08760..dd65f1afcd 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -873,6 +873,20 @@ static bool fold_call(OptContext *ctx, TCGOp *op) return true; } +static bool fold_count_zeros(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1])) { + uint64_t t = arg_info(op->args[1])->val; + + if (t != 0) { + t = do_constant_folding(op->opc, t, 0); + return tcg_opt_gen_movi(ctx, op, op->args[0], t); + } + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[2]); + } + return false; +} + static bool fold_ctpop(OptContext *ctx, TCGOp *op) { return fold_const1(ctx, op); @@ -1739,20 +1753,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(clz): - CASE_OP_32_64(ctz): - if (arg_is_const(op->args[1])) { - TCGArg v = arg_info(op->args[1])->val; - if (v != 0) { - tmp = do_constant_folding(opc, v, 0); - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - } else { - tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[2]); - } - continue; - } - break; - default: break; @@ -1777,6 +1777,10 @@ void tcg_optimize(TCGContext *s) case INDEX_op_brcond2_i32: done = fold_brcond2(&ctx, op); break; + CASE_OP_32_64(clz): + CASE_OP_32_64(ctz): + done = fold_count_zeros(&ctx, op); + break; CASE_OP_32_64(ctpop): done = fold_ctpop(&ctx, op); break; From 09bacdc2632581cafd2878d91d62ba69dc7574f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 11:58:12 -0700 Subject: [PATCH 0807/1334] tcg/optimize: Split out fold_bswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index dd65f1afcd..5374c230da 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -841,6 +841,17 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) return false; } +static bool fold_bswap(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1])) { + uint64_t t = arg_info(op->args[1])->val; + + t = do_constant_folding(op->opc, t, op->args[2]); + return tcg_opt_gen_movi(ctx, op, op->args[0], t); + } + return false; +} + static bool fold_call(OptContext *ctx, TCGOp *op) { TCGContext *s = ctx->tcg; @@ -1742,17 +1753,6 @@ void tcg_optimize(TCGContext *s) } break; - CASE_OP_32_64(bswap16): - CASE_OP_32_64(bswap32): - case INDEX_op_bswap64_i64: - if (arg_is_const(op->args[1])) { - tmp = do_constant_folding(opc, arg_info(op->args[1])->val, - op->args[2]); - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - continue; - } - break; - default: break; @@ -1777,6 +1777,11 @@ void tcg_optimize(TCGContext *s) case INDEX_op_brcond2_i32: done = fold_brcond2(&ctx, op); break; + CASE_OP_32_64(bswap16): + CASE_OP_32_64(bswap32): + case INDEX_op_bswap64_i64: + done = fold_bswap(&ctx, op); + break; CASE_OP_32_64(clz): CASE_OP_32_64(ctz): done = fold_count_zeros(&ctx, op); From 8cdb3fcb8e69502963ca4127fab656e5ffe2be93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 12:06:33 -0700 Subject: [PATCH 0808/1334] tcg/optimize: Split out fold_dup, fold_dup2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 53 +++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 5374c230da..8524fe1f8a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -920,6 +920,31 @@ static bool fold_divide(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_dup(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1])) { + uint64_t t = arg_info(op->args[1])->val; + t = dup_const(TCGOP_VECE(op), t); + return tcg_opt_gen_movi(ctx, op, op->args[0], t); + } + return false; +} + +static bool fold_dup2(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { + uint64_t t = deposit64(arg_info(op->args[1])->val, 32, 32, + arg_info(op->args[2])->val); + return tcg_opt_gen_movi(ctx, op, op->args[0], t); + } + + if (args_are_copies(op->args[1], op->args[2])) { + op->opc = INDEX_op_dup_vec; + TCGOP_VECE(op) = MO_32; + } + return false; +} + static bool fold_eqv(OptContext *ctx, TCGOp *op) { return fold_const2(ctx, op); @@ -1731,28 +1756,6 @@ void tcg_optimize(TCGContext *s) done = tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); break; - case INDEX_op_dup_vec: - if (arg_is_const(op->args[1])) { - tmp = arg_info(op->args[1])->val; - tmp = dup_const(TCGOP_VECE(op), tmp); - tcg_opt_gen_movi(&ctx, op, op->args[0], tmp); - continue; - } - break; - - case INDEX_op_dup2_vec: - assert(TCG_TARGET_REG_BITS == 32); - if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - tcg_opt_gen_movi(&ctx, op, op->args[0], - deposit64(arg_info(op->args[1])->val, 32, 32, - arg_info(op->args[2])->val)); - continue; - } else if (args_are_copies(op->args[1], op->args[2])) { - op->opc = INDEX_op_dup_vec; - TCGOP_VECE(op) = MO_32; - } - break; - default: break; @@ -1796,6 +1799,12 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(divu): done = fold_divide(&ctx, op); break; + case INDEX_op_dup_vec: + done = fold_dup(&ctx, op); + break; + case INDEX_op_dup2_vec: + done = fold_dup2(&ctx, op); + break; CASE_OP_32_64(eqv): done = fold_eqv(&ctx, op); break; From 2cfac7fa482e015310968ee68b001db94789d190 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 13:05:43 -0700 Subject: [PATCH 0809/1334] tcg/optimize: Split out fold_mov MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the final entry in the main switch that was in a different form. After this, we have the option to convert the switch into a function dispatch table. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 8524fe1f8a..5f1bd7cd78 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1015,6 +1015,11 @@ static bool fold_mb(OptContext *ctx, TCGOp *op) return true; } +static bool fold_mov(OptContext *ctx, TCGOp *op) +{ + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); +} + static bool fold_movcond(OptContext *ctx, TCGOp *op) { TCGOpcode opc = op->opc; @@ -1748,20 +1753,11 @@ void tcg_optimize(TCGContext *s) break; } - /* Propagate constants through copy operations and do constant - folding. Constants will be substituted to arguments by register - allocator where needed and possible. Also detect copies. */ + /* + * Process each opcode. + * Sorted alphabetically by opcode as much as possible. + */ switch (opc) { - CASE_OP_32_64_VEC(mov): - done = tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); - break; - - default: - break; - - /* ---------------------------------------------------------- */ - /* Sorted alphabetically by opcode as much as possible. */ - CASE_OP_32_64_VEC(add): done = fold_add(&ctx, op); break; @@ -1831,6 +1827,9 @@ void tcg_optimize(TCGContext *s) case INDEX_op_mb: done = fold_mb(&ctx, op); break; + CASE_OP_32_64_VEC(mov): + done = fold_mov(&ctx, op); + break; CASE_OP_32_64(movcond): done = fold_movcond(&ctx, op); break; @@ -1900,6 +1899,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(xor): done = fold_xor(&ctx, op); break; + default: + break; } if (!done) { From cbe42fb2f22b9adb8b78374f6ff4ca5f58807208 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 13:02:00 -0700 Subject: [PATCH 0810/1334] tcg/optimize: Split out fold_xx_to_i MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull the "op r, a, a => movi r, 0" optimization into a function, and use it in the outer opcode fold functions. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 5f1bd7cd78..2f55dc56c0 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -695,6 +695,15 @@ static bool fold_const2(OptContext *ctx, TCGOp *op) return false; } +/* If the binary operation has both arguments equal, fold to @i. */ +static bool fold_xx_to_i(OptContext *ctx, TCGOp *op, uint64_t i) +{ + if (args_are_copies(op->args[1], op->args[2])) { + return tcg_opt_gen_movi(ctx, op, op->args[0], i); + } + return false; +} + /* * These outermost fold_ functions are sorted alphabetically. */ @@ -744,7 +753,11 @@ static bool fold_and(OptContext *ctx, TCGOp *op) static bool fold_andc(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xx_to_i(ctx, op, 0)) { + return true; + } + return false; } static bool fold_brcond(OptContext *ctx, TCGOp *op) @@ -1224,7 +1237,11 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) static bool fold_sub(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xx_to_i(ctx, op, 0)) { + return true; + } + return false; } static bool fold_sub2_i32(OptContext *ctx, TCGOp *op) @@ -1234,7 +1251,11 @@ static bool fold_sub2_i32(OptContext *ctx, TCGOp *op) static bool fold_xor(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xx_to_i(ctx, op, 0)) { + return true; + } + return false; } /* Propagate constants and copies, fold constant expressions. */ @@ -1739,20 +1760,6 @@ void tcg_optimize(TCGContext *s) break; } - /* Simplify expression for "op r, a, a => movi r, 0" cases */ - switch (opc) { - CASE_OP_32_64_VEC(andc): - CASE_OP_32_64_VEC(sub): - CASE_OP_32_64_VEC(xor): - if (args_are_copies(op->args[1], op->args[2])) { - tcg_opt_gen_movi(&ctx, op, op->args[0], 0); - continue; - } - break; - default: - break; - } - /* * Process each opcode. * Sorted alphabetically by opcode as much as possible. From ca7bb049a0250890afd4dd0e66f10b8a4d51715c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 13:14:21 -0700 Subject: [PATCH 0811/1334] tcg/optimize: Split out fold_xx_to_x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull the "op r, a, a => mov r, a" optimization into a function, and use it in the outer opcode fold functions. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 2f55dc56c0..ab96849edf 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -704,8 +704,22 @@ static bool fold_xx_to_i(OptContext *ctx, TCGOp *op, uint64_t i) return false; } +/* If the binary operation has both arguments equal, fold to identity. */ +static bool fold_xx_to_x(OptContext *ctx, TCGOp *op) +{ + if (args_are_copies(op->args[1], op->args[2])) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + return false; +} + /* * These outermost fold_ functions are sorted alphabetically. + * + * The ordering of the transformations should be: + * 1) those that produce a constant + * 2) those that produce a copy + * 3) those that produce information about the result value. */ static bool fold_add(OptContext *ctx, TCGOp *op) @@ -748,7 +762,11 @@ static bool fold_add2_i32(OptContext *ctx, TCGOp *op) static bool fold_and(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xx_to_x(ctx, op)) { + return true; + } + return false; } static bool fold_andc(OptContext *ctx, TCGOp *op) @@ -1111,7 +1129,11 @@ static bool fold_not(OptContext *ctx, TCGOp *op) static bool fold_or(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xx_to_x(ctx, op)) { + return true; + } + return false; } static bool fold_orc(OptContext *ctx, TCGOp *op) @@ -1747,19 +1769,6 @@ void tcg_optimize(TCGContext *s) break; } - /* Simplify expression for "op r, a, a => mov r, a" cases */ - switch (opc) { - CASE_OP_32_64_VEC(or): - CASE_OP_32_64_VEC(and): - if (args_are_copies(op->args[1], op->args[2])) { - tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); - continue; - } - break; - default: - break; - } - /* * Process each opcode. * Sorted alphabetically by opcode as much as possible. From e8679955ec0791122a6c22d48ae760a215204f6a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 13:19:52 -0700 Subject: [PATCH 0812/1334] tcg/optimize: Split out fold_xi_to_i MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull the "op r, a, 0 => movi r, 0" optimization into a function, and use it in the outer opcode fold functions. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index ab96849edf..cfdc53c964 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -695,6 +695,15 @@ static bool fold_const2(OptContext *ctx, TCGOp *op) return false; } +/* If the binary operation has second argument @i, fold to @i. */ +static bool fold_xi_to_i(OptContext *ctx, TCGOp *op, uint64_t i) +{ + if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == i) { + return tcg_opt_gen_movi(ctx, op, op->args[0], i); + } + return false; +} + /* If the binary operation has both arguments equal, fold to @i. */ static bool fold_xx_to_i(OptContext *ctx, TCGOp *op, uint64_t i) { @@ -763,6 +772,7 @@ static bool fold_add2_i32(OptContext *ctx, TCGOp *op) static bool fold_and(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || + fold_xi_to_i(ctx, op, 0) || fold_xx_to_x(ctx, op)) { return true; } @@ -1081,12 +1091,20 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) static bool fold_mul(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xi_to_i(ctx, op, 0)) { + return true; + } + return false; } static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xi_to_i(ctx, op, 0)) { + return true; + } + return false; } static bool fold_mulu2_i32(OptContext *ctx, TCGOp *op) @@ -1753,22 +1771,6 @@ void tcg_optimize(TCGContext *s) continue; } - /* Simplify expression for "op r, a, 0 => movi r, 0" cases */ - switch (opc) { - CASE_OP_32_64_VEC(and): - CASE_OP_32_64_VEC(mul): - CASE_OP_32_64(muluh): - CASE_OP_32_64(mulsh): - if (arg_is_const(op->args[2]) - && arg_info(op->args[2])->val == 0) { - tcg_opt_gen_movi(&ctx, op, op->args[0], 0); - continue; - } - break; - default: - break; - } - /* * Process each opcode. * Sorted alphabetically by opcode as much as possible. From 67f84c962166093f26a1f2c66034a44cf294e809 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 08:00:20 -0700 Subject: [PATCH 0813/1334] tcg/optimize: Add type to OptContext Compute the type of the operation early. There are at least 4 places that used a def->flags ladder to determine the type of the operation being optimized. There were two places that assumed !TCG_OPF_64BIT means TCG_TYPE_I32, and so could potentially compute incorrect results for vector operations. Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 149 +++++++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 60 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index cfdc53c964..e869fa7e78 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -51,6 +51,7 @@ typedef struct OptContext { /* In flight values from optimization. */ uint64_t z_mask; + TCGType type; } OptContext; static inline TempOptInfo *ts_info(TCGTemp *ts) @@ -187,7 +188,6 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) { TCGTemp *dst_ts = arg_temp(dst); TCGTemp *src_ts = arg_temp(src); - const TCGOpDef *def; TempOptInfo *di; TempOptInfo *si; uint64_t z_mask; @@ -201,16 +201,24 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) reset_ts(dst_ts); di = ts_info(dst_ts); si = ts_info(src_ts); - def = &tcg_op_defs[op->opc]; - if (def->flags & TCG_OPF_VECTOR) { - new_op = INDEX_op_mov_vec; - } else if (def->flags & TCG_OPF_64BIT) { - new_op = INDEX_op_mov_i64; - } else { + + switch (ctx->type) { + case TCG_TYPE_I32: new_op = INDEX_op_mov_i32; + break; + case TCG_TYPE_I64: + new_op = INDEX_op_mov_i64; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + /* TCGOP_VECL and TCGOP_VECE remain unchanged. */ + new_op = INDEX_op_mov_vec; + break; + default: + g_assert_not_reached(); } op->opc = new_op; - /* TCGOP_VECL and TCGOP_VECE remain unchanged. */ op->args[0] = dst; op->args[1] = src; @@ -237,20 +245,9 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) static bool tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, TCGArg dst, uint64_t val) { - const TCGOpDef *def = &tcg_op_defs[op->opc]; - TCGType type; - TCGTemp *tv; - - if (def->flags & TCG_OPF_VECTOR) { - type = TCGOP_VECL(op) + TCG_TYPE_V64; - } else if (def->flags & TCG_OPF_64BIT) { - type = TCG_TYPE_I64; - } else { - type = TCG_TYPE_I32; - } - /* Convert movi to mov with constant temp. */ - tv = tcg_constant_internal(type, val); + TCGTemp *tv = tcg_constant_internal(ctx->type, val); + init_ts_info(ctx, tv); return tcg_opt_gen_mov(ctx, op, dst, temp_arg(tv)); } @@ -420,11 +417,11 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) } } -static uint64_t do_constant_folding(TCGOpcode op, uint64_t x, uint64_t y) +static uint64_t do_constant_folding(TCGOpcode op, TCGType type, + uint64_t x, uint64_t y) { - const TCGOpDef *def = &tcg_op_defs[op]; uint64_t res = do_constant_folding_2(op, x, y); - if (!(def->flags & TCG_OPF_64BIT)) { + if (type == TCG_TYPE_I32) { res = (int32_t)res; } return res; @@ -510,19 +507,21 @@ static bool do_constant_folding_cond_eq(TCGCond c) * Return -1 if the condition can't be simplified, * and the result of the condition (0 or 1) if it can. */ -static int do_constant_folding_cond(TCGOpcode op, TCGArg x, +static int do_constant_folding_cond(TCGType type, TCGArg x, TCGArg y, TCGCond c) { uint64_t xv = arg_info(x)->val; uint64_t yv = arg_info(y)->val; if (arg_is_const(x) && arg_is_const(y)) { - const TCGOpDef *def = &tcg_op_defs[op]; - tcg_debug_assert(!(def->flags & TCG_OPF_VECTOR)); - if (def->flags & TCG_OPF_64BIT) { - return do_constant_folding_cond_64(xv, yv, c); - } else { + switch (type) { + case TCG_TYPE_I32: return do_constant_folding_cond_32(xv, yv, c); + case TCG_TYPE_I64: + return do_constant_folding_cond_64(xv, yv, c); + default: + /* Only scalar comparisons are optimizable */ + return -1; } } else if (args_are_copies(x, y)) { return do_constant_folding_cond_eq(c); @@ -677,7 +676,7 @@ static bool fold_const1(OptContext *ctx, TCGOp *op) uint64_t t; t = arg_info(op->args[1])->val; - t = do_constant_folding(op->opc, t, 0); + t = do_constant_folding(op->opc, ctx->type, t, 0); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } return false; @@ -689,7 +688,7 @@ static bool fold_const2(OptContext *ctx, TCGOp *op) uint64_t t1 = arg_info(op->args[1])->val; uint64_t t2 = arg_info(op->args[2])->val; - t1 = do_constant_folding(op->opc, t1, t2); + t1 = do_constant_folding(op->opc, ctx->type, t1, t2); return tcg_opt_gen_movi(ctx, op, op->args[0], t1); } return false; @@ -791,7 +790,7 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) static bool fold_brcond(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[2]; - int i = do_constant_folding_cond(op->opc, op->args[0], op->args[1], cond); + int i = do_constant_folding_cond(ctx->type, op->args[0], op->args[1], cond); if (i == 0) { tcg_op_remove(ctx->tcg, op); @@ -836,7 +835,7 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) * Simplify EQ/NE comparisons where one of the pairs * can be simplified. */ - i = do_constant_folding_cond(INDEX_op_brcond_i32, op->args[0], + i = do_constant_folding_cond(TCG_TYPE_I32, op->args[0], op->args[2], cond); switch (i ^ inv) { case 0: @@ -845,7 +844,7 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) goto do_brcond_high; } - i = do_constant_folding_cond(INDEX_op_brcond_i32, op->args[1], + i = do_constant_folding_cond(TCG_TYPE_I32, op->args[1], op->args[3], cond); switch (i ^ inv) { case 0: @@ -887,7 +886,7 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) if (arg_is_const(op->args[1])) { uint64_t t = arg_info(op->args[1])->val; - t = do_constant_folding(op->opc, t, op->args[2]); + t = do_constant_folding(op->opc, ctx->type, t, op->args[2]); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } return false; @@ -931,7 +930,7 @@ static bool fold_count_zeros(OptContext *ctx, TCGOp *op) uint64_t t = arg_info(op->args[1])->val; if (t != 0) { - t = do_constant_folding(op->opc, t, 0); + t = do_constant_folding(op->opc, ctx->type, t, 0); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[2]); @@ -1063,9 +1062,8 @@ static bool fold_mov(OptContext *ctx, TCGOp *op) static bool fold_movcond(OptContext *ctx, TCGOp *op) { - TCGOpcode opc = op->opc; TCGCond cond = op->args[5]; - int i = do_constant_folding_cond(opc, op->args[1], op->args[2], cond); + int i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); if (i >= 0) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[4 - i]); @@ -1074,9 +1072,18 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { uint64_t tv = arg_info(op->args[3])->val; uint64_t fv = arg_info(op->args[4])->val; + TCGOpcode opc; - opc = (opc == INDEX_op_movcond_i32 - ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64); + switch (ctx->type) { + case TCG_TYPE_I32: + opc = INDEX_op_setcond_i32; + break; + case TCG_TYPE_I64: + opc = INDEX_op_setcond_i64; + break; + default: + g_assert_not_reached(); + } if (tv == 1 && fv == 0) { op->opc = opc; @@ -1181,7 +1188,7 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) static bool fold_setcond(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[3]; - int i = do_constant_folding_cond(op->opc, op->args[1], op->args[2], cond); + int i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); if (i >= 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], i); @@ -1220,7 +1227,7 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) * Simplify EQ/NE comparisons where one of the pairs * can be simplified. */ - i = do_constant_folding_cond(INDEX_op_setcond_i32, op->args[1], + i = do_constant_folding_cond(TCG_TYPE_I32, op->args[1], op->args[3], cond); switch (i ^ inv) { case 0: @@ -1229,7 +1236,7 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) goto do_setcond_high; } - i = do_constant_folding_cond(INDEX_op_setcond_i32, op->args[2], + i = do_constant_folding_cond(TCG_TYPE_I32, op->args[2], op->args[4], cond); switch (i ^ inv) { case 0: @@ -1331,6 +1338,15 @@ void tcg_optimize(TCGContext *s) init_arguments(&ctx, op, def->nb_oargs + def->nb_iargs); copy_propagate(&ctx, op, def->nb_oargs, def->nb_iargs); + /* Pre-compute the type of the operation. */ + if (def->flags & TCG_OPF_VECTOR) { + ctx.type = TCG_TYPE_V64 + TCGOP_VECL(op); + } else if (def->flags & TCG_OPF_64BIT) { + ctx.type = TCG_TYPE_I64; + } else { + ctx.type = TCG_TYPE_I32; + } + /* For commutative operations make constant second argument */ switch (opc) { CASE_OP_32_64_VEC(add): @@ -1411,19 +1427,24 @@ void tcg_optimize(TCGContext *s) /* Proceed with possible constant folding. */ break; } - if (opc == INDEX_op_sub_i32) { + switch (ctx.type) { + case TCG_TYPE_I32: neg_op = INDEX_op_neg_i32; have_neg = TCG_TARGET_HAS_neg_i32; - } else if (opc == INDEX_op_sub_i64) { + break; + case TCG_TYPE_I64: neg_op = INDEX_op_neg_i64; have_neg = TCG_TARGET_HAS_neg_i64; - } else if (TCG_TARGET_HAS_neg_vec) { - TCGType type = TCGOP_VECL(op) + TCG_TYPE_V64; - unsigned vece = TCGOP_VECE(op); - neg_op = INDEX_op_neg_vec; - have_neg = tcg_can_emit_vec_op(neg_op, type, vece) > 0; - } else { break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + neg_op = INDEX_op_neg_vec; + have_neg = tcg_can_emit_vec_op(neg_op, ctx.type, + TCGOP_VECE(op)) > 0; + break; + default: + g_assert_not_reached(); } if (!have_neg) { break; @@ -1476,15 +1497,23 @@ void tcg_optimize(TCGContext *s) TCGOpcode not_op; bool have_not; - if (def->flags & TCG_OPF_VECTOR) { - not_op = INDEX_op_not_vec; - have_not = TCG_TARGET_HAS_not_vec; - } else if (def->flags & TCG_OPF_64BIT) { - not_op = INDEX_op_not_i64; - have_not = TCG_TARGET_HAS_not_i64; - } else { + switch (ctx.type) { + case TCG_TYPE_I32: not_op = INDEX_op_not_i32; have_not = TCG_TARGET_HAS_not_i32; + break; + case TCG_TYPE_I64: + not_op = INDEX_op_not_i64; + have_not = TCG_TARGET_HAS_not_i64; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + not_op = INDEX_op_not_vec; + have_not = TCG_TARGET_HAS_not_vec; + break; + default: + g_assert_not_reached(); } if (!have_not) { break; @@ -1755,7 +1784,7 @@ void tcg_optimize(TCGContext *s) below, we can ignore high bits, but for further optimizations we need to record that the high bits contain garbage. */ partmask = z_mask; - if (!(def->flags & TCG_OPF_64BIT)) { + if (ctx.type == TCG_TYPE_I32) { z_mask |= ~(tcg_target_ulong)0xffffffffu; partmask &= 0xffffffffu; affected &= 0xffffffffu; From 0e0a32bacb29c4313ef195d2ea18809fd25cf5e2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 13:18:01 -0700 Subject: [PATCH 0814/1334] tcg/optimize: Split out fold_to_not Split out the conditional conversion from a more complex logical operation to a simple NOT. Create a couple more helpers to make this easy for the outer-most logical operations. Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 158 +++++++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 72 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index e869fa7e78..21f4251b4f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -694,6 +694,52 @@ static bool fold_const2(OptContext *ctx, TCGOp *op) return false; } +/* + * Convert @op to NOT, if NOT is supported by the host. + * Return true f the conversion is successful, which will still + * indicate that the processing is complete. + */ +static bool fold_not(OptContext *ctx, TCGOp *op); +static bool fold_to_not(OptContext *ctx, TCGOp *op, int idx) +{ + TCGOpcode not_op; + bool have_not; + + switch (ctx->type) { + case TCG_TYPE_I32: + not_op = INDEX_op_not_i32; + have_not = TCG_TARGET_HAS_not_i32; + break; + case TCG_TYPE_I64: + not_op = INDEX_op_not_i64; + have_not = TCG_TARGET_HAS_not_i64; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + not_op = INDEX_op_not_vec; + have_not = TCG_TARGET_HAS_not_vec; + break; + default: + g_assert_not_reached(); + } + if (have_not) { + op->opc = not_op; + op->args[1] = op->args[idx]; + return fold_not(ctx, op); + } + return false; +} + +/* If the binary operation has first argument @i, fold to NOT. */ +static bool fold_ix_to_not(OptContext *ctx, TCGOp *op, uint64_t i) +{ + if (arg_is_const(op->args[1]) && arg_info(op->args[1])->val == i) { + return fold_to_not(ctx, op, 2); + } + return false; +} + /* If the binary operation has second argument @i, fold to @i. */ static bool fold_xi_to_i(OptContext *ctx, TCGOp *op, uint64_t i) { @@ -703,6 +749,15 @@ static bool fold_xi_to_i(OptContext *ctx, TCGOp *op, uint64_t i) return false; } +/* If the binary operation has second argument @i, fold to NOT. */ +static bool fold_xi_to_not(OptContext *ctx, TCGOp *op, uint64_t i) +{ + if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == i) { + return fold_to_not(ctx, op, 1); + } + return false; +} + /* If the binary operation has both arguments equal, fold to @i. */ static bool fold_xx_to_i(OptContext *ctx, TCGOp *op, uint64_t i) { @@ -781,7 +836,8 @@ static bool fold_and(OptContext *ctx, TCGOp *op) static bool fold_andc(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || - fold_xx_to_i(ctx, op, 0)) { + fold_xx_to_i(ctx, op, 0) || + fold_ix_to_not(ctx, op, -1)) { return true; } return false; @@ -987,7 +1043,11 @@ static bool fold_dup2(OptContext *ctx, TCGOp *op) static bool fold_eqv(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xi_to_not(ctx, op, 0)) { + return true; + } + return false; } static bool fold_extract(OptContext *ctx, TCGOp *op) @@ -1134,7 +1194,11 @@ static bool fold_mulu2_i32(OptContext *ctx, TCGOp *op) static bool fold_nand(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xi_to_not(ctx, op, -1)) { + return true; + } + return false; } static bool fold_neg(OptContext *ctx, TCGOp *op) @@ -1144,12 +1208,22 @@ static bool fold_neg(OptContext *ctx, TCGOp *op) static bool fold_nor(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xi_to_not(ctx, op, 0)) { + return true; + } + return false; } static bool fold_not(OptContext *ctx, TCGOp *op) { - return fold_const1(ctx, op); + if (fold_const1(ctx, op)) { + return true; + } + + /* Because of fold_to_not, we want to always return true, via finish. */ + finish_folding(ctx, op); + return true; } static bool fold_or(OptContext *ctx, TCGOp *op) @@ -1163,7 +1237,11 @@ static bool fold_or(OptContext *ctx, TCGOp *op) static bool fold_orc(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_ix_to_not(ctx, op, 0)) { + return true; + } + return false; } static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) @@ -1299,7 +1377,8 @@ static bool fold_sub2_i32(OptContext *ctx, TCGOp *op) static bool fold_xor(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || - fold_xx_to_i(ctx, op, 0)) { + fold_xx_to_i(ctx, op, 0) || + fold_xi_to_not(ctx, op, -1)) { return true; } return false; @@ -1458,71 +1537,6 @@ void tcg_optimize(TCGContext *s) } } break; - CASE_OP_32_64_VEC(xor): - CASE_OP_32_64(nand): - if (!arg_is_const(op->args[1]) - && arg_is_const(op->args[2]) - && arg_info(op->args[2])->val == -1) { - i = 1; - goto try_not; - } - break; - CASE_OP_32_64(nor): - if (!arg_is_const(op->args[1]) - && arg_is_const(op->args[2]) - && arg_info(op->args[2])->val == 0) { - i = 1; - goto try_not; - } - break; - CASE_OP_32_64_VEC(andc): - if (!arg_is_const(op->args[2]) - && arg_is_const(op->args[1]) - && arg_info(op->args[1])->val == -1) { - i = 2; - goto try_not; - } - break; - CASE_OP_32_64_VEC(orc): - CASE_OP_32_64(eqv): - if (!arg_is_const(op->args[2]) - && arg_is_const(op->args[1]) - && arg_info(op->args[1])->val == 0) { - i = 2; - goto try_not; - } - break; - try_not: - { - TCGOpcode not_op; - bool have_not; - - switch (ctx.type) { - case TCG_TYPE_I32: - not_op = INDEX_op_not_i32; - have_not = TCG_TARGET_HAS_not_i32; - break; - case TCG_TYPE_I64: - not_op = INDEX_op_not_i64; - have_not = TCG_TARGET_HAS_not_i64; - break; - case TCG_TYPE_V64: - case TCG_TYPE_V128: - case TCG_TYPE_V256: - not_op = INDEX_op_not_vec; - have_not = TCG_TARGET_HAS_not_vec; - break; - default: - g_assert_not_reached(); - } - if (!have_not) { - break; - } - op->opc = not_op; - reset_temp(op->args[0]); - op->args[1] = op->args[i]; - continue; - } default: break; } From 9caca88a76a6b1e5203dd2470800941c2670a9cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Aug 2021 13:30:32 -0700 Subject: [PATCH 0815/1334] tcg/optimize: Split out fold_sub_to_neg Even though there is only one user, place this more complex conversion into its own helper. Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 89 ++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 21f4251b4f..e0d850ffe4 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1203,7 +1203,15 @@ static bool fold_nand(OptContext *ctx, TCGOp *op) static bool fold_neg(OptContext *ctx, TCGOp *op) { - return fold_const1(ctx, op); + if (fold_const1(ctx, op)) { + return true; + } + /* + * Because of fold_sub_to_neg, we want to always return true, + * via finish_folding. + */ + finish_folding(ctx, op); + return true; } static bool fold_nor(OptContext *ctx, TCGOp *op) @@ -1360,10 +1368,47 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } +static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) +{ + TCGOpcode neg_op; + bool have_neg; + + if (!arg_is_const(op->args[1]) || arg_info(op->args[1])->val != 0) { + return false; + } + + switch (ctx->type) { + case TCG_TYPE_I32: + neg_op = INDEX_op_neg_i32; + have_neg = TCG_TARGET_HAS_neg_i32; + break; + case TCG_TYPE_I64: + neg_op = INDEX_op_neg_i64; + have_neg = TCG_TARGET_HAS_neg_i64; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + neg_op = INDEX_op_neg_vec; + have_neg = (TCG_TARGET_HAS_neg_vec && + tcg_can_emit_vec_op(neg_op, ctx->type, TCGOP_VECE(op)) > 0); + break; + default: + g_assert_not_reached(); + } + if (have_neg) { + op->opc = neg_op; + op->args[1] = op->args[2]; + return fold_neg(ctx, op); + } + return false; +} + static bool fold_sub(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || - fold_xx_to_i(ctx, op, 0)) { + fold_xx_to_i(ctx, op, 0) || + fold_sub_to_neg(ctx, op)) { return true; } return false; @@ -1497,46 +1542,6 @@ void tcg_optimize(TCGContext *s) continue; } break; - CASE_OP_32_64_VEC(sub): - { - TCGOpcode neg_op; - bool have_neg; - - if (arg_is_const(op->args[2])) { - /* Proceed with possible constant folding. */ - break; - } - switch (ctx.type) { - case TCG_TYPE_I32: - neg_op = INDEX_op_neg_i32; - have_neg = TCG_TARGET_HAS_neg_i32; - break; - case TCG_TYPE_I64: - neg_op = INDEX_op_neg_i64; - have_neg = TCG_TARGET_HAS_neg_i64; - break; - case TCG_TYPE_V64: - case TCG_TYPE_V128: - case TCG_TYPE_V256: - neg_op = INDEX_op_neg_vec; - have_neg = tcg_can_emit_vec_op(neg_op, ctx.type, - TCGOP_VECE(op)) > 0; - break; - default: - g_assert_not_reached(); - } - if (!have_neg) { - break; - } - if (arg_is_const(op->args[1]) - && arg_info(op->args[1])->val == 0) { - op->opc = neg_op; - reset_temp(op->args[0]); - op->args[1] = op->args[2]; - continue; - } - } - break; default: break; } From a63ce0e9cb860439d4277bd6dca696bce1f1bb6b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 20:28:53 -0700 Subject: [PATCH 0816/1334] tcg/optimize: Split out fold_xi_to_x Pull the "op r, a, i => mov r, a" optimization into a function, and use them in the outer-most logical operations. Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 61 +++++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index e0d850ffe4..f5ab0500b7 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -749,6 +749,15 @@ static bool fold_xi_to_i(OptContext *ctx, TCGOp *op, uint64_t i) return false; } +/* If the binary operation has second argument @i, fold to identity. */ +static bool fold_xi_to_x(OptContext *ctx, TCGOp *op, uint64_t i) +{ + if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == i) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + return false; +} + /* If the binary operation has second argument @i, fold to NOT. */ static bool fold_xi_to_not(OptContext *ctx, TCGOp *op, uint64_t i) { @@ -787,7 +796,11 @@ static bool fold_xx_to_x(OptContext *ctx, TCGOp *op) static bool fold_add(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xi_to_x(ctx, op, 0)) { + return true; + } + return false; } static bool fold_addsub2_i32(OptContext *ctx, TCGOp *op, bool add) @@ -827,6 +840,7 @@ static bool fold_and(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || fold_xi_to_i(ctx, op, 0) || + fold_xi_to_x(ctx, op, -1) || fold_xx_to_x(ctx, op)) { return true; } @@ -837,6 +851,7 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, 0) || + fold_xi_to_x(ctx, op, 0) || fold_ix_to_not(ctx, op, -1)) { return true; } @@ -1044,6 +1059,7 @@ static bool fold_dup2(OptContext *ctx, TCGOp *op) static bool fold_eqv(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || + fold_xi_to_x(ctx, op, -1) || fold_xi_to_not(ctx, op, 0)) { return true; } @@ -1237,6 +1253,7 @@ static bool fold_not(OptContext *ctx, TCGOp *op) static bool fold_or(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || + fold_xi_to_x(ctx, op, 0) || fold_xx_to_x(ctx, op)) { return true; } @@ -1246,6 +1263,7 @@ static bool fold_or(OptContext *ctx, TCGOp *op) static bool fold_orc(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || + fold_xi_to_x(ctx, op, -1) || fold_ix_to_not(ctx, op, 0)) { return true; } @@ -1365,7 +1383,11 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) static bool fold_shift(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xi_to_x(ctx, op, 0)) { + return true; + } + return false; } static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) @@ -1408,6 +1430,7 @@ static bool fold_sub(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, 0) || + fold_xi_to_x(ctx, op, 0) || fold_sub_to_neg(ctx, op)) { return true; } @@ -1423,6 +1446,7 @@ static bool fold_xor(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, 0) || + fold_xi_to_x(ctx, op, 0) || fold_xi_to_not(ctx, op, -1)) { return true; } @@ -1546,39 +1570,6 @@ void tcg_optimize(TCGContext *s) break; } - /* Simplify expression for "op r, a, const => mov r, a" cases */ - switch (opc) { - CASE_OP_32_64_VEC(add): - CASE_OP_32_64_VEC(sub): - CASE_OP_32_64_VEC(or): - CASE_OP_32_64_VEC(xor): - CASE_OP_32_64_VEC(andc): - CASE_OP_32_64(shl): - CASE_OP_32_64(shr): - CASE_OP_32_64(sar): - CASE_OP_32_64(rotl): - CASE_OP_32_64(rotr): - if (!arg_is_const(op->args[1]) - && arg_is_const(op->args[2]) - && arg_info(op->args[2])->val == 0) { - tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); - continue; - } - break; - CASE_OP_32_64_VEC(and): - CASE_OP_32_64_VEC(orc): - CASE_OP_32_64(eqv): - if (!arg_is_const(op->args[1]) - && arg_is_const(op->args[2]) - && arg_info(op->args[2])->val == -1) { - tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); - continue; - } - break; - default: - break; - } - /* Simplify using known-zero bits. Currently only ops with a single output argument is supported. */ z_mask = -1; From da48e2720227473041b7a14dd9f838577d36833a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 20:42:04 -0700 Subject: [PATCH 0817/1334] tcg/optimize: Split out fold_ix_to_i MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull the "op r, 0, b => movi r, 0" optimization into a function, and use it in fold_shift. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f5ab0500b7..bf74b77355 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -731,6 +731,15 @@ static bool fold_to_not(OptContext *ctx, TCGOp *op, int idx) return false; } +/* If the binary operation has first argument @i, fold to @i. */ +static bool fold_ix_to_i(OptContext *ctx, TCGOp *op, uint64_t i) +{ + if (arg_is_const(op->args[1]) && arg_info(op->args[1])->val == i) { + return tcg_opt_gen_movi(ctx, op, op->args[0], i); + } + return false; +} + /* If the binary operation has first argument @i, fold to NOT. */ static bool fold_ix_to_not(OptContext *ctx, TCGOp *op, uint64_t i) { @@ -1384,6 +1393,7 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) static bool fold_shift(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || + fold_ix_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 0)) { return true; } @@ -1552,24 +1562,6 @@ void tcg_optimize(TCGContext *s) break; } - /* Simplify expressions for "shift/rot r, 0, a => movi r, 0", - and "sub r, 0, a => neg r, a" case. */ - switch (opc) { - CASE_OP_32_64(shl): - CASE_OP_32_64(shr): - CASE_OP_32_64(sar): - CASE_OP_32_64(rotl): - CASE_OP_32_64(rotr): - if (arg_is_const(op->args[1]) - && arg_info(op->args[1])->val == 0) { - tcg_opt_gen_movi(&ctx, op, op->args[0], 0); - continue; - } - break; - default: - break; - } - /* Simplify using known-zero bits. Currently only ops with a single output argument is supported. */ z_mask = -1; From fae450ba4764dcf8c7a131131639ac0969a8eae8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Aug 2021 22:42:19 -0700 Subject: [PATCH 0818/1334] tcg/optimize: Split out fold_masks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move all of the known-zero optimizations into the per-opcode functions. Use fold_masks when there is a possibility of the result being determined, and simply set ctx->z_mask otherwise. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 545 ++++++++++++++++++++++++++----------------------- 1 file changed, 294 insertions(+), 251 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index bf74b77355..e84d10be53 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -50,7 +50,8 @@ typedef struct OptContext { TCGTempSet temps_used; /* In flight values from optimization. */ - uint64_t z_mask; + uint64_t a_mask; /* mask bit is 0 iff value identical to first input */ + uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */ TCGType type; } OptContext; @@ -694,6 +695,31 @@ static bool fold_const2(OptContext *ctx, TCGOp *op) return false; } +static bool fold_masks(OptContext *ctx, TCGOp *op) +{ + uint64_t a_mask = ctx->a_mask; + uint64_t z_mask = ctx->z_mask; + + /* + * 32-bit ops generate 32-bit results. For the result is zero test + * below, we can ignore high bits, but for further optimizations we + * need to record that the high bits contain garbage. + */ + if (ctx->type == TCG_TYPE_I32) { + ctx->z_mask |= MAKE_64BIT_MASK(32, 32); + a_mask &= MAKE_64BIT_MASK(0, 32); + z_mask &= MAKE_64BIT_MASK(0, 32); + } + + if (z_mask == 0) { + return tcg_opt_gen_movi(ctx, op, op->args[0], 0); + } + if (a_mask == 0) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + return false; +} + /* * Convert @op to NOT, if NOT is supported by the host. * Return true f the conversion is successful, which will still @@ -847,24 +873,55 @@ static bool fold_add2_i32(OptContext *ctx, TCGOp *op) static bool fold_and(OptContext *ctx, TCGOp *op) { + uint64_t z1, z2; + if (fold_const2(ctx, op) || fold_xi_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, -1) || fold_xx_to_x(ctx, op)) { return true; } - return false; + + z1 = arg_info(op->args[1])->z_mask; + z2 = arg_info(op->args[2])->z_mask; + ctx->z_mask = z1 & z2; + + /* + * Known-zeros does not imply known-ones. Therefore unless + * arg2 is constant, we can't infer affected bits from it. + */ + if (arg_is_const(op->args[2])) { + ctx->a_mask = z1 & ~z2; + } + + return fold_masks(ctx, op); } static bool fold_andc(OptContext *ctx, TCGOp *op) { + uint64_t z1; + if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 0) || fold_ix_to_not(ctx, op, -1)) { return true; } - return false; + + z1 = arg_info(op->args[1])->z_mask; + + /* + * Known-zeros does not imply known-ones. Therefore unless + * arg2 is constant, we can't infer anything from it. + */ + if (arg_is_const(op->args[2])) { + uint64_t z2 = ~arg_info(op->args[2])->z_mask; + ctx->a_mask = z1 & ~z2; + z1 &= z2; + } + ctx->z_mask = z1; + + return fold_masks(ctx, op); } static bool fold_brcond(OptContext *ctx, TCGOp *op) @@ -963,13 +1020,52 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) static bool fold_bswap(OptContext *ctx, TCGOp *op) { + uint64_t z_mask, sign; + if (arg_is_const(op->args[1])) { uint64_t t = arg_info(op->args[1])->val; t = do_constant_folding(op->opc, ctx->type, t, op->args[2]); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } - return false; + + z_mask = arg_info(op->args[1])->z_mask; + switch (op->opc) { + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + z_mask = bswap16(z_mask); + sign = INT16_MIN; + break; + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: + z_mask = bswap32(z_mask); + sign = INT32_MIN; + break; + case INDEX_op_bswap64_i64: + z_mask = bswap64(z_mask); + sign = INT64_MIN; + break; + default: + g_assert_not_reached(); + } + + switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { + case TCG_BSWAP_OZ: + break; + case TCG_BSWAP_OS: + /* If the sign bit may be 1, force all the bits above to 1. */ + if (z_mask & sign) { + z_mask |= sign; + } + break; + default: + /* The high bits are undefined: force all bits above the sign to 1. */ + z_mask |= sign << 1; + break; + } + ctx->z_mask = z_mask; + + return fold_masks(ctx, op); } static bool fold_call(OptContext *ctx, TCGOp *op) @@ -1006,6 +1102,8 @@ static bool fold_call(OptContext *ctx, TCGOp *op) static bool fold_count_zeros(OptContext *ctx, TCGOp *op) { + uint64_t z_mask; + if (arg_is_const(op->args[1])) { uint64_t t = arg_info(op->args[1])->val; @@ -1015,12 +1113,39 @@ static bool fold_count_zeros(OptContext *ctx, TCGOp *op) } return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[2]); } + + switch (ctx->type) { + case TCG_TYPE_I32: + z_mask = 31; + break; + case TCG_TYPE_I64: + z_mask = 63; + break; + default: + g_assert_not_reached(); + } + ctx->z_mask = arg_info(op->args[2])->z_mask | z_mask; + return false; } static bool fold_ctpop(OptContext *ctx, TCGOp *op) { - return fold_const1(ctx, op); + if (fold_const1(ctx, op)) { + return true; + } + + switch (ctx->type) { + case TCG_TYPE_I32: + ctx->z_mask = 32 | 31; + break; + case TCG_TYPE_I64: + ctx->z_mask = 64 | 63; + break; + default: + g_assert_not_reached(); + } + return false; } static bool fold_deposit(OptContext *ctx, TCGOp *op) @@ -1032,6 +1157,10 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) t1 = deposit64(t1, op->args[3], op->args[4], t2); return tcg_opt_gen_movi(ctx, op, op->args[0], t1); } + + ctx->z_mask = deposit64(arg_info(op->args[1])->z_mask, + op->args[3], op->args[4], + arg_info(op->args[2])->z_mask); return false; } @@ -1077,6 +1206,8 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) static bool fold_extract(OptContext *ctx, TCGOp *op) { + uint64_t z_mask_old, z_mask; + if (arg_is_const(op->args[1])) { uint64_t t; @@ -1084,7 +1215,15 @@ static bool fold_extract(OptContext *ctx, TCGOp *op) t = extract64(t, op->args[2], op->args[3]); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } - return false; + + z_mask_old = arg_info(op->args[1])->z_mask; + z_mask = extract64(z_mask_old, op->args[2], op->args[3]); + if (op->args[2] == 0) { + ctx->a_mask = z_mask_old ^ z_mask; + } + ctx->z_mask = z_mask; + + return fold_masks(ctx, op); } static bool fold_extract2(OptContext *ctx, TCGOp *op) @@ -1108,12 +1247,83 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) static bool fold_exts(OptContext *ctx, TCGOp *op) { - return fold_const1(ctx, op); + uint64_t z_mask_old, z_mask, sign; + bool type_change = false; + + if (fold_const1(ctx, op)) { + return true; + } + + z_mask_old = z_mask = arg_info(op->args[1])->z_mask; + + switch (op->opc) { + CASE_OP_32_64(ext8s): + sign = INT8_MIN; + z_mask = (uint8_t)z_mask; + break; + CASE_OP_32_64(ext16s): + sign = INT16_MIN; + z_mask = (uint16_t)z_mask; + break; + case INDEX_op_ext_i32_i64: + type_change = true; + QEMU_FALLTHROUGH; + case INDEX_op_ext32s_i64: + sign = INT32_MIN; + z_mask = (uint32_t)z_mask; + break; + default: + g_assert_not_reached(); + } + + if (z_mask & sign) { + z_mask |= sign; + } else if (!type_change) { + ctx->a_mask = z_mask_old ^ z_mask; + } + ctx->z_mask = z_mask; + + return fold_masks(ctx, op); } static bool fold_extu(OptContext *ctx, TCGOp *op) { - return fold_const1(ctx, op); + uint64_t z_mask_old, z_mask; + bool type_change = false; + + if (fold_const1(ctx, op)) { + return true; + } + + z_mask_old = z_mask = arg_info(op->args[1])->z_mask; + + switch (op->opc) { + CASE_OP_32_64(ext8u): + z_mask = (uint8_t)z_mask; + break; + CASE_OP_32_64(ext16u): + z_mask = (uint16_t)z_mask; + break; + case INDEX_op_extrl_i64_i32: + case INDEX_op_extu_i32_i64: + type_change = true; + QEMU_FALLTHROUGH; + case INDEX_op_ext32u_i64: + z_mask = (uint32_t)z_mask; + break; + case INDEX_op_extrh_i64_i32: + type_change = true; + z_mask >>= 32; + break; + default: + g_assert_not_reached(); + } + + ctx->z_mask = z_mask; + if (!type_change) { + ctx->a_mask = z_mask_old ^ z_mask; + } + return fold_masks(ctx, op); } static bool fold_mb(OptContext *ctx, TCGOp *op) @@ -1154,6 +1364,9 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[4 - i]); } + ctx->z_mask = arg_info(op->args[3])->z_mask + | arg_info(op->args[4])->z_mask; + if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { uint64_t tv = arg_info(op->args[3])->val; uint64_t fv = arg_info(op->args[4])->val; @@ -1228,9 +1441,16 @@ static bool fold_nand(OptContext *ctx, TCGOp *op) static bool fold_neg(OptContext *ctx, TCGOp *op) { + uint64_t z_mask; + if (fold_const1(ctx, op)) { return true; } + + /* Set to 1 all bits to the left of the rightmost. */ + z_mask = arg_info(op->args[1])->z_mask; + ctx->z_mask = -(z_mask & -z_mask); + /* * Because of fold_sub_to_neg, we want to always return true, * via finish_folding. @@ -1266,7 +1486,10 @@ static bool fold_or(OptContext *ctx, TCGOp *op) fold_xx_to_x(ctx, op)) { return true; } - return false; + + ctx->z_mask = arg_info(op->args[1])->z_mask + | arg_info(op->args[2])->z_mask; + return fold_masks(ctx, op); } static bool fold_orc(OptContext *ctx, TCGOp *op) @@ -1281,6 +1504,15 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) { + const TCGOpDef *def = &tcg_op_defs[op->opc]; + MemOpIdx oi = op->args[def->nb_oargs + def->nb_iargs]; + MemOp mop = get_memop(oi); + int width = 8 * memop_size(mop); + + if (!(mop & MO_SIGN) && width < 64) { + ctx->z_mask = MAKE_64BIT_MASK(0, width); + } + /* Opcodes that touch guest memory stop the mb optimization. */ ctx->prev_mb = NULL; return false; @@ -1306,6 +1538,8 @@ static bool fold_setcond(OptContext *ctx, TCGOp *op) if (i >= 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], i); } + + ctx->z_mask = 1; return false; } @@ -1372,6 +1606,8 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) op->opc = INDEX_op_setcond_i32; break; } + + ctx->z_mask = 1; return false; do_setcond_const: @@ -1380,6 +1616,8 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) static bool fold_sextract(OptContext *ctx, TCGOp *op) { + int64_t z_mask_old, z_mask; + if (arg_is_const(op->args[1])) { uint64_t t; @@ -1387,7 +1625,15 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) t = sextract64(t, op->args[2], op->args[3]); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } - return false; + + z_mask_old = arg_info(op->args[1])->z_mask; + z_mask = sextract64(z_mask_old, op->args[2], op->args[3]); + if (op->args[2] == 0 && z_mask >= 0) { + ctx->a_mask = z_mask_old ^ z_mask; + } + ctx->z_mask = z_mask; + + return fold_masks(ctx, op); } static bool fold_shift(OptContext *ctx, TCGOp *op) @@ -1397,6 +1643,13 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) fold_xi_to_x(ctx, op, 0)) { return true; } + + if (arg_is_const(op->args[2])) { + ctx->z_mask = do_constant_folding(op->opc, ctx->type, + arg_info(op->args[1])->z_mask, + arg_info(op->args[2])->val); + return fold_masks(ctx, op); + } return false; } @@ -1452,6 +1705,25 @@ static bool fold_sub2_i32(OptContext *ctx, TCGOp *op) return fold_addsub2_i32(ctx, op, false); } +static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) +{ + /* We can't do any folding with a load, but we can record bits. */ + switch (op->opc) { + CASE_OP_32_64(ld8u): + ctx->z_mask = MAKE_64BIT_MASK(0, 8); + break; + CASE_OP_32_64(ld16u): + ctx->z_mask = MAKE_64BIT_MASK(0, 16); + break; + case INDEX_op_ld32u_i64: + ctx->z_mask = MAKE_64BIT_MASK(0, 32); + break; + default: + g_assert_not_reached(); + } + return false; +} + static bool fold_xor(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || @@ -1460,7 +1732,10 @@ static bool fold_xor(OptContext *ctx, TCGOp *op) fold_xi_to_not(ctx, op, -1)) { return true; } - return false; + + ctx->z_mask = arg_info(op->args[1])->z_mask + | arg_info(op->args[2])->z_mask; + return fold_masks(ctx, op); } /* Propagate constants and copies, fold constant expressions. */ @@ -1481,7 +1756,6 @@ void tcg_optimize(TCGContext *s) } QTAILQ_FOREACH_SAFE(op, &s->ops, link, op_next) { - uint64_t z_mask, partmask, affected, tmp; TCGOpcode opc = op->opc; const TCGOpDef *def; bool done = false; @@ -1562,245 +1836,9 @@ void tcg_optimize(TCGContext *s) break; } - /* Simplify using known-zero bits. Currently only ops with a single - output argument is supported. */ - z_mask = -1; - affected = -1; - switch (opc) { - CASE_OP_32_64(ext8s): - if ((arg_info(op->args[1])->z_mask & 0x80) != 0) { - break; - } - QEMU_FALLTHROUGH; - CASE_OP_32_64(ext8u): - z_mask = 0xff; - goto and_const; - CASE_OP_32_64(ext16s): - if ((arg_info(op->args[1])->z_mask & 0x8000) != 0) { - break; - } - QEMU_FALLTHROUGH; - CASE_OP_32_64(ext16u): - z_mask = 0xffff; - goto and_const; - case INDEX_op_ext32s_i64: - if ((arg_info(op->args[1])->z_mask & 0x80000000) != 0) { - break; - } - QEMU_FALLTHROUGH; - case INDEX_op_ext32u_i64: - z_mask = 0xffffffffU; - goto and_const; - - CASE_OP_32_64(and): - z_mask = arg_info(op->args[2])->z_mask; - if (arg_is_const(op->args[2])) { - and_const: - affected = arg_info(op->args[1])->z_mask & ~z_mask; - } - z_mask = arg_info(op->args[1])->z_mask & z_mask; - break; - - case INDEX_op_ext_i32_i64: - if ((arg_info(op->args[1])->z_mask & 0x80000000) != 0) { - break; - } - QEMU_FALLTHROUGH; - case INDEX_op_extu_i32_i64: - /* We do not compute affected as it is a size changing op. */ - z_mask = (uint32_t)arg_info(op->args[1])->z_mask; - break; - - CASE_OP_32_64(andc): - /* Known-zeros does not imply known-ones. Therefore unless - op->args[2] is constant, we can't infer anything from it. */ - if (arg_is_const(op->args[2])) { - z_mask = ~arg_info(op->args[2])->z_mask; - goto and_const; - } - /* But we certainly know nothing outside args[1] may be set. */ - z_mask = arg_info(op->args[1])->z_mask; - break; - - case INDEX_op_sar_i32: - if (arg_is_const(op->args[2])) { - tmp = arg_info(op->args[2])->val & 31; - z_mask = (int32_t)arg_info(op->args[1])->z_mask >> tmp; - } - break; - case INDEX_op_sar_i64: - if (arg_is_const(op->args[2])) { - tmp = arg_info(op->args[2])->val & 63; - z_mask = (int64_t)arg_info(op->args[1])->z_mask >> tmp; - } - break; - - case INDEX_op_shr_i32: - if (arg_is_const(op->args[2])) { - tmp = arg_info(op->args[2])->val & 31; - z_mask = (uint32_t)arg_info(op->args[1])->z_mask >> tmp; - } - break; - case INDEX_op_shr_i64: - if (arg_is_const(op->args[2])) { - tmp = arg_info(op->args[2])->val & 63; - z_mask = (uint64_t)arg_info(op->args[1])->z_mask >> tmp; - } - break; - - case INDEX_op_extrl_i64_i32: - z_mask = (uint32_t)arg_info(op->args[1])->z_mask; - break; - case INDEX_op_extrh_i64_i32: - z_mask = (uint64_t)arg_info(op->args[1])->z_mask >> 32; - break; - - CASE_OP_32_64(shl): - if (arg_is_const(op->args[2])) { - tmp = arg_info(op->args[2])->val & (TCG_TARGET_REG_BITS - 1); - z_mask = arg_info(op->args[1])->z_mask << tmp; - } - break; - - CASE_OP_32_64(neg): - /* Set to 1 all bits to the left of the rightmost. */ - z_mask = -(arg_info(op->args[1])->z_mask - & -arg_info(op->args[1])->z_mask); - break; - - CASE_OP_32_64(deposit): - z_mask = deposit64(arg_info(op->args[1])->z_mask, - op->args[3], op->args[4], - arg_info(op->args[2])->z_mask); - break; - - CASE_OP_32_64(extract): - z_mask = extract64(arg_info(op->args[1])->z_mask, - op->args[2], op->args[3]); - if (op->args[2] == 0) { - affected = arg_info(op->args[1])->z_mask & ~z_mask; - } - break; - CASE_OP_32_64(sextract): - z_mask = sextract64(arg_info(op->args[1])->z_mask, - op->args[2], op->args[3]); - if (op->args[2] == 0 && (tcg_target_long)z_mask >= 0) { - affected = arg_info(op->args[1])->z_mask & ~z_mask; - } - break; - - CASE_OP_32_64(or): - CASE_OP_32_64(xor): - z_mask = arg_info(op->args[1])->z_mask - | arg_info(op->args[2])->z_mask; - break; - - case INDEX_op_clz_i32: - case INDEX_op_ctz_i32: - z_mask = arg_info(op->args[2])->z_mask | 31; - break; - - case INDEX_op_clz_i64: - case INDEX_op_ctz_i64: - z_mask = arg_info(op->args[2])->z_mask | 63; - break; - - case INDEX_op_ctpop_i32: - z_mask = 32 | 31; - break; - case INDEX_op_ctpop_i64: - z_mask = 64 | 63; - break; - - CASE_OP_32_64(setcond): - case INDEX_op_setcond2_i32: - z_mask = 1; - break; - - CASE_OP_32_64(movcond): - z_mask = arg_info(op->args[3])->z_mask - | arg_info(op->args[4])->z_mask; - break; - - CASE_OP_32_64(ld8u): - z_mask = 0xff; - break; - CASE_OP_32_64(ld16u): - z_mask = 0xffff; - break; - case INDEX_op_ld32u_i64: - z_mask = 0xffffffffu; - break; - - CASE_OP_32_64(qemu_ld): - { - MemOpIdx oi = op->args[def->nb_oargs + def->nb_iargs]; - MemOp mop = get_memop(oi); - if (!(mop & MO_SIGN)) { - z_mask = (2ULL << ((8 << (mop & MO_SIZE)) - 1)) - 1; - } - } - break; - - CASE_OP_32_64(bswap16): - z_mask = arg_info(op->args[1])->z_mask; - if (z_mask <= 0xffff) { - op->args[2] |= TCG_BSWAP_IZ; - } - z_mask = bswap16(z_mask); - switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { - case TCG_BSWAP_OZ: - break; - case TCG_BSWAP_OS: - z_mask = (int16_t)z_mask; - break; - default: /* undefined high bits */ - z_mask |= MAKE_64BIT_MASK(16, 48); - break; - } - break; - - case INDEX_op_bswap32_i64: - z_mask = arg_info(op->args[1])->z_mask; - if (z_mask <= 0xffffffffu) { - op->args[2] |= TCG_BSWAP_IZ; - } - z_mask = bswap32(z_mask); - switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { - case TCG_BSWAP_OZ: - break; - case TCG_BSWAP_OS: - z_mask = (int32_t)z_mask; - break; - default: /* undefined high bits */ - z_mask |= MAKE_64BIT_MASK(32, 32); - break; - } - break; - - default: - break; - } - - /* 32-bit ops generate 32-bit results. For the result is zero test - below, we can ignore high bits, but for further optimizations we - need to record that the high bits contain garbage. */ - partmask = z_mask; - if (ctx.type == TCG_TYPE_I32) { - z_mask |= ~(tcg_target_ulong)0xffffffffu; - partmask &= 0xffffffffu; - affected &= 0xffffffffu; - } - ctx.z_mask = z_mask; - - if (partmask == 0) { - tcg_opt_gen_movi(&ctx, op, op->args[0], 0); - continue; - } - if (affected == 0) { - tcg_opt_gen_mov(&ctx, op, op->args[0], op->args[1]); - continue; - } + /* Assume all bits affected, and no bits known zero. */ + ctx.a_mask = -1; + ctx.z_mask = -1; /* * Process each opcode. @@ -1873,6 +1911,11 @@ void tcg_optimize(TCGContext *s) case INDEX_op_extrh_i64_i32: done = fold_extu(&ctx, op); break; + CASE_OP_32_64(ld8u): + CASE_OP_32_64(ld16u): + case INDEX_op_ld32u_i64: + done = fold_tcg_ld(&ctx, op); + break; case INDEX_op_mb: done = fold_mb(&ctx, op); break; From 407112b03d7665a3cb7b3a21105e721a8a0c5fd8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 06:33:04 -0700 Subject: [PATCH 0819/1334] tcg/optimize: Expand fold_mulu2_i32 to all 4-arg multiplies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename to fold_multiply2, and handle muls2_i32, mulu2_i64, and muls2_i64. Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index e84d10be53..e2ecad2884 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1412,19 +1412,44 @@ static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) return false; } -static bool fold_mulu2_i32(OptContext *ctx, TCGOp *op) +static bool fold_multiply2(OptContext *ctx, TCGOp *op) { if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { - uint32_t a = arg_info(op->args[2])->val; - uint32_t b = arg_info(op->args[3])->val; - uint64_t r = (uint64_t)a * b; + uint64_t a = arg_info(op->args[2])->val; + uint64_t b = arg_info(op->args[3])->val; + uint64_t h, l; TCGArg rl, rh; - TCGOp *op2 = tcg_op_insert_before(ctx->tcg, op, INDEX_op_mov_i32); + TCGOp *op2; + + switch (op->opc) { + case INDEX_op_mulu2_i32: + l = (uint64_t)(uint32_t)a * (uint32_t)b; + h = (int32_t)(l >> 32); + l = (int32_t)l; + break; + case INDEX_op_muls2_i32: + l = (int64_t)(int32_t)a * (int32_t)b; + h = l >> 32; + l = (int32_t)l; + break; + case INDEX_op_mulu2_i64: + mulu64(&l, &h, a, b); + break; + case INDEX_op_muls2_i64: + muls64(&l, &h, a, b); + break; + default: + g_assert_not_reached(); + } rl = op->args[0]; rh = op->args[1]; - tcg_opt_gen_movi(ctx, op, rl, (int32_t)r); - tcg_opt_gen_movi(ctx, op2, rh, (int32_t)(r >> 32)); + + /* The proper opcode is supplied by tcg_opt_gen_mov. */ + op2 = tcg_op_insert_before(ctx->tcg, op, 0); + + tcg_opt_gen_movi(ctx, op, rl, l); + tcg_opt_gen_movi(ctx, op2, rh, h); return true; } return false; @@ -1932,8 +1957,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(muluh): done = fold_mul_highpart(&ctx, op); break; - case INDEX_op_mulu2_i32: - done = fold_mulu2_i32(&ctx, op); + CASE_OP_32_64(muls2): + CASE_OP_32_64(mulu2): + done = fold_multiply2(&ctx, op); break; CASE_OP_32_64(nand): done = fold_nand(&ctx, op); From 9531c078ff63783f2a21ba302827123e7b073304 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 06:51:39 -0700 Subject: [PATCH 0820/1334] tcg/optimize: Expand fold_addsub2_i32 to 64-bit ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename to fold_addsub2. Use Int128 to implement the wider operation. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 65 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index e2ecad2884..f723deaafe 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/int128.h" #include "tcg/tcg-op.h" #include "tcg-internal.h" @@ -838,37 +839,59 @@ static bool fold_add(OptContext *ctx, TCGOp *op) return false; } -static bool fold_addsub2_i32(OptContext *ctx, TCGOp *op, bool add) +static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) { if (arg_is_const(op->args[2]) && arg_is_const(op->args[3]) && arg_is_const(op->args[4]) && arg_is_const(op->args[5])) { - uint32_t al = arg_info(op->args[2])->val; - uint32_t ah = arg_info(op->args[3])->val; - uint32_t bl = arg_info(op->args[4])->val; - uint32_t bh = arg_info(op->args[5])->val; - uint64_t a = ((uint64_t)ah << 32) | al; - uint64_t b = ((uint64_t)bh << 32) | bl; + uint64_t al = arg_info(op->args[2])->val; + uint64_t ah = arg_info(op->args[3])->val; + uint64_t bl = arg_info(op->args[4])->val; + uint64_t bh = arg_info(op->args[5])->val; TCGArg rl, rh; - TCGOp *op2 = tcg_op_insert_before(ctx->tcg, op, INDEX_op_mov_i32); + TCGOp *op2; - if (add) { - a += b; + if (ctx->type == TCG_TYPE_I32) { + uint64_t a = deposit64(al, 32, 32, ah); + uint64_t b = deposit64(bl, 32, 32, bh); + + if (add) { + a += b; + } else { + a -= b; + } + + al = sextract64(a, 0, 32); + ah = sextract64(a, 32, 32); } else { - a -= b; + Int128 a = int128_make128(al, ah); + Int128 b = int128_make128(bl, bh); + + if (add) { + a = int128_add(a, b); + } else { + a = int128_sub(a, b); + } + + al = int128_getlo(a); + ah = int128_gethi(a); } rl = op->args[0]; rh = op->args[1]; - tcg_opt_gen_movi(ctx, op, rl, (int32_t)a); - tcg_opt_gen_movi(ctx, op2, rh, (int32_t)(a >> 32)); + + /* The proper opcode is supplied by tcg_opt_gen_mov. */ + op2 = tcg_op_insert_before(ctx->tcg, op, 0); + + tcg_opt_gen_movi(ctx, op, rl, al); + tcg_opt_gen_movi(ctx, op2, rh, ah); return true; } return false; } -static bool fold_add2_i32(OptContext *ctx, TCGOp *op) +static bool fold_add2(OptContext *ctx, TCGOp *op) { - return fold_addsub2_i32(ctx, op, true); + return fold_addsub2(ctx, op, true); } static bool fold_and(OptContext *ctx, TCGOp *op) @@ -1725,9 +1748,9 @@ static bool fold_sub(OptContext *ctx, TCGOp *op) return false; } -static bool fold_sub2_i32(OptContext *ctx, TCGOp *op) +static bool fold_sub2(OptContext *ctx, TCGOp *op) { - return fold_addsub2_i32(ctx, op, false); + return fold_addsub2(ctx, op, false); } static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) @@ -1873,8 +1896,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(add): done = fold_add(&ctx, op); break; - case INDEX_op_add2_i32: - done = fold_add2_i32(&ctx, op); + CASE_OP_32_64(add2): + done = fold_add2(&ctx, op); break; CASE_OP_32_64_VEC(and): done = fold_and(&ctx, op); @@ -2011,8 +2034,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(sub): done = fold_sub(&ctx, op); break; - case INDEX_op_sub2_i32: - done = fold_sub2_i32(&ctx, op); + CASE_OP_32_64(sub2): + done = fold_sub2(&ctx, op); break; CASE_OP_32_64_VEC(xor): done = fold_xor(&ctx, op); From 7a2f70845255bd6a2ad6399d159c58a7e7b64b51 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 07:06:39 -0700 Subject: [PATCH 0821/1334] tcg/optimize: Sink commutative operand swapping into fold functions Most of these are handled by creating a fold_const2_commutative to handle all of the binary operators. The rest were already handled on a case-by-case basis in the switch, and have their own fold function in which to place the call. We now have only one major switch on TCGOpcode. Introduce NO_DEST and a block comment for swap_commutative in order to make the handling of brcond and movcond opcodes cleaner. Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 142 ++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 72 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f723deaafe..e42f5a145f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -577,6 +577,19 @@ static int do_constant_folding_cond2(TCGArg *p1, TCGArg *p2, TCGCond c) return -1; } +/** + * swap_commutative: + * @dest: TCGArg of the destination argument, or NO_DEST. + * @p1: first paired argument + * @p2: second paired argument + * + * If *@p1 is a constant and *@p2 is not, swap. + * If *@p2 matches @dest, swap. + * Return true if a swap was performed. + */ + +#define NO_DEST temp_arg(NULL) + static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) { TCGArg a1 = *p1, a2 = *p2; @@ -696,6 +709,12 @@ static bool fold_const2(OptContext *ctx, TCGOp *op) return false; } +static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) +{ + swap_commutative(op->args[0], &op->args[1], &op->args[2]); + return fold_const2(ctx, op); +} + static bool fold_masks(OptContext *ctx, TCGOp *op) { uint64_t a_mask = ctx->a_mask; @@ -832,7 +851,7 @@ static bool fold_xx_to_x(OptContext *ctx, TCGOp *op) static bool fold_add(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, 0)) { return true; } @@ -891,6 +910,10 @@ static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) static bool fold_add2(OptContext *ctx, TCGOp *op) { + /* Note that the high and low parts may be independently swapped. */ + swap_commutative(op->args[0], &op->args[2], &op->args[4]); + swap_commutative(op->args[1], &op->args[3], &op->args[5]); + return fold_addsub2(ctx, op, true); } @@ -898,7 +921,7 @@ static bool fold_and(OptContext *ctx, TCGOp *op) { uint64_t z1, z2; - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, -1) || fold_xx_to_x(ctx, op)) { @@ -950,8 +973,13 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) static bool fold_brcond(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[2]; - int i = do_constant_folding_cond(ctx->type, op->args[0], op->args[1], cond); + int i; + if (swap_commutative(NO_DEST, &op->args[0], &op->args[1])) { + op->args[2] = cond = tcg_swap_cond(cond); + } + + i = do_constant_folding_cond(ctx->type, op->args[0], op->args[1], cond); if (i == 0) { tcg_op_remove(ctx->tcg, op); return true; @@ -966,10 +994,14 @@ static bool fold_brcond(OptContext *ctx, TCGOp *op) static bool fold_brcond2(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[4]; - int i = do_constant_folding_cond2(&op->args[0], &op->args[2], cond); TCGArg label = op->args[5]; - int inv = 0; + int i, inv = 0; + if (swap_commutative2(&op->args[0], &op->args[2])) { + op->args[4] = cond = tcg_swap_cond(cond); + } + + i = do_constant_folding_cond2(&op->args[0], &op->args[2], cond); if (i >= 0) { goto do_brcond_const; } @@ -1219,7 +1251,7 @@ static bool fold_dup2(OptContext *ctx, TCGOp *op) static bool fold_eqv(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, -1) || fold_xi_to_not(ctx, op, 0)) { return true; @@ -1381,8 +1413,20 @@ static bool fold_mov(OptContext *ctx, TCGOp *op) static bool fold_movcond(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[5]; - int i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); + int i; + if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { + op->args[5] = cond = tcg_swap_cond(cond); + } + /* + * Canonicalize the "false" input reg to match the destination reg so + * that the tcg backend can implement a "move if true" operation. + */ + if (swap_commutative(op->args[0], &op->args[4], &op->args[3])) { + op->args[5] = cond = tcg_invert_cond(cond); + } + + i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); if (i >= 0) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[4 - i]); } @@ -1428,7 +1472,7 @@ static bool fold_mul(OptContext *ctx, TCGOp *op) static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_i(ctx, op, 0)) { return true; } @@ -1437,6 +1481,8 @@ static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) static bool fold_multiply2(OptContext *ctx, TCGOp *op) { + swap_commutative(op->args[0], &op->args[2], &op->args[3]); + if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { uint64_t a = arg_info(op->args[2])->val; uint64_t b = arg_info(op->args[3])->val; @@ -1480,7 +1526,7 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) static bool fold_nand(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_not(ctx, op, -1)) { return true; } @@ -1509,7 +1555,7 @@ static bool fold_neg(OptContext *ctx, TCGOp *op) static bool fold_nor(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_not(ctx, op, 0)) { return true; } @@ -1529,7 +1575,7 @@ static bool fold_not(OptContext *ctx, TCGOp *op) static bool fold_or(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, 0) || fold_xx_to_x(ctx, op)) { return true; @@ -1581,8 +1627,13 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) static bool fold_setcond(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[3]; - int i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); + int i; + if (swap_commutative(op->args[0], &op->args[1], &op->args[2])) { + op->args[3] = cond = tcg_swap_cond(cond); + } + + i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); if (i >= 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], i); } @@ -1594,9 +1645,13 @@ static bool fold_setcond(OptContext *ctx, TCGOp *op) static bool fold_setcond2(OptContext *ctx, TCGOp *op) { TCGCond cond = op->args[5]; - int i = do_constant_folding_cond2(&op->args[1], &op->args[3], cond); - int inv = 0; + int i, inv = 0; + if (swap_commutative2(&op->args[1], &op->args[3])) { + op->args[5] = cond = tcg_swap_cond(cond); + } + + i = do_constant_folding_cond2(&op->args[1], &op->args[3], cond); if (i >= 0) { goto do_setcond_const; } @@ -1774,7 +1829,7 @@ static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) static bool fold_xor(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xx_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 0) || fold_xi_to_not(ctx, op, -1)) { @@ -1827,63 +1882,6 @@ void tcg_optimize(TCGContext *s) ctx.type = TCG_TYPE_I32; } - /* For commutative operations make constant second argument */ - switch (opc) { - CASE_OP_32_64_VEC(add): - CASE_OP_32_64_VEC(mul): - CASE_OP_32_64_VEC(and): - CASE_OP_32_64_VEC(or): - CASE_OP_32_64_VEC(xor): - CASE_OP_32_64(eqv): - CASE_OP_32_64(nand): - CASE_OP_32_64(nor): - CASE_OP_32_64(muluh): - CASE_OP_32_64(mulsh): - swap_commutative(op->args[0], &op->args[1], &op->args[2]); - break; - CASE_OP_32_64(brcond): - if (swap_commutative(-1, &op->args[0], &op->args[1])) { - op->args[2] = tcg_swap_cond(op->args[2]); - } - break; - CASE_OP_32_64(setcond): - if (swap_commutative(op->args[0], &op->args[1], &op->args[2])) { - op->args[3] = tcg_swap_cond(op->args[3]); - } - break; - CASE_OP_32_64(movcond): - if (swap_commutative(-1, &op->args[1], &op->args[2])) { - op->args[5] = tcg_swap_cond(op->args[5]); - } - /* For movcond, we canonicalize the "false" input reg to match - the destination reg so that the tcg backend can implement - a "move if true" operation. */ - if (swap_commutative(op->args[0], &op->args[4], &op->args[3])) { - op->args[5] = tcg_invert_cond(op->args[5]); - } - break; - CASE_OP_32_64(add2): - swap_commutative(op->args[0], &op->args[2], &op->args[4]); - swap_commutative(op->args[1], &op->args[3], &op->args[5]); - break; - CASE_OP_32_64(mulu2): - CASE_OP_32_64(muls2): - swap_commutative(op->args[0], &op->args[2], &op->args[3]); - break; - case INDEX_op_brcond2_i32: - if (swap_commutative2(&op->args[0], &op->args[2])) { - op->args[4] = tcg_swap_cond(op->args[4]); - } - break; - case INDEX_op_setcond2_i32: - if (swap_commutative2(&op->args[1], &op->args[3])) { - op->args[5] = tcg_swap_cond(op->args[5]); - } - break; - default: - break; - } - /* Assume all bits affected, and no bits known zero. */ ctx.a_mask = -1; ctx.z_mask = -1; From 9925c8bb81d34339ea0433192fdb1d58c12b8edb Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 22 Oct 2021 16:01:29 +1000 Subject: [PATCH 0822/1334] hw/riscv: virt: Don't use a macro for the PLIC configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using a macro for the PLIC configuration doesn't make the code any easier to read. Instead it makes it harder to figure out what is going on, so let's remove it. Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-id: 20211022060133.3045020-1-alistair.francis@opensource.wdc.com --- hw/riscv/virt.c | 2 +- include/hw/riscv/virt.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index b3b431c847..28a5909a3b 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -758,7 +758,7 @@ static char *plic_hart_config_string(int hart_count) int i; for (i = 0; i < hart_count; i++) { - vals[i] = VIRT_PLIC_HART_CONFIG; + vals[i] = "MS"; } vals[i] = NULL; diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index d9105c1886..b8ef99f348 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -73,7 +73,6 @@ enum { VIRTIO_NDEV = 0x35 /* Arbitrary maximum number of interrupts */ }; -#define VIRT_PLIC_HART_CONFIG "MS" #define VIRT_PLIC_NUM_SOURCES 127 #define VIRT_PLIC_NUM_PRIORITIES 7 #define VIRT_PLIC_PRIORITY_BASE 0x04 From bf357e1d72cd8b7b590518dacdf4b65beb2c61e2 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 22 Oct 2021 16:01:30 +1000 Subject: [PATCH 0823/1334] hw/riscv: boot: Add a PLIC config string function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a generic function that can create the PLIC strings. Signed-off-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Message-id: 20211022060133.3045020-2-alistair.francis@opensource.wdc.com --- hw/riscv/boot.c | 25 +++++++++++++++++++++++++ include/hw/riscv/boot.h | 2 ++ 2 files changed, 27 insertions(+) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index d1ffc7b56c..519fa455a1 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -38,6 +38,31 @@ bool riscv_is_32bit(RISCVHartArrayState *harts) return harts->harts[0].env.misa_mxl_max == MXL_RV32; } +/* + * Return the per-socket PLIC hart topology configuration string + * (caller must free with g_free()) + */ +char *riscv_plic_hart_config_string(int hart_count) +{ + g_autofree const char **vals = g_new(const char *, hart_count + 1); + int i; + + for (i = 0; i < hart_count; i++) { + CPUState *cs = qemu_get_cpu(i); + CPURISCVState *env = &RISCV_CPU(cs)->env; + + if (riscv_has_ext(env, RVS)) { + vals[i] = "MS"; + } else { + vals[i] = "M"; + } + } + vals[i] = NULL; + + /* g_strjoinv() obliges us to cast away const here */ + return g_strjoinv(",", (char **)vals); +} + target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, target_ulong firmware_end_addr) { if (riscv_is_32bit(harts)) { diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index 0e89400b09..baff11dd8a 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -31,6 +31,8 @@ bool riscv_is_32bit(RISCVHartArrayState *harts); +char *riscv_plic_hart_config_string(int hart_count); + target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, target_ulong firmware_end_addr); target_ulong riscv_find_and_load_firmware(MachineState *machine, From 4e8fb53c0b58cbb18cd243a5b067e4f26db83f77 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 22 Oct 2021 16:01:31 +1000 Subject: [PATCH 0824/1334] hw/riscv: sifive_u: Use the PLIC config helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Tested-by: Bin Meng Message-id: 20211022060133.3045020-3-alistair.francis@opensource.wdc.com --- hw/riscv/sifive_u.c | 14 +------------- include/hw/riscv/sifive_u.h | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 0217006c27..589ae72a59 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -811,7 +811,6 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) MemoryRegion *mask_rom = g_new(MemoryRegion, 1); MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1); char *plic_hart_config; - size_t plic_hart_config_len; int i, j; NICInfo *nd = &nd_table[0]; @@ -852,18 +851,7 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) l2lim_mem); /* create PLIC hart topology configuration string */ - plic_hart_config_len = (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1) * - ms->smp.cpus; - plic_hart_config = g_malloc0(plic_hart_config_len); - for (i = 0; i < ms->smp.cpus; i++) { - if (i != 0) { - strncat(plic_hart_config, "," SIFIVE_U_PLIC_HART_CONFIG, - plic_hart_config_len); - } else { - strncat(plic_hart_config, "M", plic_hart_config_len); - } - plic_hart_config_len -= (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1); - } + plic_hart_config = riscv_plic_hart_config_string(ms->smp.cpus); /* MMIO */ s->plic = sifive_plic_create(memmap[SIFIVE_U_DEV_PLIC].base, diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index f71c90c94c..8f63a183c4 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -156,7 +156,6 @@ enum { #define SIFIVE_U_MANAGEMENT_CPU_COUNT 1 #define SIFIVE_U_COMPUTE_CPU_COUNT 4 -#define SIFIVE_U_PLIC_HART_CONFIG "MS" #define SIFIVE_U_PLIC_NUM_SOURCES 54 #define SIFIVE_U_PLIC_NUM_PRIORITIES 7 #define SIFIVE_U_PLIC_PRIORITY_BASE 0x04 From 8486eb8cdcd336de8ae52d95da45af97f54db63e Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 22 Oct 2021 16:01:32 +1000 Subject: [PATCH 0825/1334] hw/riscv: microchip_pfsoc: Use the PLIC config helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Tested-by: Bin Meng Message-id: 20211022060133.3045020-4-alistair.francis@opensource.wdc.com --- hw/riscv/microchip_pfsoc.c | 14 +------------- include/hw/riscv/microchip_pfsoc.h | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 3fc8545562..57d779fb55 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -187,7 +187,6 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) MemoryRegion *envm_data = g_new(MemoryRegion, 1); MemoryRegion *qspi_xip_mem = g_new(MemoryRegion, 1); char *plic_hart_config; - size_t plic_hart_config_len; NICInfo *nd; int i; @@ -262,18 +261,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) l2lim_mem); /* create PLIC hart topology configuration string */ - plic_hart_config_len = (strlen(MICROCHIP_PFSOC_PLIC_HART_CONFIG) + 1) * - ms->smp.cpus; - plic_hart_config = g_malloc0(plic_hart_config_len); - for (i = 0; i < ms->smp.cpus; i++) { - if (i != 0) { - strncat(plic_hart_config, "," MICROCHIP_PFSOC_PLIC_HART_CONFIG, - plic_hart_config_len); - } else { - strncat(plic_hart_config, "M", plic_hart_config_len); - } - plic_hart_config_len -= (strlen(MICROCHIP_PFSOC_PLIC_HART_CONFIG) + 1); - } + plic_hart_config = riscv_plic_hart_config_string(ms->smp.cpus); /* PLIC */ s->plic = sifive_plic_create(memmap[MICROCHIP_PFSOC_PLIC].base, diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index d30916f45d..a0673f5f59 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -138,7 +138,6 @@ enum { #define MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT 1 #define MICROCHIP_PFSOC_COMPUTE_CPU_COUNT 4 -#define MICROCHIP_PFSOC_PLIC_HART_CONFIG "MS" #define MICROCHIP_PFSOC_PLIC_NUM_SOURCES 185 #define MICROCHIP_PFSOC_PLIC_NUM_PRIORITIES 7 #define MICROCHIP_PFSOC_PLIC_PRIORITY_BASE 0x04 From 7d10ff8a4de7a9bff1e7b25011f5eb43f24a6713 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 22 Oct 2021 16:01:33 +1000 Subject: [PATCH 0826/1334] hw/riscv: virt: Use the PLIC config helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Tested-by: Bin Meng Message-id: 20211022060133.3045020-5-alistair.francis@opensource.wdc.com --- hw/riscv/virt.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 28a5909a3b..3af074148e 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -748,24 +748,6 @@ static FWCfgState *create_fw_cfg(const MachineState *mc) return fw_cfg; } -/* - * Return the per-socket PLIC hart topology configuration string - * (caller must free with g_free()) - */ -static char *plic_hart_config_string(int hart_count) -{ - g_autofree const char **vals = g_new(const char *, hart_count + 1); - int i; - - for (i = 0; i < hart_count; i++) { - vals[i] = "MS"; - } - vals[i] = NULL; - - /* g_strjoinv() obliges us to cast away const here */ - return g_strjoinv(",", (char **)vals); -} - static void virt_machine_init(MachineState *machine) { const MemMapEntry *memmap = virt_memmap; @@ -839,7 +821,7 @@ static void virt_machine_init(MachineState *machine) } /* Per-socket PLIC hart topology configuration string */ - plic_hart_config = plic_hart_config_string(hart_count); + plic_hart_config = riscv_plic_hart_config_string(hart_count); /* Per-socket PLIC */ s->plic[i] = sifive_plic_create( From 9b144ed444f1fb3149d9ec17f0c4a64d4fd7d662 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 25 Oct 2021 14:06:57 +1000 Subject: [PATCH 0827/1334] hw/riscv: opentitan: Fixup the PLIC context addresses Fixup the PLIC context address to correctly support the threshold and claim register. Fixes: ef63100648 ("hw/riscv: opentitan: Update to the latest build") Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 20211025040657.262696-1-alistair.francis@opensource.wdc.com --- hw/riscv/opentitan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 83e1511f28..c531450b9f 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -161,8 +161,8 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) qdev_prop_set_uint32(DEVICE(&s->plic), "pending-base", 0x1000); qdev_prop_set_uint32(DEVICE(&s->plic), "enable-base", 0x2000); qdev_prop_set_uint32(DEVICE(&s->plic), "enable-stride", 0x18); - qdev_prop_set_uint32(DEVICE(&s->plic), "context-base", 0x200004); - qdev_prop_set_uint32(DEVICE(&s->plic), "context-stride", 4); + qdev_prop_set_uint32(DEVICE(&s->plic), "context-base", 0x200000); + qdev_prop_set_uint32(DEVICE(&s->plic), "context-stride", 8); qdev_prop_set_uint32(DEVICE(&s->plic), "aperture-size", memmap[IBEX_DEV_PLIC].size); if (!sysbus_realize(SYS_BUS_DEVICE(&s->plic), errp)) { From 53dcea58b8ab150ab034f9c19074c5f74d6ca41e Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 25 Oct 2021 20:36:02 +0300 Subject: [PATCH 0828/1334] target/riscv: Add J-extension into RISC-V Signed-off-by: Alexey Baturo Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Bin Meng Message-id: 20211025173609.2724490-2-space.monkey.delivers@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index a33dc30be8..1cfc6a53a0 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -65,6 +65,7 @@ #define RVS RV('S') #define RVU RV('U') #define RVH RV('H') +#define RVJ RV('J') /* S extension denotes that Supervisor mode exists, however it is possible to have a core that support S mode but does not have an MMU and there @@ -291,6 +292,7 @@ struct RISCVCPU { bool ext_s; bool ext_u; bool ext_h; + bool ext_j; bool ext_v; bool ext_zba; bool ext_zbb; From 138b5c5f8f5abec5acc18d2a256f0a082dc51ef5 Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 25 Oct 2021 20:36:03 +0300 Subject: [PATCH 0829/1334] target/riscv: Add CSR defines for RISC-V PM extension Signed-off-by: Alexey Baturo Reviewed-by: Alistair Francis Message-id: 20211025173609.2724490-3-space.monkey.delivers@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 96 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index cffcd3a5df..aa0bce4e06 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -334,6 +334,38 @@ #define CSR_MHPMCOUNTER30H 0xb9e #define CSR_MHPMCOUNTER31H 0xb9f +/* + * User PointerMasking registers + * NB: actual CSR numbers might be changed in future + */ +#define CSR_UMTE 0x4c0 +#define CSR_UPMMASK 0x4c1 +#define CSR_UPMBASE 0x4c2 + +/* + * Machine PointerMasking registers + * NB: actual CSR numbers might be changed in future + */ +#define CSR_MMTE 0x3c0 +#define CSR_MPMMASK 0x3c1 +#define CSR_MPMBASE 0x3c2 + +/* + * Supervisor PointerMaster registers + * NB: actual CSR numbers might be changed in future + */ +#define CSR_SMTE 0x1c0 +#define CSR_SPMMASK 0x1c1 +#define CSR_SPMBASE 0x1c2 + +/* + * Hypervisor PointerMaster registers + * NB: actual CSR numbers might be changed in future + */ +#define CSR_VSMTE 0x2c0 +#define CSR_VSPMMASK 0x2c1 +#define CSR_VSPMBASE 0x2c2 + /* mstatus CSR bits */ #define MSTATUS_UIE 0x00000001 #define MSTATUS_SIE 0x00000002 @@ -525,4 +557,68 @@ typedef enum RISCVException { #define MIE_UTIE (1 << IRQ_U_TIMER) #define MIE_SSIE (1 << IRQ_S_SOFT) #define MIE_USIE (1 << IRQ_U_SOFT) + +/* General PointerMasking CSR bits*/ +#define PM_ENABLE 0x00000001ULL +#define PM_CURRENT 0x00000002ULL +#define PM_INSN 0x00000004ULL +#define PM_XS_MASK 0x00000003ULL + +/* PointerMasking XS bits values */ +#define PM_EXT_DISABLE 0x00000000ULL +#define PM_EXT_INITIAL 0x00000001ULL +#define PM_EXT_CLEAN 0x00000002ULL +#define PM_EXT_DIRTY 0x00000003ULL + +/* Offsets for every pair of control bits per each priv level */ +#define XS_OFFSET 0ULL +#define U_OFFSET 2ULL +#define S_OFFSET 5ULL +#define M_OFFSET 8ULL + +#define PM_XS_BITS (PM_XS_MASK << XS_OFFSET) +#define U_PM_ENABLE (PM_ENABLE << U_OFFSET) +#define U_PM_CURRENT (PM_CURRENT << U_OFFSET) +#define U_PM_INSN (PM_INSN << U_OFFSET) +#define S_PM_ENABLE (PM_ENABLE << S_OFFSET) +#define S_PM_CURRENT (PM_CURRENT << S_OFFSET) +#define S_PM_INSN (PM_INSN << S_OFFSET) +#define M_PM_ENABLE (PM_ENABLE << M_OFFSET) +#define M_PM_CURRENT (PM_CURRENT << M_OFFSET) +#define M_PM_INSN (PM_INSN << M_OFFSET) + +/* mmte CSR bits */ +#define MMTE_PM_XS_BITS PM_XS_BITS +#define MMTE_U_PM_ENABLE U_PM_ENABLE +#define MMTE_U_PM_CURRENT U_PM_CURRENT +#define MMTE_U_PM_INSN U_PM_INSN +#define MMTE_S_PM_ENABLE S_PM_ENABLE +#define MMTE_S_PM_CURRENT S_PM_CURRENT +#define MMTE_S_PM_INSN S_PM_INSN +#define MMTE_M_PM_ENABLE M_PM_ENABLE +#define MMTE_M_PM_CURRENT M_PM_CURRENT +#define MMTE_M_PM_INSN M_PM_INSN +#define MMTE_MASK (MMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | MMTE_U_PM_INSN | \ + MMTE_S_PM_ENABLE | MMTE_S_PM_CURRENT | MMTE_S_PM_INSN | \ + MMTE_M_PM_ENABLE | MMTE_M_PM_CURRENT | MMTE_M_PM_INSN | \ + MMTE_PM_XS_BITS) + +/* (v)smte CSR bits */ +#define SMTE_PM_XS_BITS PM_XS_BITS +#define SMTE_U_PM_ENABLE U_PM_ENABLE +#define SMTE_U_PM_CURRENT U_PM_CURRENT +#define SMTE_U_PM_INSN U_PM_INSN +#define SMTE_S_PM_ENABLE S_PM_ENABLE +#define SMTE_S_PM_CURRENT S_PM_CURRENT +#define SMTE_S_PM_INSN S_PM_INSN +#define SMTE_MASK (SMTE_U_PM_ENABLE | SMTE_U_PM_CURRENT | SMTE_U_PM_INSN | \ + SMTE_S_PM_ENABLE | SMTE_S_PM_CURRENT | SMTE_S_PM_INSN | \ + SMTE_PM_XS_BITS) + +/* umte CSR bits */ +#define UMTE_U_PM_ENABLE U_PM_ENABLE +#define UMTE_U_PM_CURRENT U_PM_CURRENT +#define UMTE_U_PM_INSN U_PM_INSN +#define UMTE_MASK (UMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | UMTE_U_PM_INSN) + #endif From 4bbe8033fcd08769cef49f5149c5c165594ae10a Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 25 Oct 2021 20:36:04 +0300 Subject: [PATCH 0830/1334] target/riscv: Support CSRs required for RISC-V PM extension except for the h-mode Signed-off-by: Alexey Baturo Reviewed-by: Alistair Francis Message-id: 20211025173609.2724490-4-space.monkey.delivers@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 + target/riscv/cpu.h | 11 ++ target/riscv/csr.c | 285 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 788fa0b11c..6b767a4a0b 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -367,6 +367,8 @@ static void riscv_cpu_reset(DeviceState *dev) env->mcause = 0; env->pc = env->resetvec; env->two_stage_lookup = false; + /* mmte is supposed to have pm.current hardwired to 1 */ + env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT); #endif cs->exception_index = RISCV_EXCP_NONE; env->load_res = -1; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 1cfc6a53a0..b2422e3f99 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -238,6 +238,17 @@ struct CPURISCVState { /* True if in debugger mode. */ bool debugger; + + /* + * CSRs for PointerMasking extension + */ + target_ulong mmte; + target_ulong mpmmask; + target_ulong mpmbase; + target_ulong spmmask; + target_ulong spmbase; + target_ulong upmmask; + target_ulong upmbase; #endif float_status fp_status; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 69e4d65fcd..9f41954894 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -192,6 +192,16 @@ static RISCVException hmode32(CPURISCVState *env, int csrno) } +/* Checks if PointerMasking registers could be accessed */ +static RISCVException pointer_masking(CPURISCVState *env, int csrno) +{ + /* Check if j-ext is present */ + if (riscv_has_ext(env, RVJ)) { + return RISCV_EXCP_NONE; + } + return RISCV_EXCP_ILLEGAL_INST; +} + static RISCVException pmp(CPURISCVState *env, int csrno) { if (riscv_feature(env, RISCV_FEATURE_PMP)) { @@ -1425,6 +1435,268 @@ static RISCVException write_pmpaddr(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +/* + * Functions to access Pointer Masking feature registers + * We have to check if current priv lvl could modify + * csr in given mode + */ +static bool check_pm_current_disabled(CPURISCVState *env, int csrno) +{ + int csr_priv = get_field(csrno, 0x300); + int pm_current; + + /* + * If priv lvls differ that means we're accessing csr from higher priv lvl, + * so allow the access + */ + if (env->priv != csr_priv) { + return false; + } + switch (env->priv) { + case PRV_M: + pm_current = get_field(env->mmte, M_PM_CURRENT); + break; + case PRV_S: + pm_current = get_field(env->mmte, S_PM_CURRENT); + break; + case PRV_U: + pm_current = get_field(env->mmte, U_PM_CURRENT); + break; + default: + g_assert_not_reached(); + } + /* It's same priv lvl, so we allow to modify csr only if pm.current==1 */ + return !pm_current; +} + +static RISCVException read_mmte(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mmte & MMTE_MASK; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mmte(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mstatus; + target_ulong wpri_val = val & MMTE_MASK; + + if (val != wpri_val) { + qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" TARGET_FMT_lx "\n", + "MMTE: WPRI violation written 0x", val, + "vs expected 0x", wpri_val); + } + /* for machine mode pm.current is hardwired to 1 */ + wpri_val |= MMTE_M_PM_CURRENT; + + /* hardwiring pm.instruction bit to 0, since it's not supported yet */ + wpri_val &= ~(MMTE_M_PM_INSN | MMTE_S_PM_INSN | MMTE_U_PM_INSN); + env->mmte = wpri_val | PM_EXT_DIRTY; + + /* Set XS and SD bits, since PM CSRs are dirty */ + mstatus = env->mstatus | MSTATUS_XS; + write_mstatus(env, csrno, mstatus); + return RISCV_EXCP_NONE; +} + +static RISCVException read_smte(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mmte & SMTE_MASK; + return RISCV_EXCP_NONE; +} + +static RISCVException write_smte(CPURISCVState *env, int csrno, + target_ulong val) +{ + target_ulong wpri_val = val & SMTE_MASK; + + if (val != wpri_val) { + qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" TARGET_FMT_lx "\n", + "SMTE: WPRI violation written 0x", val, + "vs expected 0x", wpri_val); + } + + /* if pm.current==0 we can't modify current PM CSRs */ + if (check_pm_current_disabled(env, csrno)) { + return RISCV_EXCP_NONE; + } + + wpri_val |= (env->mmte & ~SMTE_MASK); + write_mmte(env, csrno, wpri_val); + return RISCV_EXCP_NONE; +} + +static RISCVException read_umte(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mmte & UMTE_MASK; + return RISCV_EXCP_NONE; +} + +static RISCVException write_umte(CPURISCVState *env, int csrno, + target_ulong val) +{ + target_ulong wpri_val = val & UMTE_MASK; + + if (val != wpri_val) { + qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" TARGET_FMT_lx "\n", + "UMTE: WPRI violation written 0x", val, + "vs expected 0x", wpri_val); + } + + if (check_pm_current_disabled(env, csrno)) { + return RISCV_EXCP_NONE; + } + + wpri_val |= (env->mmte & ~UMTE_MASK); + write_mmte(env, csrno, wpri_val); + return RISCV_EXCP_NONE; +} + +static RISCVException read_mpmmask(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mpmmask; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mpmmask(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mstatus; + + env->mpmmask = val; + env->mmte |= PM_EXT_DIRTY; + + /* Set XS and SD bits, since PM CSRs are dirty */ + mstatus = env->mstatus | MSTATUS_XS; + write_mstatus(env, csrno, mstatus); + return RISCV_EXCP_NONE; +} + +static RISCVException read_spmmask(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->spmmask; + return RISCV_EXCP_NONE; +} + +static RISCVException write_spmmask(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mstatus; + + /* if pm.current==0 we can't modify current PM CSRs */ + if (check_pm_current_disabled(env, csrno)) { + return RISCV_EXCP_NONE; + } + env->spmmask = val; + env->mmte |= PM_EXT_DIRTY; + + /* Set XS and SD bits, since PM CSRs are dirty */ + mstatus = env->mstatus | MSTATUS_XS; + write_mstatus(env, csrno, mstatus); + return RISCV_EXCP_NONE; +} + +static RISCVException read_upmmask(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->upmmask; + return RISCV_EXCP_NONE; +} + +static RISCVException write_upmmask(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mstatus; + + /* if pm.current==0 we can't modify current PM CSRs */ + if (check_pm_current_disabled(env, csrno)) { + return RISCV_EXCP_NONE; + } + env->upmmask = val; + env->mmte |= PM_EXT_DIRTY; + + /* Set XS and SD bits, since PM CSRs are dirty */ + mstatus = env->mstatus | MSTATUS_XS; + write_mstatus(env, csrno, mstatus); + return RISCV_EXCP_NONE; +} + +static RISCVException read_mpmbase(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mpmbase; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mpmbase(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mstatus; + + env->mpmbase = val; + env->mmte |= PM_EXT_DIRTY; + + /* Set XS and SD bits, since PM CSRs are dirty */ + mstatus = env->mstatus | MSTATUS_XS; + write_mstatus(env, csrno, mstatus); + return RISCV_EXCP_NONE; +} + +static RISCVException read_spmbase(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->spmbase; + return RISCV_EXCP_NONE; +} + +static RISCVException write_spmbase(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mstatus; + + /* if pm.current==0 we can't modify current PM CSRs */ + if (check_pm_current_disabled(env, csrno)) { + return RISCV_EXCP_NONE; + } + env->spmbase = val; + env->mmte |= PM_EXT_DIRTY; + + /* Set XS and SD bits, since PM CSRs are dirty */ + mstatus = env->mstatus | MSTATUS_XS; + write_mstatus(env, csrno, mstatus); + return RISCV_EXCP_NONE; +} + +static RISCVException read_upmbase(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->upmbase; + return RISCV_EXCP_NONE; +} + +static RISCVException write_upmbase(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mstatus; + + /* if pm.current==0 we can't modify current PM CSRs */ + if (check_pm_current_disabled(env, csrno)) { + return RISCV_EXCP_NONE; + } + env->upmbase = val; + env->mmte |= PM_EXT_DIRTY; + + /* Set XS and SD bits, since PM CSRs are dirty */ + mstatus = env->mstatus | MSTATUS_XS; + write_mstatus(env, csrno, mstatus); + return RISCV_EXCP_NONE; +} + #endif /* @@ -1659,6 +1931,19 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr }, + /* User Pointer Masking */ + [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, + [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, write_upmmask }, + [CSR_UPMBASE] = { "upmbase", pointer_masking, read_upmbase, write_upmbase }, + /* Machine Pointer Masking */ + [CSR_MMTE] = { "mmte", pointer_masking, read_mmte, write_mmte }, + [CSR_MPMMASK] = { "mpmmask", pointer_masking, read_mpmmask, write_mpmmask }, + [CSR_MPMBASE] = { "mpmbase", pointer_masking, read_mpmbase, write_mpmbase }, + /* Supervisor Pointer Masking */ + [CSR_SMTE] = { "smte", pointer_masking, read_smte, write_smte }, + [CSR_SPMMASK] = { "spmmask", pointer_masking, read_spmmask, write_spmmask }, + [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase, write_spmbase }, + /* Performance Counters */ [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_zero }, [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_zero }, From b1c279e135e0da33a06544919c2cb89b6988e874 Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 25 Oct 2021 20:36:05 +0300 Subject: [PATCH 0831/1334] target/riscv: Add J extension state description Signed-off-by: Alexey Baturo Reviewed-by: Alistair Francis Message-id: 20211025173609.2724490-5-space.monkey.delivers@gmail.com Signed-off-by: Alistair Francis --- target/riscv/machine.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/target/riscv/machine.c b/target/riscv/machine.c index f64b2a96c1..7b4c739564 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -84,6 +84,14 @@ static bool vector_needed(void *opaque) return riscv_has_ext(env, RVV); } +static bool pointermasking_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + return riscv_has_ext(env, RVJ); +} + static const VMStateDescription vmstate_vector = { .name = "cpu/vector", .version_id = 1, @@ -100,6 +108,24 @@ static const VMStateDescription vmstate_vector = { } }; +static const VMStateDescription vmstate_pointermasking = { + .name = "cpu/pointer_masking", + .version_id = 1, + .minimum_version_id = 1, + .needed = pointermasking_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(env.mmte, RISCVCPU), + VMSTATE_UINTTL(env.mpmmask, RISCVCPU), + VMSTATE_UINTTL(env.mpmbase, RISCVCPU), + VMSTATE_UINTTL(env.spmmask, RISCVCPU), + VMSTATE_UINTTL(env.spmbase, RISCVCPU), + VMSTATE_UINTTL(env.upmmask, RISCVCPU), + VMSTATE_UINTTL(env.upmbase, RISCVCPU), + + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_hyper = { .name = "cpu/hyper", .version_id = 1, @@ -191,6 +217,7 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_pmp, &vmstate_hyper, &vmstate_vector, + &vmstate_pointermasking, NULL } }; From bd5594ca2808b3e353d350a08d72f36cb8e01048 Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 25 Oct 2021 20:36:06 +0300 Subject: [PATCH 0832/1334] target/riscv: Print new PM CSRs in QEMU logs Signed-off-by: Alexey Baturo Reviewed-by: Alistair Francis Message-id: 20211025173609.2724490-6-space.monkey.delivers@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 6b767a4a0b..16fac64806 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -271,6 +271,13 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) CSR_MSCRATCH, CSR_SSCRATCH, CSR_SATP, + CSR_MMTE, + CSR_UPMBASE, + CSR_UPMMASK, + CSR_SPMBASE, + CSR_SPMMASK, + CSR_MPMBASE, + CSR_MPMMASK, }; for (int i = 0; i < ARRAY_SIZE(dump_csrs); ++i) { From c655df7fe00669ac9ac3b0614be6e4a6f5782737 Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 25 Oct 2021 20:36:07 +0300 Subject: [PATCH 0833/1334] target/riscv: Support pointer masking for RISC-V for i/c/f/d/a types of instructions Signed-off-by: Alexey Baturo Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-id: 20211025173609.2724490-7-space.monkey.delivers@gmail.com Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rva.c.inc | 3 +++ target/riscv/insn_trans/trans_rvd.c.inc | 2 ++ target/riscv/insn_trans/trans_rvf.c.inc | 2 ++ target/riscv/insn_trans/trans_rvi.c.inc | 2 ++ target/riscv/translate.c | 8 ++++++++ 5 files changed, 17 insertions(+) diff --git a/target/riscv/insn_trans/trans_rva.c.inc b/target/riscv/insn_trans/trans_rva.c.inc index 6ea07d89b0..40fe132b04 100644 --- a/target/riscv/insn_trans/trans_rva.c.inc +++ b/target/riscv/insn_trans/trans_rva.c.inc @@ -25,6 +25,7 @@ static bool gen_lr(DisasContext *ctx, arg_atomic *a, MemOp mop) if (a->rl) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } + src1 = gen_pm_adjust_address(ctx, src1); tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop); if (a->aq) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); @@ -44,6 +45,7 @@ static bool gen_sc(DisasContext *ctx, arg_atomic *a, MemOp mop) TCGLabel *l2 = gen_new_label(); src1 = get_gpr(ctx, a->rs1, EXT_ZERO); + src1 = gen_pm_adjust_address(ctx, src1); tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1); /* @@ -84,6 +86,7 @@ static bool gen_amo(DisasContext *ctx, arg_atomic *a, TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + src1 = gen_pm_adjust_address(ctx, src1); func(dest, src1, src2, ctx->mem_idx, mop); gen_set_gpr(ctx, a->rd, dest); diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index db9ae15755..64fb0046f7 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -31,6 +31,7 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) tcg_gen_addi_tl(temp, addr, a->imm); addr = temp; } + addr = gen_pm_adjust_address(ctx, addr); tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, MO_TEQ); @@ -51,6 +52,7 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) tcg_gen_addi_tl(temp, addr, a->imm); addr = temp; } + addr = gen_pm_adjust_address(ctx, addr); tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEQ); diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index bddbd418d9..b5459249c4 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -37,6 +37,7 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) tcg_gen_addi_tl(temp, addr, a->imm); addr = temp; } + addr = gen_pm_adjust_address(ctx, addr); dest = cpu_fpr[a->rd]; tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUL); @@ -59,6 +60,7 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) tcg_gen_addi_tl(temp, addr, a->imm); addr = temp; } + addr = gen_pm_adjust_address(ctx, addr); tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUL); diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 91dc438a3a..e51dbc41c5 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -144,6 +144,7 @@ static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) tcg_gen_addi_tl(temp, addr, a->imm); addr = temp; } + addr = gen_pm_adjust_address(ctx, addr); tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, memop); gen_set_gpr(ctx, a->rd, dest); @@ -185,6 +186,7 @@ static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop) tcg_gen_addi_tl(temp, addr, a->imm); addr = temp; } + addr = gen_pm_adjust_address(ctx, addr); tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop); return true; diff --git a/target/riscv/translate.c b/target/riscv/translate.c index d38f87d718..a5e6fa145d 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -271,6 +271,14 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) ctx->base.is_jmp = DISAS_NORETURN; } +/* + * Temp stub: generates address adjustment for PointerMasking + */ +static TCGv gen_pm_adjust_address(DisasContext *s, TCGv src) +{ + return src; +} + #ifndef CONFIG_USER_ONLY /* The states of mstatus_fs are: * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty From 0774a7a1ff24d6b8c2f90b9c341f057914b18134 Mon Sep 17 00:00:00 2001 From: Anatoly Parshintsev Date: Mon, 25 Oct 2021 20:36:08 +0300 Subject: [PATCH 0834/1334] target/riscv: Implement address masking functions required for RISC-V Pointer Masking extension Signed-off-by: Anatoly Parshintsev Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-id: 20211025173609.2724490-8-space.monkey.delivers@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 2 ++ target/riscv/cpu_helper.c | 18 ++++++++++++++++++ target/riscv/translate.c | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index b2422e3f99..325908287d 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -410,6 +410,8 @@ FIELD(TB_FLAGS, HLSX, 10, 1) FIELD(TB_FLAGS, MSTATUS_HS_FS, 11, 2) /* The combination of MXL/SXL/UXL that applies to the current cpu mode. */ FIELD(TB_FLAGS, XL, 13, 2) +/* If PointerMasking should be applied */ +FIELD(TB_FLAGS, PM_ENABLED, 15, 1) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 0d1132f39d..662228c238 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -107,6 +107,24 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_FS, get_field(env->mstatus_hs, MSTATUS_FS)); } + if (riscv_has_ext(env, RVJ)) { + int priv = flags & TB_FLAGS_PRIV_MMU_MASK; + bool pm_enabled = false; + switch (priv) { + case PRV_U: + pm_enabled = env->mmte & U_PM_ENABLE; + break; + case PRV_S: + pm_enabled = env->mmte & S_PM_ENABLE; + break; + case PRV_M: + pm_enabled = env->mmte & M_PM_ENABLE; + break; + default: + g_assert_not_reached(); + } + flags = FIELD_DP32(flags, TB_FLAGS, PM_ENABLED, pm_enabled); + } #endif flags = FIELD_DP32(flags, TB_FLAGS, XL, cpu_get_xl(env)); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index a5e6fa145d..1d57bc97b5 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -36,6 +36,9 @@ static TCGv cpu_gpr[32], cpu_pc, cpu_vl; static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ static TCGv load_res; static TCGv load_val; +/* globals for PM CSRs */ +static TCGv pm_mask[4]; +static TCGv pm_base[4]; #include "exec/gen-icount.h" @@ -83,6 +86,10 @@ typedef struct DisasContext { TCGv zero; /* Space for 3 operands plus 1 extra for address computation. */ TCGv temp[4]; + /* PointerMasking extension */ + bool pm_enabled; + TCGv pm_mask; + TCGv pm_base; } DisasContext; static inline bool has_ext(DisasContext *ctx, uint32_t ext) @@ -272,11 +279,20 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) } /* - * Temp stub: generates address adjustment for PointerMasking + * Generates address adjustment for PointerMasking */ static TCGv gen_pm_adjust_address(DisasContext *s, TCGv src) { - return src; + TCGv temp; + if (!s->pm_enabled) { + /* Load unmodified address */ + return src; + } else { + temp = temp_new(s); + tcg_gen_andc_tl(temp, src, s->pm_mask); + tcg_gen_or_tl(temp, temp, s->pm_base); + return temp; + } } #ifndef CONFIG_USER_ONLY @@ -622,6 +638,10 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->cs = cs; ctx->ntemp = 0; memset(ctx->temp, 0, sizeof(ctx->temp)); + ctx->pm_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_ENABLED); + int priv = tb_flags & TB_FLAGS_PRIV_MMU_MASK; + ctx->pm_mask = pm_mask[priv]; + ctx->pm_base = pm_base[priv]; ctx->zero = tcg_constant_tl(0); } @@ -735,4 +755,19 @@ void riscv_translate_init(void) "load_res"); load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val), "load_val"); +#ifndef CONFIG_USER_ONLY + /* Assign PM CSRs to tcg globals */ + pm_mask[PRV_U] = + tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, upmmask), "upmmask"); + pm_base[PRV_U] = + tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, upmbase), "upmbase"); + pm_mask[PRV_S] = + tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, spmmask), "spmmask"); + pm_base[PRV_S] = + tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, spmbase), "spmbase"); + pm_mask[PRV_M] = + tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, mpmmask), "mpmmask"); + pm_base[PRV_M] = + tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, mpmbase), "mpmbase"); +#endif } From 0ee9a4e57e1cee4577cab22f6ced6c0c34fb2d94 Mon Sep 17 00:00:00 2001 From: Alexey Baturo Date: Mon, 25 Oct 2021 20:36:09 +0300 Subject: [PATCH 0835/1334] target/riscv: Allow experimental J-ext to be turned on Signed-off-by: Alexey Baturo Reviewed-by: Alistair Francis Reviewed-by: Bin Meng Reviewed-by: Richard Henderson Message-id: 20211025173609.2724490-9-space.monkey.delivers@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 16fac64806..7d53125dbc 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -562,6 +562,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } set_vext_version(env, vext_version); } + if (cpu->cfg.ext_j) { + ext |= RVJ; + } set_misa(env, env->misa_mxl, ext); } @@ -637,6 +640,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("x-zbc", RISCVCPU, cfg.ext_zbc, false), DEFINE_PROP_BOOL("x-zbs", RISCVCPU, cfg.ext_zbs, false), DEFINE_PROP_BOOL("x-h", RISCVCPU, cfg.ext_h, false), + DEFINE_PROP_BOOL("x-j", RISCVCPU, cfg.ext_j, false), DEFINE_PROP_BOOL("x-v", RISCVCPU, cfg.ext_v, false), DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec), DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), From f448397a512189e726f5e8026c89ce7fc4392377 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Mon, 11 Oct 2021 11:48:52 -0500 Subject: [PATCH 0836/1334] Hexagon (target/hexagon) more tcg_constant_* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change additional tcg_const_tl to tcg_constant_tl Note that gen_pred_cancal had slot_mask initialized with tcg_const_tl. However, it is not constant throughout, so we initialize it with tcg_temp_new and replace the first use with the constant value. Inspired-by: Richard Henderson Inspired-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg.h | 9 +++------ target/hexagon/gen_tcg_funcs.py | 11 ++--------- target/hexagon/macros.h | 7 +++---- target/hexagon/translate.c | 3 +-- 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index 0361564104..c6f0879b6e 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -66,11 +66,10 @@ } while (0) #define GET_EA_pci \ do { \ - TCGv tcgv_siV = tcg_const_tl(siV); \ + TCGv tcgv_siV = tcg_constant_tl(siV); \ tcg_gen_mov_tl(EA, RxV); \ gen_helper_fcircadd(RxV, RxV, tcgv_siV, MuV, \ hex_gpr[HEX_REG_CS0 + MuN]); \ - tcg_temp_free(tcgv_siV); \ } while (0) #define GET_EA_pcr(SHIFT) \ do { \ @@ -557,7 +556,7 @@ #define fGEN_TCG_A4_addp_c(SHORTCODE) \ do { \ TCGv_i64 carry = tcg_temp_new_i64(); \ - TCGv_i64 zero = tcg_const_i64(0); \ + TCGv_i64 zero = tcg_constant_i64(0); \ tcg_gen_extu_i32_i64(carry, PxV); \ tcg_gen_andi_i64(carry, carry, 1); \ tcg_gen_add2_i64(RddV, carry, RssV, zero, carry, zero); \ @@ -565,14 +564,13 @@ tcg_gen_extrl_i64_i32(PxV, carry); \ gen_8bitsof(PxV, PxV); \ tcg_temp_free_i64(carry); \ - tcg_temp_free_i64(zero); \ } while (0) /* r5:4 = sub(r1:0, r3:2, p1):carry */ #define fGEN_TCG_A4_subp_c(SHORTCODE) \ do { \ TCGv_i64 carry = tcg_temp_new_i64(); \ - TCGv_i64 zero = tcg_const_i64(0); \ + TCGv_i64 zero = tcg_constant_i64(0); \ TCGv_i64 not_RttV = tcg_temp_new_i64(); \ tcg_gen_extu_i32_i64(carry, PxV); \ tcg_gen_andi_i64(carry, carry, 1); \ @@ -582,7 +580,6 @@ tcg_gen_extrl_i64_i32(PxV, carry); \ gen_8bitsof(PxV, PxV); \ tcg_temp_free_i64(carry); \ - tcg_temp_free_i64(zero); \ tcg_temp_free_i64(not_RttV); \ } while (0) diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index ca8a801baa..e3d59dd552 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -279,15 +279,12 @@ def gen_helper_call_opn(f, tag, regtype, regid, toss, numregs, i): print("Bad register parse: ",regtype,regid,toss,numregs) def gen_helper_decl_imm(f,immlett): - f.write(" TCGv tcgv_%s = tcg_const_tl(%s);\n" % \ + f.write(" TCGv tcgv_%s = tcg_constant_tl(%s);\n" % \ (hex_common.imm_name(immlett), hex_common.imm_name(immlett))) def gen_helper_call_imm(f,immlett): f.write(", tcgv_%s" % hex_common.imm_name(immlett)) -def gen_helper_free_imm(f,immlett): - f.write(" tcg_temp_free(tcgv_%s);\n" % hex_common.imm_name(immlett)) - def genptr_dst_write_pair(f, tag, regtype, regid): if ('A_CONDEXEC' in hex_common.attribdict[tag]): f.write(" gen_log_predicated_reg_write_pair(%s%sN, %s%sV, insn->slot);\n" % \ @@ -401,7 +398,7 @@ def gen_tcg_func(f, tag, regs, imms): for immlett,bits,immshift in imms: gen_helper_decl_imm(f,immlett) if hex_common.need_part1(tag): - f.write(" TCGv part1 = tcg_const_tl(insn->part1);\n") + f.write(" TCGv part1 = tcg_constant_tl(insn->part1);\n") if hex_common.need_slot(tag): f.write(" TCGv slot = tcg_constant_tl(insn->slot);\n") f.write(" gen_helper_%s(" % (tag)) @@ -424,10 +421,6 @@ def gen_tcg_func(f, tag, regs, imms): if hex_common.need_slot(tag): f.write(", slot") if hex_common.need_part1(tag): f.write(", part1" ) f.write(");\n") - if hex_common.need_part1(tag): - f.write(" tcg_temp_free(part1);\n") - for immlett,bits,immshift in imms: - gen_helper_free_imm(f,immlett) ## Write all the outputs for regtype,regid,toss,numregs in regs: diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 44e9b857b5..5c19cdeb00 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -187,10 +187,10 @@ #ifdef QEMU_GENERATE static inline void gen_pred_cancel(TCGv pred, int slot_num) { - TCGv slot_mask = tcg_const_tl(1 << slot_num); + TCGv slot_mask = tcg_temp_new(); TCGv tmp = tcg_temp_new(); TCGv zero = tcg_constant_tl(0); - tcg_gen_or_tl(slot_mask, hex_slot_cancelled, slot_mask); + tcg_gen_ori_tl(slot_mask, hex_slot_cancelled, 1 << slot_num); tcg_gen_andi_tl(tmp, pred, 1); tcg_gen_movcond_tl(TCG_COND_EQ, hex_slot_cancelled, tmp, zero, slot_mask, hex_slot_cancelled); @@ -498,10 +498,9 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fPM_M(REG, MVAL) tcg_gen_add_tl(REG, REG, MVAL) #define fPM_CIRI(REG, IMM, MVAL) \ do { \ - TCGv tcgv_siV = tcg_const_tl(siV); \ + TCGv tcgv_siV = tcg_constant_tl(siV); \ gen_helper_fcircadd(REG, REG, tcgv_siV, MuV, \ hex_gpr[HEX_REG_CS0 + MuN]); \ - tcg_temp_free(tcgv_siV); \ } while (0) #else #define fEA_IMM(IMM) do { EA = (IMM); } while (0) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 159931e8ee..724b4fc11e 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -487,9 +487,8 @@ static void gen_commit_packet(DisasContext *ctx, Packet *pkt) * process_store_log will execute the slot 1 store first, * so we only have to probe the store in slot 0 */ - TCGv mem_idx = tcg_const_tl(ctx->mem_idx); + TCGv mem_idx = tcg_constant_tl(ctx->mem_idx); gen_helper_probe_pkt_scalar_store_s0(cpu_env, mem_idx); - tcg_temp_free(mem_idx); } process_store_log(ctx, pkt); From b9dd6ff91d29b9e38afd7facf1d683f34bd1ec10 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Mon, 4 Oct 2021 19:12:31 -0500 Subject: [PATCH 0837/1334] Hexagon (target/hexagon) put writes to USR into temp until commit Change SET_USR_FIELD to write to hex_new_value[HEX_REG_USR] instead of hex_gpr[HEX_REG_USR]. Then, we need code to mark the instructions that can set implicitly set USR - Macros added to hex_common.py - A_FPOP added in translate.c Test case added in tests/tcg/hexagon/overflow.c Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/attribs_def.h.inc | 1 + target/hexagon/hex_common.py | 2 + target/hexagon/macros.h | 2 +- target/hexagon/translate.c | 9 ++- tests/tcg/hexagon/Makefile.target | 1 + tests/tcg/hexagon/overflow.c | 107 ++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 tests/tcg/hexagon/overflow.c diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index 381550909d..e44a7ead16 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -64,6 +64,7 @@ DEF_ATTRIB(IMPLICIT_WRITES_P1, "Writes Predicate 1", "", "UREG.P1") DEF_ATTRIB(IMPLICIT_WRITES_P2, "Writes Predicate 1", "", "UREG.P2") DEF_ATTRIB(IMPLICIT_WRITES_P3, "May write Predicate 3", "", "UREG.P3") DEF_ATTRIB(IMPLICIT_READS_PC, "Reads the PC register", "", "") +DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "") DEF_ATTRIB(WRITES_PRED_REG, "Writes a predicate register", "", "") DEF_ATTRIB(CRSLOT23, "Can execute in slot 2 or slot 3 (CR)", "", "") diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index b3b534057d..a84b003f7e 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -73,6 +73,8 @@ def calculate_attribs(): add_qemu_macro_attrib('fWRITE_P1', 'A_WRITES_PRED_REG') add_qemu_macro_attrib('fWRITE_P2', 'A_WRITES_PRED_REG') add_qemu_macro_attrib('fWRITE_P3', 'A_WRITES_PRED_REG') + add_qemu_macro_attrib('fSET_OVERFLOW', 'A_IMPLICIT_WRITES_USR') + add_qemu_macro_attrib('fSET_LPCFG', 'A_IMPLICIT_WRITES_USR') # Recurse down macros, find attributes from sub-macros macroValues = list(macros.values()) diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 5c19cdeb00..13e957b41d 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -62,7 +62,7 @@ reg_field_info[FIELD].offset) #define SET_USR_FIELD(FIELD, VAL) \ - fINSERT_BITS(env->gpr[HEX_REG_USR], reg_field_info[FIELD].width, \ + fINSERT_BITS(env->new_value[HEX_REG_USR], reg_field_info[FIELD].width, \ reg_field_info[FIELD].offset, (VAL)) #endif diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 724b4fc11e..e10ef36c5c 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -204,7 +204,12 @@ static void mark_implicit_reg_write(DisasContext *ctx, Insn *insn, int attrib, int rnum) { if (GET_ATTRIB(insn->opcode, attrib)) { - bool is_predicated = GET_ATTRIB(insn->opcode, A_CONDEXEC); + /* + * USR is used to set overflow and FP exceptions, + * so treat it as conditional + */ + bool is_predicated = GET_ATTRIB(insn->opcode, A_CONDEXEC) || + rnum == HEX_REG_USR; if (is_predicated && !is_preloaded(ctx, rnum)) { tcg_gen_mov_tl(hex_new_value[rnum], hex_gpr[rnum]); } @@ -230,6 +235,8 @@ static void mark_implicit_reg_writes(DisasContext *ctx, Insn *insn) mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0); mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1); mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1); + mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_USR, HEX_REG_USR); + mark_implicit_reg_write(ctx, insn, A_FPOP, HEX_REG_USR); } static void mark_implicit_pred_writes(DisasContext *ctx, Insn *insn) diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index c1e1650798..8b07a28166 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -40,5 +40,6 @@ HEX_TESTS += load_unpack HEX_TESTS += load_align HEX_TESTS += atomics HEX_TESTS += fpstuff +HEX_TESTS += overflow TESTS += $(HEX_TESTS) diff --git a/tests/tcg/hexagon/overflow.c b/tests/tcg/hexagon/overflow.c new file mode 100644 index 0000000000..196fcf7f3a --- /dev/null +++ b/tests/tcg/hexagon/overflow.c @@ -0,0 +1,107 @@ +/* + * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + + +int err; + +static void __check(const char *filename, int line, int x, int expect) +{ + if (x != expect) { + printf("ERROR %s:%d - %d != %d\n", + filename, line, x, expect); + err++; + } +} + +#define check(x, expect) __check(__FILE__, __LINE__, (x), (expect)) + +static int satub(int src, int *p, int *ovf_result) +{ + int result; + int usr; + + /* + * This instruction can set bit 0 (OVF/overflow) in usr + * Clear the bit first, then return that bit to the caller + * + * We also store the src into *p in the same packet, so we + * can ensure the overflow doesn't get set when an exception + * is generated. + */ + asm volatile("r2 = usr\n\t" + "r2 = clrbit(r2, #0)\n\t" /* clear overflow bit */ + "usr = r2\n\t" + "{\n\t" + " %0 = satub(%2)\n\t" + " memw(%3) = %2\n\t" + "}\n\t" + "%1 = usr\n\t" + : "=r"(result), "=r"(usr) + : "r"(src), "r"(p) + : "r2", "usr", "memory"); + *ovf_result = (usr & 1); + return result; +} + +int read_usr_overflow(void) +{ + int result; + asm volatile("%0 = usr\n\t" : "=r"(result)); + return result & 1; +} + + +jmp_buf jmp_env; +int usr_overflow; + +static void sig_segv(int sig, siginfo_t *info, void *puc) +{ + usr_overflow = read_usr_overflow(); + longjmp(jmp_env, 1); +} + +int main() +{ + struct sigaction act; + int ovf; + + /* SIGSEGV test */ + act.sa_sigaction = sig_segv; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &act, NULL); + if (setjmp(jmp_env) == 0) { + satub(300, 0, &ovf); + } + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + + check(usr_overflow, 0); + + puts(err ? "FAIL" : "PASS"); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} From 18cf3d07a2556700895c626754937e90f8e972cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Oct 2021 14:34:10 -0400 Subject: [PATCH 0838/1334] tcg: Extend call args using the correct opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pretending that the source is i64 when it is in fact i32 is incorrect; we have type-changing opcodes that must be used. This bug trips up the subsequent change to the optimizer. Fixes: 4f2331e5b67a Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index 024a22cf39..6332cdceca 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1508,11 +1508,11 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args) if (is_32bit) { TCGv_i64 temp = tcg_temp_new_i64(); - TCGv_i64 orig = temp_tcgv_i64(args[i]); + TCGv_i32 orig = temp_tcgv_i32(args[i]); if (is_signed) { - tcg_gen_ext32s_i64(temp, orig); + tcg_gen_ext_i32_i64(temp, orig); } else { - tcg_gen_ext32u_i64(temp, orig); + tcg_gen_extu_i32_i64(temp, orig); } args[i] = tcgv_i64_temp(temp); } From faa2e10045ef82f4a1a24a7f69e285736143b469 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 09:03:59 -0700 Subject: [PATCH 0839/1334] tcg/optimize: Stop forcing z_mask to "garbage" for 32-bit values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This "garbage" setting pre-dates the addition of the type changing opcodes INDEX_op_ext_i32_i64, INDEX_op_extu_i32_i64, and INDEX_op_extr{l,h}_i64_i32. So now we have a definitive points at which to adjust z_mask to eliminate such bits from the 32-bit operands. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index e42f5a145f..e0abf769d0 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -124,10 +124,6 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) ti->is_const = true; ti->val = ts->val; ti->z_mask = ts->val; - if (TCG_TARGET_REG_BITS > 32 && ts->type == TCG_TYPE_I32) { - /* High bits of a 32-bit quantity are garbage. */ - ti->z_mask |= ~0xffffffffull; - } } else { ti->is_const = false; ti->z_mask = -1; @@ -192,7 +188,6 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) TCGTemp *src_ts = arg_temp(src); TempOptInfo *di; TempOptInfo *si; - uint64_t z_mask; TCGOpcode new_op; if (ts_are_copies(dst_ts, src_ts)) { @@ -224,12 +219,7 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) op->args[0] = dst; op->args[1] = src; - z_mask = si->z_mask; - if (TCG_TARGET_REG_BITS > 32 && new_op == INDEX_op_mov_i32) { - /* High bits of the destination are now garbage. */ - z_mask |= ~0xffffffffull; - } - di->z_mask = z_mask; + di->z_mask = si->z_mask; if (src_ts->type == dst_ts->type) { TempOptInfo *ni = ts_info(si->next_copy); @@ -247,9 +237,14 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) static bool tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, TCGArg dst, uint64_t val) { - /* Convert movi to mov with constant temp. */ - TCGTemp *tv = tcg_constant_internal(ctx->type, val); + TCGTemp *tv; + if (ctx->type == TCG_TYPE_I32) { + val = (int32_t)val; + } + + /* Convert movi to mov with constant temp. */ + tv = tcg_constant_internal(ctx->type, val); init_ts_info(ctx, tv); return tcg_opt_gen_mov(ctx, op, dst, temp_arg(tv)); } @@ -721,14 +716,16 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) uint64_t z_mask = ctx->z_mask; /* - * 32-bit ops generate 32-bit results. For the result is zero test - * below, we can ignore high bits, but for further optimizations we - * need to record that the high bits contain garbage. + * 32-bit ops generate 32-bit results, which for the purpose of + * simplifying tcg are sign-extended. Certainly that's how we + * represent our constants elsewhere. Note that the bits will + * be reset properly for a 64-bit value when encountering the + * type changing opcodes. */ if (ctx->type == TCG_TYPE_I32) { - ctx->z_mask |= MAKE_64BIT_MASK(32, 32); - a_mask &= MAKE_64BIT_MASK(0, 32); - z_mask &= MAKE_64BIT_MASK(0, 32); + a_mask = (int32_t)a_mask; + z_mask = (int32_t)z_mask; + ctx->z_mask = z_mask; } if (z_mask == 0) { From 4e858d96aa3997113014a31afabcf95fb36d2860 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 07:31:13 -0700 Subject: [PATCH 0840/1334] tcg/optimize: Use fold_xx_to_i for orc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recognize the constant function for or-complement. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index e0abf769d0..6d795954f2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1586,6 +1586,7 @@ static bool fold_or(OptContext *ctx, TCGOp *op) static bool fold_orc(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || + fold_xx_to_i(ctx, op, -1) || fold_xi_to_x(ctx, op, -1) || fold_ix_to_not(ctx, op, 0)) { return true; From 5b5cf47983c37d4c49eb2b8449bc62a7d3d76dbc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Oct 2021 11:19:14 -0700 Subject: [PATCH 0841/1334] tcg/optimize: Use fold_xi_to_x for mul MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recognize the identity function for low-part multiply. Suggested-by: Luis Pires Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 6d795954f2..907049fb06 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1461,7 +1461,8 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) static bool fold_mul(OptContext *ctx, TCGOp *op) { if (fold_const2(ctx, op) || - fold_xi_to_i(ctx, op, 0)) { + fold_xi_to_i(ctx, op, 0) || + fold_xi_to_x(ctx, op, 1)) { return true; } return false; From 2f9d9a34221feb80d5b1fc6b584f910f8cee317a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Oct 2021 11:30:14 -0700 Subject: [PATCH 0842/1334] tcg/optimize: Use fold_xi_to_x for div MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recognize the identity function for division. Suggested-by: Luis Pires Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 907049fb06..f8b0709157 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1218,7 +1218,11 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) static bool fold_divide(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xi_to_x(ctx, op, 1)) { + return true; + } + return false; } static bool fold_dup(OptContext *ctx, TCGOp *op) From 267c17e8252fd70b311871f3e936eb7cdf4abb51 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Oct 2021 11:30:33 -0700 Subject: [PATCH 0843/1334] tcg/optimize: Use fold_xx_to_i for rem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recognize the constant function for remainder. Suggested-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f8b0709157..7ac63c9231 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1624,7 +1624,11 @@ static bool fold_qemu_st(OptContext *ctx, TCGOp *op) static bool fold_remainder(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op); + if (fold_const2(ctx, op) || + fold_xx_to_i(ctx, op, 0)) { + return true; + } + return false; } static bool fold_setcond(OptContext *ctx, TCGOp *op) From 57fe5c6df21c05af11435b2ed426a42ee3345298 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 12:04:46 -0700 Subject: [PATCH 0844/1334] tcg/optimize: Optimize sign extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Certain targets, like riscv, produce signed 32-bit results. This can lead to lots of redundant extensions as values are manipulated. Begin by tracking only the obvious sign-extensions, and converting them to simple copies when possible. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 123 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 21 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 7ac63c9231..ef202abbcb 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -43,6 +43,7 @@ typedef struct TempOptInfo { TCGTemp *next_copy; uint64_t val; uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */ + uint64_t s_mask; /* a left-aligned mask of clrsb(value) bits. */ } TempOptInfo; typedef struct OptContext { @@ -53,9 +54,37 @@ typedef struct OptContext { /* In flight values from optimization. */ uint64_t a_mask; /* mask bit is 0 iff value identical to first input */ uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */ + uint64_t s_mask; /* mask of clrsb(value) bits */ TCGType type; } OptContext; +/* Calculate the smask for a specific value. */ +static uint64_t smask_from_value(uint64_t value) +{ + int rep = clrsb64(value); + return ~(~0ull >> rep); +} + +/* + * Calculate the smask for a given set of known-zeros. + * If there are lots of zeros on the left, we can consider the remainder + * an unsigned field, and thus the corresponding signed field is one bit + * larger. + */ +static uint64_t smask_from_zmask(uint64_t zmask) +{ + /* + * Only the 0 bits are significant for zmask, thus the msb itself + * must be zero, else we have no sign information. + */ + int rep = clz64(zmask); + if (rep == 0) { + return 0; + } + rep -= 1; + return ~(~0ull >> rep); +} + static inline TempOptInfo *ts_info(TCGTemp *ts) { return ts->state_ptr; @@ -94,6 +123,7 @@ static void reset_ts(TCGTemp *ts) ti->prev_copy = ts; ti->is_const = false; ti->z_mask = -1; + ti->s_mask = 0; } static void reset_temp(TCGArg arg) @@ -124,9 +154,11 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) ti->is_const = true; ti->val = ts->val; ti->z_mask = ts->val; + ti->s_mask = smask_from_value(ts->val); } else { ti->is_const = false; ti->z_mask = -1; + ti->s_mask = 0; } } @@ -220,6 +252,7 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) op->args[1] = src; di->z_mask = si->z_mask; + di->s_mask = si->s_mask; if (src_ts->type == dst_ts->type) { TempOptInfo *ni = ts_info(si->next_copy); @@ -658,13 +691,15 @@ static void finish_folding(OptContext *ctx, TCGOp *op) nb_oargs = def->nb_oargs; for (i = 0; i < nb_oargs; i++) { - reset_temp(op->args[i]); + TCGTemp *ts = arg_temp(op->args[i]); + reset_ts(ts); /* - * Save the corresponding known-zero bits mask for the + * Save the corresponding known-zero/sign bits mask for the * first output argument (only one supported so far). */ if (i == 0) { - arg_info(op->args[i])->z_mask = ctx->z_mask; + ts_info(ts)->z_mask = ctx->z_mask; + ts_info(ts)->s_mask = ctx->s_mask; } } } @@ -714,6 +749,7 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) { uint64_t a_mask = ctx->a_mask; uint64_t z_mask = ctx->z_mask; + uint64_t s_mask = ctx->s_mask; /* * 32-bit ops generate 32-bit results, which for the purpose of @@ -725,7 +761,9 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) if (ctx->type == TCG_TYPE_I32) { a_mask = (int32_t)a_mask; z_mask = (int32_t)z_mask; + s_mask |= MAKE_64BIT_MASK(32, 32); ctx->z_mask = z_mask; + ctx->s_mask = s_mask; } if (z_mask == 0) { @@ -1072,7 +1110,7 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) static bool fold_bswap(OptContext *ctx, TCGOp *op) { - uint64_t z_mask, sign; + uint64_t z_mask, s_mask, sign; if (arg_is_const(op->args[1])) { uint64_t t = arg_info(op->args[1])->val; @@ -1082,6 +1120,7 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) } z_mask = arg_info(op->args[1])->z_mask; + switch (op->opc) { case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: @@ -1100,6 +1139,7 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) default: g_assert_not_reached(); } + s_mask = smask_from_zmask(z_mask); switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { case TCG_BSWAP_OZ: @@ -1108,14 +1148,17 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) /* If the sign bit may be 1, force all the bits above to 1. */ if (z_mask & sign) { z_mask |= sign; + s_mask = sign << 1; } break; default: /* The high bits are undefined: force all bits above the sign to 1. */ z_mask |= sign << 1; + s_mask = 0; break; } ctx->z_mask = z_mask; + ctx->s_mask = s_mask; return fold_masks(ctx, op); } @@ -1263,21 +1306,24 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) static bool fold_extract(OptContext *ctx, TCGOp *op) { uint64_t z_mask_old, z_mask; + int pos = op->args[2]; + int len = op->args[3]; if (arg_is_const(op->args[1])) { uint64_t t; t = arg_info(op->args[1])->val; - t = extract64(t, op->args[2], op->args[3]); + t = extract64(t, pos, len); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } z_mask_old = arg_info(op->args[1])->z_mask; - z_mask = extract64(z_mask_old, op->args[2], op->args[3]); - if (op->args[2] == 0) { + z_mask = extract64(z_mask_old, pos, len); + if (pos == 0) { ctx->a_mask = z_mask_old ^ z_mask; } ctx->z_mask = z_mask; + ctx->s_mask = smask_from_zmask(z_mask); return fold_masks(ctx, op); } @@ -1303,14 +1349,16 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) static bool fold_exts(OptContext *ctx, TCGOp *op) { - uint64_t z_mask_old, z_mask, sign; + uint64_t s_mask_old, s_mask, z_mask, sign; bool type_change = false; if (fold_const1(ctx, op)) { return true; } - z_mask_old = z_mask = arg_info(op->args[1])->z_mask; + z_mask = arg_info(op->args[1])->z_mask; + s_mask = arg_info(op->args[1])->s_mask; + s_mask_old = s_mask; switch (op->opc) { CASE_OP_32_64(ext8s): @@ -1334,10 +1382,14 @@ static bool fold_exts(OptContext *ctx, TCGOp *op) if (z_mask & sign) { z_mask |= sign; - } else if (!type_change) { - ctx->a_mask = z_mask_old ^ z_mask; } + s_mask |= sign << 1; + ctx->z_mask = z_mask; + ctx->s_mask = s_mask; + if (!type_change) { + ctx->a_mask = s_mask & ~s_mask_old; + } return fold_masks(ctx, op); } @@ -1376,6 +1428,7 @@ static bool fold_extu(OptContext *ctx, TCGOp *op) } ctx->z_mask = z_mask; + ctx->s_mask = smask_from_zmask(z_mask); if (!type_change) { ctx->a_mask = z_mask_old ^ z_mask; } @@ -1606,8 +1659,12 @@ static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) MemOp mop = get_memop(oi); int width = 8 * memop_size(mop); - if (!(mop & MO_SIGN) && width < 64) { - ctx->z_mask = MAKE_64BIT_MASK(0, width); + if (width < 64) { + ctx->s_mask = MAKE_64BIT_MASK(width, 64 - width); + if (!(mop & MO_SIGN)) { + ctx->z_mask = MAKE_64BIT_MASK(0, width); + ctx->s_mask <<= 1; + } } /* Opcodes that touch guest memory stop the mb optimization. */ @@ -1726,23 +1783,31 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) static bool fold_sextract(OptContext *ctx, TCGOp *op) { - int64_t z_mask_old, z_mask; + uint64_t z_mask, s_mask, s_mask_old; + int pos = op->args[2]; + int len = op->args[3]; if (arg_is_const(op->args[1])) { uint64_t t; t = arg_info(op->args[1])->val; - t = sextract64(t, op->args[2], op->args[3]); + t = sextract64(t, pos, len); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } - z_mask_old = arg_info(op->args[1])->z_mask; - z_mask = sextract64(z_mask_old, op->args[2], op->args[3]); - if (op->args[2] == 0 && z_mask >= 0) { - ctx->a_mask = z_mask_old ^ z_mask; - } + z_mask = arg_info(op->args[1])->z_mask; + z_mask = sextract64(z_mask, pos, len); ctx->z_mask = z_mask; + s_mask_old = arg_info(op->args[1])->s_mask; + s_mask = sextract64(s_mask_old, pos, len); + s_mask |= MAKE_64BIT_MASK(len, 64 - len); + ctx->s_mask = s_mask; + + if (pos == 0) { + ctx->a_mask = s_mask & ~s_mask_old; + } + return fold_masks(ctx, op); } @@ -1819,14 +1884,26 @@ static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) { /* We can't do any folding with a load, but we can record bits. */ switch (op->opc) { + CASE_OP_32_64(ld8s): + ctx->s_mask = MAKE_64BIT_MASK(8, 56); + break; CASE_OP_32_64(ld8u): ctx->z_mask = MAKE_64BIT_MASK(0, 8); + ctx->s_mask = MAKE_64BIT_MASK(9, 55); + break; + CASE_OP_32_64(ld16s): + ctx->s_mask = MAKE_64BIT_MASK(16, 48); break; CASE_OP_32_64(ld16u): ctx->z_mask = MAKE_64BIT_MASK(0, 16); + ctx->s_mask = MAKE_64BIT_MASK(17, 47); + break; + case INDEX_op_ld32s_i64: + ctx->s_mask = MAKE_64BIT_MASK(32, 32); break; case INDEX_op_ld32u_i64: ctx->z_mask = MAKE_64BIT_MASK(0, 32); + ctx->s_mask = MAKE_64BIT_MASK(33, 31); break; default: g_assert_not_reached(); @@ -1889,9 +1966,10 @@ void tcg_optimize(TCGContext *s) ctx.type = TCG_TYPE_I32; } - /* Assume all bits affected, and no bits known zero. */ + /* Assume all bits affected, no bits known zero, no sign reps. */ ctx.a_mask = -1; ctx.z_mask = -1; + ctx.s_mask = 0; /* * Process each opcode. @@ -1964,8 +2042,11 @@ void tcg_optimize(TCGContext *s) case INDEX_op_extrh_i64_i32: done = fold_extu(&ctx, op); break; + CASE_OP_32_64(ld8s): CASE_OP_32_64(ld8u): + CASE_OP_32_64(ld16s): CASE_OP_32_64(ld16u): + case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: done = fold_tcg_ld(&ctx, op); break; From 3f2b1f8376c11327ca2ea54cdc1085d4d4c1d97c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 13:08:54 -0700 Subject: [PATCH 0845/1334] tcg/optimize: Propagate sign info for logical operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sign repetitions are perforce all identical, whether they are 1 or 0. Bitwise operations preserve the relative quantity of the repetitions. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index ef202abbcb..de1abd9cc3 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -967,6 +967,13 @@ static bool fold_and(OptContext *ctx, TCGOp *op) z2 = arg_info(op->args[2])->z_mask; ctx->z_mask = z1 & z2; + /* + * Sign repetitions are perforce all identical, whether they are 1 or 0. + * Bitwise operations preserve the relative quantity of the repetitions. + */ + ctx->s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; + /* * Known-zeros does not imply known-ones. Therefore unless * arg2 is constant, we can't infer affected bits from it. @@ -1002,6 +1009,8 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) } ctx->z_mask = z1; + ctx->s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; return fold_masks(ctx, op); } @@ -1300,6 +1309,9 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) fold_xi_to_not(ctx, op, 0)) { return true; } + + ctx->s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; return false; } @@ -1487,6 +1499,8 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) ctx->z_mask = arg_info(op->args[3])->z_mask | arg_info(op->args[4])->z_mask; + ctx->s_mask = arg_info(op->args[3])->s_mask + & arg_info(op->args[4])->s_mask; if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { uint64_t tv = arg_info(op->args[3])->val; @@ -1585,6 +1599,9 @@ static bool fold_nand(OptContext *ctx, TCGOp *op) fold_xi_to_not(ctx, op, -1)) { return true; } + + ctx->s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; return false; } @@ -1614,6 +1631,9 @@ static bool fold_nor(OptContext *ctx, TCGOp *op) fold_xi_to_not(ctx, op, 0)) { return true; } + + ctx->s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; return false; } @@ -1623,6 +1643,8 @@ static bool fold_not(OptContext *ctx, TCGOp *op) return true; } + ctx->s_mask = arg_info(op->args[1])->s_mask; + /* Because of fold_to_not, we want to always return true, via finish. */ finish_folding(ctx, op); return true; @@ -1638,6 +1660,8 @@ static bool fold_or(OptContext *ctx, TCGOp *op) ctx->z_mask = arg_info(op->args[1])->z_mask | arg_info(op->args[2])->z_mask; + ctx->s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; return fold_masks(ctx, op); } @@ -1649,6 +1673,9 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) fold_ix_to_not(ctx, op, 0)) { return true; } + + ctx->s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; return false; } @@ -1922,6 +1949,8 @@ static bool fold_xor(OptContext *ctx, TCGOp *op) ctx->z_mask = arg_info(op->args[1])->z_mask | arg_info(op->args[2])->z_mask; + ctx->s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; return fold_masks(ctx, op); } From 275d7d8e70ef8679e241ce78c83548067f403394 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 13:20:39 -0700 Subject: [PATCH 0846/1334] tcg/optimize: Propagate sign info for setcond MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The result is either 0 or 1, which means that we have a 2 bit signed result, and thus 62 bits of sign. For clarity, use the smask_from_zmask function. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index de1abd9cc3..5fa4d7285d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1730,6 +1730,7 @@ static bool fold_setcond(OptContext *ctx, TCGOp *op) } ctx->z_mask = 1; + ctx->s_mask = smask_from_zmask(1); return false; } @@ -1802,6 +1803,7 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) } ctx->z_mask = 1; + ctx->s_mask = smask_from_zmask(1); return false; do_setcond_const: From 2b9d0c59edec097c72ce9b917d3c08dc5d59cdda Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 13:24:17 -0700 Subject: [PATCH 0847/1334] tcg/optimize: Propagate sign info for bit counting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The results are generally 6 bit unsigned values, though the count leading and trailing bits may produce any value for a zero input. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 5fa4d7285d..c0eccc61d6 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1229,7 +1229,7 @@ static bool fold_count_zeros(OptContext *ctx, TCGOp *op) g_assert_not_reached(); } ctx->z_mask = arg_info(op->args[2])->z_mask | z_mask; - + ctx->s_mask = smask_from_zmask(ctx->z_mask); return false; } @@ -1249,6 +1249,7 @@ static bool fold_ctpop(OptContext *ctx, TCGOp *op) default: g_assert_not_reached(); } + ctx->s_mask = smask_from_zmask(ctx->z_mask); return false; } From 93a967fbb571ae34857c769dbf0bcc08f2286328 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Aug 2021 13:24:59 -0700 Subject: [PATCH 0848/1334] tcg/optimize: Propagate sign info for shifting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For constant shifts, we can simply shift the s_mask. For variable shifts, we know that sar does not reduce the s_mask, which helps for sequences like ext32s_i64 t, in sar_i64 t, t, v ext32s_i64 out, t allowing the final extend to be eliminated. Reviewed-by: Alex Bennée Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- tcg/optimize.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index c0eccc61d6..dbb2d46e88 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -85,6 +85,18 @@ static uint64_t smask_from_zmask(uint64_t zmask) return ~(~0ull >> rep); } +/* + * Recreate a properly left-aligned smask after manipulation. + * Some bit-shuffling, particularly shifts and rotates, may + * retain sign bits on the left, but may scatter disconnected + * sign bits on the right. Retain only what remains to the left. + */ +static uint64_t smask_from_smask(int64_t smask) +{ + /* Only the 1 bits are significant for smask */ + return smask_from_zmask(~smask); +} + static inline TempOptInfo *ts_info(TCGTemp *ts) { return ts->state_ptr; @@ -1843,18 +1855,50 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) static bool fold_shift(OptContext *ctx, TCGOp *op) { + uint64_t s_mask, z_mask, sign; + if (fold_const2(ctx, op) || fold_ix_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 0)) { return true; } + s_mask = arg_info(op->args[1])->s_mask; + z_mask = arg_info(op->args[1])->z_mask; + if (arg_is_const(op->args[2])) { - ctx->z_mask = do_constant_folding(op->opc, ctx->type, - arg_info(op->args[1])->z_mask, - arg_info(op->args[2])->val); + int sh = arg_info(op->args[2])->val; + + ctx->z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); + + s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); + ctx->s_mask = smask_from_smask(s_mask); + return fold_masks(ctx, op); } + + switch (op->opc) { + CASE_OP_32_64(sar): + /* + * Arithmetic right shift will not reduce the number of + * input sign repetitions. + */ + ctx->s_mask = s_mask; + break; + CASE_OP_32_64(shr): + /* + * If the sign bit is known zero, then logical right shift + * will not reduced the number of input sign repetitions. + */ + sign = (s_mask & -s_mask) >> 1; + if (!(z_mask & sign)) { + ctx->s_mask = s_mask; + } + break; + default: + break; + } + return false; } From 9f660c077b53f58792b6fa6a45b2c3eb1e25b716 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Thu, 28 Oct 2021 14:48:05 +0300 Subject: [PATCH 0849/1334] softmmu: fix watchpoint processing in icount mode Watchpoint processing code restores vCPU state twice: in tb_check_watchpoint and in cpu_loop_exit_restore/cpu_restore_state. Normally it does not affect anything, but in icount mode instruction counter is incremented twice and becomes incorrect. This patch eliminates unneeded CPU state restore. Signed-off-by: Pavel Dovgalyuk Reviewed-by: David Hildenbrand Reviewed-by: Richard Henderson Message-Id: <163542168516.2127597.8781375223437124644.stgit@pasha-ThinkPad-X280> Signed-off-by: Richard Henderson --- softmmu/physmem.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 555c907f67..d58752678d 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -938,18 +938,16 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, cpu->watchpoint_hit = wp; mmap_lock(); + /* This call also restores vCPU state */ tb_check_watchpoint(cpu, ra); if (wp->flags & BP_STOP_BEFORE_ACCESS) { cpu->exception_index = EXCP_DEBUG; mmap_unlock(); - cpu_loop_exit_restore(cpu, ra); + cpu_loop_exit(cpu); } else { /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | curr_cflags(cpu); mmap_unlock(); - if (ra) { - cpu_restore_state(cpu, ra, true); - } cpu_loop_exit_noexc(cpu); } } From 1ab0ba8ab525046f4727c6e1b146e779db8b0489 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Thu, 28 Oct 2021 14:48:10 +0300 Subject: [PATCH 0850/1334] softmmu: remove useless condition in watchpoint check cpu_check_watchpoint function checks cpu->watchpoint_hit at the entry. But then it also does the same in the middle of the function, while this field can't change. That is why this patch removes this useless condition. Signed-off-by: Pavel Dovgalyuk Reviewed-by: Richard Henderson Message-Id: <163542169094.2127597.8801843697434113110.stgit@pasha-ThinkPad-X280> Signed-off-by: Richard Henderson --- softmmu/physmem.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index d58752678d..9765a509d2 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -929,27 +929,26 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, } wp->hitaddr = MAX(addr, wp->vaddr); wp->hitattrs = attrs; - if (!cpu->watchpoint_hit) { - if (wp->flags & BP_CPU && cc->tcg_ops->debug_check_watchpoint && - !cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { - wp->flags &= ~BP_WATCHPOINT_HIT; - continue; - } - cpu->watchpoint_hit = wp; - mmap_lock(); - /* This call also restores vCPU state */ - tb_check_watchpoint(cpu, ra); - if (wp->flags & BP_STOP_BEFORE_ACCESS) { - cpu->exception_index = EXCP_DEBUG; - mmap_unlock(); - cpu_loop_exit(cpu); - } else { - /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | curr_cflags(cpu); - mmap_unlock(); - cpu_loop_exit_noexc(cpu); - } + if (wp->flags & BP_CPU && cc->tcg_ops->debug_check_watchpoint && + !cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { + wp->flags &= ~BP_WATCHPOINT_HIT; + continue; + } + cpu->watchpoint_hit = wp; + + mmap_lock(); + /* This call also restores vCPU state */ + tb_check_watchpoint(cpu, ra); + if (wp->flags & BP_STOP_BEFORE_ACCESS) { + cpu->exception_index = EXCP_DEBUG; + mmap_unlock(); + cpu_loop_exit(cpu); + } else { + /* Force execution of one insn next time. */ + cpu->cflags_next_tb = 1 | curr_cflags(cpu); + mmap_unlock(); + cpu_loop_exit_noexc(cpu); } } else { wp->flags &= ~BP_WATCHPOINT_HIT; From efd629fb21e2ff6a8f62642d9ed7a23dfee4d320 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Thu, 28 Oct 2021 14:48:17 +0300 Subject: [PATCH 0851/1334] softmmu: fix for "after access" watchpoints Watchpoints that should fire after the memory access break an execution of the current block, try to translate current instruction into the separate block, which then causes debug interrupt. But cpu_interrupt can't be called in such block when icount is enabled, because interrupts muse be allowed explicitly. This patch sets CF_LAST_IO flag for retranslated block, allowing interrupt request for the last instruction. Signed-off-by: Pavel Dovgalyuk Reviewed-by: Richard Henderson Message-Id: <163542169727.2127597.8141772572696627329.stgit@pasha-ThinkPad-X280> Signed-off-by: Richard Henderson --- softmmu/physmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 9765a509d2..b9a8c1d1f4 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -946,7 +946,7 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, cpu_loop_exit(cpu); } else { /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | curr_cflags(cpu); + cpu->cflags_next_tb = 1 | CF_LAST_IO | curr_cflags(cpu); mmap_unlock(); cpu_loop_exit_noexc(cpu); } From 487a99551ae903fc83a878d4cbc6d853e17ad252 Mon Sep 17 00:00:00 2001 From: Jose Martins Date: Tue, 26 Oct 2021 15:51:25 +0100 Subject: [PATCH 0852/1334] target/riscv: fix VS interrupts forwarding to HS VS interrupts (2, 6, 10) were not correctly forwarded to hs-mode when not delegated in hideleg (which was not being taken into account). This was mainly because hs level sie was not always considered enabled when it should. The spec states that "Interrupts for higher-privilege modes, y>x, are always globally enabled regardless of the setting of the global yIE bit for the higher-privilege mode." and also "For purposes of interrupt global enables, HS-mode is considered more privileged than VS-mode, and VS-mode is considered more privileged than VU-mode". Also, vs-level interrupts were not being taken into account unless V=1, but should be unless delegated. Finally, there is no need for a special case for to handle vs interrupts as the current privilege level, the state of the global ie and of the delegation registers should be enough to route all interrupts to the appropriate privilege level in riscv_cpu_do_interrupt. Signed-off-by: Jose Martins Reviewed-by: Alistair Francis Message-id: 20211026145126.11025-2-josemartins90@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 662228c238..5076580374 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -135,36 +135,24 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, #ifndef CONFIG_USER_ONLY static int riscv_cpu_local_irq_pending(CPURISCVState *env) { - target_ulong irqs; + target_ulong virt_enabled = riscv_cpu_virt_enabled(env); target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE); target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE); - target_ulong hs_mstatus_sie = get_field(env->mstatus_hs, MSTATUS_SIE); - target_ulong pending = env->mip & env->mie & - ~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); - target_ulong vspending = (env->mip & env->mie & - (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)); + target_ulong pending = env->mip & env->mie; target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie); target_ulong sie = env->priv < PRV_S || (env->priv == PRV_S && mstatus_sie); - target_ulong hs_sie = env->priv < PRV_S || - (env->priv == PRV_S && hs_mstatus_sie); + target_ulong hsie = virt_enabled || sie; + target_ulong vsie = virt_enabled && sie; - if (riscv_cpu_virt_enabled(env)) { - target_ulong pending_hs_irq = pending & -hs_sie; - - if (pending_hs_irq) { - riscv_cpu_set_force_hs_excep(env, FORCE_HS_EXCEP); - return ctz64(pending_hs_irq); - } - - pending = vspending; - } - - irqs = (pending & ~env->mideleg & -mie) | (pending & env->mideleg & -sie); + target_ulong irqs = + (pending & ~env->mideleg & -mie) | + (pending & env->mideleg & ~env->hideleg & -hsie) | + (pending & env->mideleg & env->hideleg & -vsie); if (irqs) { return ctz64(irqs); /* since non-zero */ From 50d160876414e91e51ac718ac6edea6dbadf4694 Mon Sep 17 00:00:00 2001 From: Jose Martins Date: Tue, 26 Oct 2021 15:51:26 +0100 Subject: [PATCH 0853/1334] target/riscv: remove force HS exception There is no need to "force an hs exception" as the current privilege level, the state of the global ie and of the delegation registers should be enough to route the interrupt to the appropriate privilege level in riscv_cpu_do_interrupt. The is true for both asynchronous and synchronous exceptions, specifically, guest page faults which must be hardwired to zero hedeleg. As such the hs_force_except mechanism can be removed. Signed-off-by: Jose Martins Reviewed-by: Alistair Francis Message-id: 20211026145126.11025-3-josemartins90@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 2 -- target/riscv/cpu_bits.h | 6 ------ target/riscv/cpu_helper.c | 26 +------------------------- 3 files changed, 1 insertion(+), 33 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 325908287d..0760c0af93 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -352,8 +352,6 @@ int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); bool riscv_cpu_fp_enabled(CPURISCVState *env); bool riscv_cpu_virt_enabled(CPURISCVState *env); void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); -bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env); -void riscv_cpu_set_force_hs_excep(CPURISCVState *env, bool enable); bool riscv_cpu_two_stage_lookup(int mmu_idx); int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index aa0bce4e06..9913fa9f77 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -444,12 +444,6 @@ typedef enum { /* Virtulisation Register Fields */ #define VIRT_ONOFF 1 -/* This is used to save state for when we take an exception. If this is set - * that means that we want to force a HS level exception (no matter what the - * delegation is set to). This will occur for things such as a second level - * page table fault. - */ -#define FORCE_HS_EXCEP 2 /* RV32 satp CSR field masks */ #define SATP32_MODE 0x80000000 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 5076580374..f30ff672f8 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -270,24 +270,6 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) env->virt = set_field(env->virt, VIRT_ONOFF, enable); } -bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env) -{ - if (!riscv_has_ext(env, RVH)) { - return false; - } - - return get_field(env->virt, FORCE_HS_EXCEP); -} - -void riscv_cpu_set_force_hs_excep(CPURISCVState *env, bool enable) -{ - if (!riscv_has_ext(env, RVH)) { - return; - } - - env->virt = set_field(env->virt, FORCE_HS_EXCEP, enable); -} - bool riscv_cpu_two_stage_lookup(int mmu_idx) { return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK; @@ -1004,7 +986,6 @@ void riscv_cpu_do_interrupt(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - bool force_hs_execp = riscv_cpu_force_hs_excep_enabled(env); uint64_t s; /* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide @@ -1033,8 +1014,6 @@ void riscv_cpu_do_interrupt(CPUState *cs) case RISCV_EXCP_INST_GUEST_PAGE_FAULT: case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: - force_hs_execp = true; - /* fallthrough */ case RISCV_EXCP_INST_ADDR_MIS: case RISCV_EXCP_INST_ACCESS_FAULT: case RISCV_EXCP_LOAD_ADDR_MIS: @@ -1093,8 +1072,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->hstatus = set_field(env->hstatus, HSTATUS_GVA, 0); } - if (riscv_cpu_virt_enabled(env) && ((hdeleg >> cause) & 1) && - !force_hs_execp) { + if (riscv_cpu_virt_enabled(env) && ((hdeleg >> cause) & 1)) { /* Trap to VS mode */ /* * See if we need to adjust cause. Yes if its VS mode interrupt @@ -1116,7 +1094,6 @@ void riscv_cpu_do_interrupt(CPUState *cs) htval = env->guest_phys_fault_addr; riscv_cpu_set_virt_enabled(env, 0); - riscv_cpu_set_force_hs_excep(env, 0); } else { /* Trap into HS mode */ env->hstatus = set_field(env->hstatus, HSTATUS_SPV, false); @@ -1152,7 +1129,6 @@ void riscv_cpu_do_interrupt(CPUState *cs) /* Trapping to M mode, virt is disabled */ riscv_cpu_set_virt_enabled(env, 0); - riscv_cpu_set_force_hs_excep(env, 0); } s = env->mstatus; From 0e9030376e1a8eb6d15cb5e69dffa09a6ff16b92 Mon Sep 17 00:00:00 2001 From: Chih-Min Chao Date: Fri, 22 Oct 2021 00:08:45 +0800 Subject: [PATCH 0854/1334] softfloat: add APIs to handle alternative sNaN propagation for fmax/fmin For "fmax/fmin ft0, ft1, ft2" and if one of the inputs is sNaN, The original logic: Return NaN and set invalid flag if ft1 == sNaN || ft2 == sNan. The alternative path: Set invalid flag if ft1 == sNaN || ft2 == sNaN. Return NaN only if ft1 == NaN && ft2 == NaN. The IEEE 754 spec allows both implementation and some architecture such as riscv choose different defintions in two spec versions. (riscv-spec-v2.2 use original version, riscv-spec-20191213 changes to alternative) Signed-off-by: Chih-Min Chao Signed-off-by: Frank Chang Reviewed-by: Richard Henderson Message-Id: <20211021160847.2748577-2-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- fpu/softfloat-parts.c.inc | 25 +++++++++++++++++++++++-- fpu/softfloat.c | 19 +++++++++++++------ include/fpu/softfloat.h | 10 ++++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index dddee92d6e..41d4b17e41 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -1219,14 +1219,35 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, if (unlikely(ab_mask & float_cmask_anynan)) { /* - * For minnum/maxnum, if one operand is a QNaN, and the other + * For minNum/maxNum (IEEE 754-2008) + * or minimumNumber/maximumNumber (IEEE 754-2019), + * if one operand is a QNaN, and the other * operand is numerical, then return numerical argument. */ - if ((flags & minmax_isnum) + if ((flags & (minmax_isnum | minmax_isnumber)) && !(ab_mask & float_cmask_snan) && (ab_mask & ~float_cmask_qnan)) { return is_nan(a->cls) ? b : a; } + + /* + * In IEEE 754-2019, minNum, maxNum, minNumMag and maxNumMag + * are removed and replaced with minimum, minimumNumber, maximum + * and maximumNumber. + * minimumNumber/maximumNumber behavior for SNaN is changed to: + * If both operands are NaNs, a QNaN is returned. + * If either operand is a SNaN, + * an invalid operation exception is signaled, + * but unless both operands are NaNs, + * the SNaN is otherwise ignored and not converted to a QNaN. + */ + if ((flags & minmax_isnumber) + && (ab_mask & float_cmask_snan) + && (ab_mask & ~float_cmask_anynan)) { + float_raise(float_flag_invalid, s); + return is_nan(a->cls) ? b : a; + } + return parts_pick_nan(a, b, s); } diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 6e769f990c..9a28720d82 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -436,6 +436,11 @@ enum { minmax_isnum = 2, /* Set for the IEEE 754-2008 minNumMag() and minNumMag() operations. */ minmax_ismag = 4, + /* + * Set for the IEEE 754-2019 minimumNumber() and maximumNumber() + * operations. + */ + minmax_isnumber = 8, }; /* Simple helpers for checking if, or what kind of, NaN we have */ @@ -3927,12 +3932,14 @@ static float128 float128_minmax(float128 a, float128 b, { return type##_minmax(a, b, s, flags); } #define MINMAX_2(type) \ - MINMAX_1(type, max, 0) \ - MINMAX_1(type, maxnum, minmax_isnum) \ - MINMAX_1(type, maxnummag, minmax_isnum | minmax_ismag) \ - MINMAX_1(type, min, minmax_ismin) \ - MINMAX_1(type, minnum, minmax_ismin | minmax_isnum) \ - MINMAX_1(type, minnummag, minmax_ismin | minmax_isnum | minmax_ismag) + MINMAX_1(type, max, 0) \ + MINMAX_1(type, maxnum, minmax_isnum) \ + MINMAX_1(type, maxnummag, minmax_isnum | minmax_ismag) \ + MINMAX_1(type, maximum_number, minmax_isnumber) \ + MINMAX_1(type, min, minmax_ismin) \ + MINMAX_1(type, minnum, minmax_ismin | minmax_isnum) \ + MINMAX_1(type, minnummag, minmax_ismin | minmax_isnum | minmax_ismag) \ + MINMAX_1(type, minimum_number, minmax_ismin | minmax_isnumber) \ MINMAX_2(float16) MINMAX_2(bfloat16) diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index ec7dca0960..a249991e61 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -243,6 +243,8 @@ float16 float16_minnum(float16, float16, float_status *status); float16 float16_maxnum(float16, float16, float_status *status); float16 float16_minnummag(float16, float16, float_status *status); float16 float16_maxnummag(float16, float16, float_status *status); +float16 float16_minimum_number(float16, float16, float_status *status); +float16 float16_maximum_number(float16, float16, float_status *status); float16 float16_sqrt(float16, float_status *status); FloatRelation float16_compare(float16, float16, float_status *status); FloatRelation float16_compare_quiet(float16, float16, float_status *status); @@ -422,6 +424,8 @@ bfloat16 bfloat16_minnum(bfloat16, bfloat16, float_status *status); bfloat16 bfloat16_maxnum(bfloat16, bfloat16, float_status *status); bfloat16 bfloat16_minnummag(bfloat16, bfloat16, float_status *status); bfloat16 bfloat16_maxnummag(bfloat16, bfloat16, float_status *status); +bfloat16 bfloat16_minimum_number(bfloat16, bfloat16, float_status *status); +bfloat16 bfloat16_maximum_number(bfloat16, bfloat16, float_status *status); bfloat16 bfloat16_sqrt(bfloat16, float_status *status); FloatRelation bfloat16_compare(bfloat16, bfloat16, float_status *status); FloatRelation bfloat16_compare_quiet(bfloat16, bfloat16, float_status *status); @@ -589,6 +593,8 @@ float32 float32_minnum(float32, float32, float_status *status); float32 float32_maxnum(float32, float32, float_status *status); float32 float32_minnummag(float32, float32, float_status *status); float32 float32_maxnummag(float32, float32, float_status *status); +float32 float32_minimum_number(float32, float32, float_status *status); +float32 float32_maximum_number(float32, float32, float_status *status); bool float32_is_quiet_nan(float32, float_status *status); bool float32_is_signaling_nan(float32, float_status *status); float32 float32_silence_nan(float32, float_status *status); @@ -778,6 +784,8 @@ float64 float64_minnum(float64, float64, float_status *status); float64 float64_maxnum(float64, float64, float_status *status); float64 float64_minnummag(float64, float64, float_status *status); float64 float64_maxnummag(float64, float64, float_status *status); +float64 float64_minimum_number(float64, float64, float_status *status); +float64 float64_maximum_number(float64, float64, float_status *status); bool float64_is_quiet_nan(float64 a, float_status *status); bool float64_is_signaling_nan(float64, float_status *status); float64 float64_silence_nan(float64, float_status *status); @@ -1210,6 +1218,8 @@ float128 float128_minnum(float128, float128, float_status *status); float128 float128_maxnum(float128, float128, float_status *status); float128 float128_minnummag(float128, float128, float_status *status); float128 float128_maxnummag(float128, float128, float_status *status); +float128 float128_minimum_number(float128, float128, float_status *status); +float128 float128_maximum_number(float128, float128, float_status *status); bool float128_is_quiet_nan(float128, float_status *status); bool float128_is_signaling_nan(float128, float_status *status); float128 float128_silence_nan(float128, float_status *status); From 15161e425ee1bb1180f9cec574cda44fb10c0931 Mon Sep 17 00:00:00 2001 From: Chih-Min Chao Date: Fri, 22 Oct 2021 00:08:46 +0800 Subject: [PATCH 0855/1334] target/riscv: change the api for RVF/RVD fmin/fmax The sNaN propagation behavior has been changed since cd20cee7 in https://github.com/riscv/riscv-isa-manual. In Priv spec v1.10, RVF is v2.0. fmin.s and fmax.s are implemented with IEEE 754-2008 minNum and maxNum operations. In Priv spec v1.11, RVF is v2.2. fmin.s and fmax.s are amended to implement IEEE 754-2019 minimumNumber and maximumNumber operations. Therefore, to prevent the risk of having too many version variables. Instead of introducing an extra *fext_ver* variable, we tie RVF version to Priv version. Though it's not completely accurate but is close enough. Signed-off-by: Chih-Min Chao Signed-off-by: Frank Chang Acked-by: Alistair Francis Message-Id: <20211021160847.2748577-3-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/fpu_helper.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 8700516a14..d62f470900 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -174,14 +174,18 @@ uint64_t helper_fmin_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(rs1); float32 frs2 = check_nanbox_s(rs2); - return nanbox_s(float32_minnum(frs1, frs2, &env->fp_status)); + return nanbox_s(env->priv_ver < PRIV_VERSION_1_11_0 ? + float32_minnum(frs1, frs2, &env->fp_status) : + float32_minimum_number(frs1, frs2, &env->fp_status)); } uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(rs1); float32 frs2 = check_nanbox_s(rs2); - return nanbox_s(float32_maxnum(frs1, frs2, &env->fp_status)); + return nanbox_s(env->priv_ver < PRIV_VERSION_1_11_0 ? + float32_maxnum(frs1, frs2, &env->fp_status) : + float32_maximum_number(frs1, frs2, &env->fp_status)); } uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t rs1) @@ -283,12 +287,16 @@ uint64_t helper_fdiv_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { - return float64_minnum(frs1, frs2, &env->fp_status); + return env->priv_ver < PRIV_VERSION_1_11_0 ? + float64_minnum(frs1, frs2, &env->fp_status) : + float64_minimum_number(frs1, frs2, &env->fp_status); } uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { - return float64_maxnum(frs1, frs2, &env->fp_status); + return env->priv_ver < PRIV_VERSION_1_11_0 ? + float64_maxnum(frs1, frs2, &env->fp_status) : + float64_maximum_number(frs1, frs2, &env->fp_status); } uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1) From a3c45b3e62962f99338716b1347cfb0d427cea44 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:12 +0200 Subject: [PATCH 0856/1334] qapi: New special feature flag "unstable" By convention, names starting with "x-" are experimental. The parts of external interfaces so named may be withdrawn or changed incompatibly in future releases. The naming convention makes unstable interfaces easy to recognize. Promoting something from experimental to stable involves a name change. Client code needs to be updated. Occasionally bothersome. Worse, the convention is not universally observed: * QOM type "input-barrier" has properties "x-origin", "y-origin". Looks accidental, but it's ABI since 4.2. * QOM types "memory-backend-file", "memory-backend-memfd", "memory-backend-ram", and "memory-backend-epc" have a property "x-use-canonical-path-for-ramblock-id" that is documented to be stable despite its name. We could document these exceptions, but documentation helps only humans. We want to recognize "unstable" in code, like "deprecated". So support recognizing it the same way: introduce new special feature flag "unstable". It will be treated specially by the QAPI generator, like the existing feature flag "deprecated", and unlike regular feature flags. This commit updates documentation and prepares tests. The next commit updates the QAPI schema. The remaining patches update the QAPI generator and wire up -compat policy checking. Management applications can then use query-qmp-schema and -compat to manage or guard against use of unstable interfaces the same way as for deprecated interfaces. docs/devel/qapi-code-gen.txt no longer mandates the naming convention. Using it anyway might help writers of programs that aren't full-fledged management applications. Not using it can save us bothersome renames. We'll see how that shakes out. Signed-off-by: Markus Armbruster Reviewed-by: Juan Quintela Reviewed-by: John Snow Message-Id: <20211028102520.747396-2-armbru@redhat.com> --- docs/devel/qapi-code-gen.rst | 9 ++++++--- tests/qapi-schema/qapi-schema-test.json | 7 +++++-- tests/qapi-schema/qapi-schema-test.out | 5 +++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 4071c9074a..38f2d7aad3 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -713,6 +713,10 @@ member as deprecated. It is not supported elsewhere so far. Interfaces so marked may be withdrawn in future releases in accordance with QEMU's deprecation policy. +Feature "unstable" marks a command, event, enum value, or struct +member as unstable. It is not supported elsewhere so far. Interfaces +so marked may be withdrawn or changed incompatibly in future releases. + Naming rules and reserved names ------------------------------- @@ -746,9 +750,8 @@ Member name ``u`` and names starting with ``has-`` or ``has_`` are reserved for the generator, which uses them for unions and for tracking optional members. -Any name (command, event, type, member, or enum value) beginning with -``x-`` is marked experimental, and may be withdrawn or changed -incompatibly in a future release. +Names beginning with ``x-`` used to signify "experimental". This +convention has been replaced by special feature "unstable". Pragmas ``command-name-exceptions`` and ``member-name-exceptions`` let you violate naming rules. Use for new code is strongly discouraged. See diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index b677ab861d..43b8697002 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -273,7 +273,7 @@ 'data': { 'foo': { 'type': 'int', 'features': [ 'deprecated' ] } }, 'features': [ 'feature1' ] } { 'struct': 'FeatureStruct2', - 'data': { 'foo': 'int' }, + 'data': { 'foo': { 'type': 'int', 'features': [ 'unstable' ] } }, 'features': [ { 'name': 'feature1' } ] } { 'struct': 'FeatureStruct3', 'data': { 'foo': 'int' }, @@ -331,7 +331,7 @@ { 'command': 'test-command-features1', 'features': [ 'deprecated' ] } { 'command': 'test-command-features3', - 'features': [ 'feature1', 'feature2' ] } + 'features': [ 'unstable', 'feature1', 'feature2' ] } { 'command': 'test-command-cond-features1', 'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'} ] } @@ -348,3 +348,6 @@ { 'event': 'TEST_EVENT_FEATURES1', 'features': [ 'deprecated' ] } + +{ 'event': 'TEST_EVENT_FEATURES2', + 'features': [ 'unstable' ] } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 16846dbeb8..1f9585fa9b 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -308,6 +308,7 @@ object FeatureStruct1 feature feature1 object FeatureStruct2 member foo: int optional=False + feature unstable feature feature1 object FeatureStruct3 member foo: int optional=False @@ -373,6 +374,7 @@ command test-command-features1 None -> None feature deprecated command test-command-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False + feature unstable feature feature1 feature feature2 command test-command-cond-features1 None -> None @@ -394,6 +396,9 @@ event TEST_EVENT_FEATURES0 FeatureStruct1 event TEST_EVENT_FEATURES1 None boxed=False feature deprecated +event TEST_EVENT_FEATURES2 None + boxed=False + feature unstable module include/sub-module.json include sub-sub-module.json object SecondArrayRef From 9fb49daabfb0052f05981e5a484cd0d3bf283fc6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:13 +0200 Subject: [PATCH 0857/1334] qapi: Mark unstable QMP parts with feature 'unstable' Add special feature 'unstable' everywhere the name starts with 'x-', except for InputBarrierProperties member x-origin and MemoryBackendProperties member x-use-canonical-path-for-ramblock-id, because these two are actually stable. Signed-off-by: Markus Armbruster Reviewed-by: Juan Quintela Acked-by: John Snow Message-Id: <20211028102520.747396-3-armbru@redhat.com> --- qapi/block-core.json | 123 +++++++++++++++++++++++++++++++------------ qapi/migration.json | 35 +++++++++--- qapi/misc.json | 6 ++- qapi/qom.json | 11 ++-- 4 files changed, 130 insertions(+), 45 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 6d3217abb6..ce2c1352cb 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1438,6 +1438,9 @@ # # @x-perf: Performance options. (Since 6.0) # +# Features: +# @unstable: Member @x-perf is experimental. +# # Note: @on-source-error and @on-target-error only affect background # I/O. If an error occurs during a guest write request, the device's # rerror/werror actions will be used. @@ -1452,7 +1455,9 @@ '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', '*auto-finalize': 'bool', '*auto-dismiss': 'bool', - '*filter-node-name': 'str', '*x-perf': 'BackupPerf' } } + '*filter-node-name': 'str', + '*x-perf': { 'type': 'BackupPerf', + 'features': [ 'unstable' ] } } } ## # @DriveBackup: @@ -1916,9 +1921,13 @@ # # Get the block graph. # +# Features: +# @unstable: This command is meant for debugging. +# # Since: 4.0 ## -{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph' } +{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph', + 'features': [ 'unstable' ] } ## # @drive-mirror: @@ -2257,6 +2266,9 @@ # # Get bitmap SHA256. # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: - BlockDirtyBitmapSha256 on success # - If @node is not a valid block device, DeviceNotFound # - If @name is not found or if hashing has failed, GenericError with an @@ -2265,7 +2277,8 @@ # Since: 2.10 ## { 'command': 'x-debug-block-dirty-bitmap-sha256', - 'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256' } + 'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256', + 'features': [ 'unstable' ] } ## # @blockdev-mirror: @@ -2495,27 +2508,57 @@ # # Properties for throttle-group objects. # -# The options starting with x- are aliases for the same key without x- in -# the @limits object. As indicated by the x- prefix, this is not a stable -# interface and may be removed or changed incompatibly in the future. Use -# @limits for a supported stable interface. -# # @limits: limits to apply for this throttle group # +# Features: +# @unstable: All members starting with x- are aliases for the same key +# without x- in the @limits object. This is not a stable +# interface and may be removed or changed incompatibly in +# the future. Use @limits for a supported stable +# interface. +# # Since: 2.11 ## { 'struct': 'ThrottleGroupProperties', 'data': { '*limits': 'ThrottleLimits', - '*x-iops-total' : 'int', '*x-iops-total-max' : 'int', - '*x-iops-total-max-length' : 'int', '*x-iops-read' : 'int', - '*x-iops-read-max' : 'int', '*x-iops-read-max-length' : 'int', - '*x-iops-write' : 'int', '*x-iops-write-max' : 'int', - '*x-iops-write-max-length' : 'int', '*x-bps-total' : 'int', - '*x-bps-total-max' : 'int', '*x-bps-total-max-length' : 'int', - '*x-bps-read' : 'int', '*x-bps-read-max' : 'int', - '*x-bps-read-max-length' : 'int', '*x-bps-write' : 'int', - '*x-bps-write-max' : 'int', '*x-bps-write-max-length' : 'int', - '*x-iops-size' : 'int' } } + '*x-iops-total': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-total-max': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-total-max-length': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-read': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-read-max': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-read-max-length': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-write': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-write-max': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-write-max-length': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-total': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-total-max': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-total-max-length': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-read': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-read-max': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-read-max-length': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-write': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-write-max': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-bps-write-max-length': { 'type': 'int', + 'features': [ 'unstable' ] }, + '*x-iops-size': { 'type': 'int', + 'features': [ 'unstable' ] } } } ## # @block-stream: @@ -2916,6 +2959,7 @@ # read-only when the last writer is detached. This # allows giving QEMU write permissions only on demand # when an operation actually needs write access. +# @unstable: Member x-check-cache-dropped is meant for debugging. # # Since: 2.9 ## @@ -2926,7 +2970,8 @@ '*aio': 'BlockdevAioOptions', '*drop-cache': {'type': 'bool', 'if': 'CONFIG_LINUX'}, - '*x-check-cache-dropped': 'bool' }, + '*x-check-cache-dropped': { 'type': 'bool', + 'features': [ 'unstable' ] } }, 'features': [ { 'name': 'dynamic-auto-read-only', 'if': 'CONFIG_POSIX' } ] } @@ -4041,13 +4086,16 @@ # future requests before a successful reconnect will # immediately fail. Default 0 (Since 4.2) # +# Features: +# @unstable: Member @x-dirty-bitmap is experimental. +# # Since: 2.9 ## { 'struct': 'BlockdevOptionsNbd', 'data': { 'server': 'SocketAddress', '*export': 'str', '*tls-creds': 'str', - '*x-dirty-bitmap': 'str', + '*x-dirty-bitmap': { 'type': 'str', 'features': [ 'unstable' ] }, '*reconnect-delay': 'uint32' } } ## @@ -4865,13 +4913,17 @@ # and replacement of an active keyslot # (possible loss of data if IO error happens) # +# Features: +# @unstable: This command is experimental. +# # Since: 5.1 ## { 'command': 'x-blockdev-amend', 'data': { 'job-id': 'str', 'node-name': 'str', 'options': 'BlockdevAmendOptions', - '*force': 'bool' } } + '*force': 'bool' }, + 'features': [ 'unstable' ] } ## # @BlockErrorAction: @@ -5242,16 +5294,18 @@ # # @node: the name of the node that will be added. # -# Note: this command is experimental, and its API is not stable. It -# does not support all kinds of operations, all kinds of children, nor -# all block drivers. +# Features: +# @unstable: This command is experimental, and its API is not stable. It +# does not support all kinds of operations, all kinds of +# children, nor all block drivers. # -# FIXME Removing children from a quorum node means introducing gaps in the -# child indices. This cannot be represented in the 'children' list of -# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename(). +# FIXME Removing children from a quorum node means introducing +# gaps in the child indices. This cannot be represented in the +# 'children' list of BlockdevOptionsQuorum, as returned by +# .bdrv_refresh_filename(). # -# Warning: The data in a new quorum child MUST be consistent with that of -# the rest of the array. +# Warning: The data in a new quorum child MUST be consistent +# with that of the rest of the array. # # Since: 2.7 # @@ -5280,7 +5334,8 @@ { 'command': 'x-blockdev-change', 'data' : { 'parent': 'str', '*child': 'str', - '*node': 'str' } } + '*node': 'str' }, + 'features': [ 'unstable' ] } ## # @x-blockdev-set-iothread: @@ -5297,8 +5352,9 @@ # @force: true if the node and its children should be moved when a BlockBackend # is already attached # -# Note: this command is experimental and intended for test cases that need -# control over IOThreads only. +# Features: +# @unstable: This command is experimental and intended for test cases that +# need control over IOThreads only. # # Since: 2.12 # @@ -5320,7 +5376,8 @@ { 'command': 'x-blockdev-set-iothread', 'data' : { 'node-name': 'str', 'iothread': 'StrOrNull', - '*force': 'bool' } } + '*force': 'bool' }, + 'features': [ 'unstable' ] } ## # @QuorumOpType: diff --git a/qapi/migration.json b/qapi/migration.json index 88f07baedd..9aa8bc5759 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -452,14 +452,20 @@ # procedure starts. The VM RAM is saved with running VM. # (since 6.0) # +# Features: +# @unstable: Members @x-colo and @x-ignore-shared are experimental. +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', - 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', + 'compress', 'events', 'postcopy-ram', + { 'name': 'x-colo', 'features': [ 'unstable' ] }, + 'release-ram', 'block', 'return-path', 'pause-before-switchover', 'multifd', 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', - 'x-ignore-shared', 'validate-uuid', 'background-snapshot'] } + { 'name': 'x-ignore-shared', 'features': [ 'unstable' ] }, + 'validate-uuid', 'background-snapshot'] } ## # @MigrationCapabilityStatus: @@ -743,6 +749,9 @@ # block device name if there is one, and to their node name # otherwise. (Since 5.2) # +# Features: +# @unstable: Member @x-checkpoint-delay is experimental. +# # Since: 2.4 ## { 'enum': 'MigrationParameter', @@ -753,7 +762,9 @@ 'cpu-throttle-initial', 'cpu-throttle-increment', 'cpu-throttle-tailslow', 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', - 'downtime-limit', 'x-checkpoint-delay', 'block-incremental', + 'downtime-limit', + { 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] }, + 'block-incremental', 'multifd-channels', 'xbzrle-cache-size', 'max-postcopy-bandwidth', 'max-cpu-throttle', 'multifd-compression', @@ -903,6 +914,9 @@ # block device name if there is one, and to their node name # otherwise. (Since 5.2) # +# Features: +# @unstable: Member @x-checkpoint-delay is experimental. +# # Since: 2.4 ## # TODO either fuse back into MigrationParameters, or make @@ -925,7 +939,8 @@ '*tls-authz': 'StrOrNull', '*max-bandwidth': 'size', '*downtime-limit': 'uint64', - '*x-checkpoint-delay': 'uint32', + '*x-checkpoint-delay': { 'type': 'uint32', + 'features': [ 'unstable' ] }, '*block-incremental': 'bool', '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', @@ -1099,6 +1114,9 @@ # block device name if there is one, and to their node name # otherwise. (Since 5.2) # +# Features: +# @unstable: Member @x-checkpoint-delay is experimental. +# # Since: 2.4 ## { 'struct': 'MigrationParameters', @@ -1119,7 +1137,8 @@ '*tls-authz': 'str', '*max-bandwidth': 'size', '*downtime-limit': 'uint64', - '*x-checkpoint-delay': 'uint32', + '*x-checkpoint-delay': { 'type': 'uint32', + 'features': [ 'unstable' ] }, '*block-incremental': 'bool', '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', @@ -1351,6 +1370,9 @@ # If sent to the Secondary, the Secondary side will run failover work, # then takes over server operation to become the service VM. # +# Features: +# @unstable: This command is experimental. +# # Since: 2.8 # # Example: @@ -1359,7 +1381,8 @@ # <- { "return": {} } # ## -{ 'command': 'x-colo-lost-heartbeat' } +{ 'command': 'x-colo-lost-heartbeat', + 'features': [ 'unstable' ] } ## # @migrate_cancel: diff --git a/qapi/misc.json b/qapi/misc.json index 5c2ca3b556..358548abe1 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -185,6 +185,9 @@ # available during the preconfig state (i.e. when the --preconfig command # line option was in use). # +# Features: +# @unstable: This command is experimental. +# # Since 3.0 # # Returns: nothing @@ -195,7 +198,8 @@ # <- { "return": {} } # ## -{ 'command': 'x-exit-preconfig', 'allow-preconfig': true } +{ 'command': 'x-exit-preconfig', 'allow-preconfig': true, + 'features': [ 'unstable' ] } ## # @human-monitor-command: diff --git a/qapi/qom.json b/qapi/qom.json index 7231ac3f34..ccd1167808 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -559,10 +559,8 @@ # for ramblock-id. Disable this for 4.0 # machine types or older to allow # migration with newer QEMU versions. -# This option is considered stable -# despite the x- prefix. (default: -# false generally, but true for machine -# types <= 4.0) +# (default: false generally, +# but true for machine types <= 4.0) # # Note: prealloc=true and reserve=false cannot be set at the same time. With # reserve=true, the behavior depends on the operating system: for example, @@ -785,6 +783,9 @@ ## # @ObjectType: # +# Features: +# @unstable: Member @x-remote-object is experimental. +# # Since: 6.0 ## { 'enum': 'ObjectType', @@ -836,7 +837,7 @@ 'tls-creds-psk', 'tls-creds-x509', 'tls-cipher-suites', - 'x-remote-object' + { 'name': 'x-remote-object', 'features': [ 'unstable' ] } ] } ## From 9bafe07bc8b00ce9ba5ea6f4c590239c579d83ee Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:14 +0200 Subject: [PATCH 0858/1334] qapi: Eliminate QCO_NO_OPTIONS for a slight simplification Signed-off-by: Markus Armbruster Reviewed-by: Juan Quintela Reviewed-by: John Snow Message-Id: <20211028102520.747396-4-armbru@redhat.com> --- include/qapi/qmp/dispatch.h | 1 - monitor/misc.c | 3 +-- scripts/qapi/commands.py | 5 +---- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 075203dc67..0ce88200b9 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -21,7 +21,6 @@ typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); typedef enum QmpCommandOptions { - QCO_NO_OPTIONS = 0x0, QCO_NO_SUCCESS_RESP = (1U << 0), QCO_ALLOW_OOB = (1U << 1), QCO_ALLOW_PRECONFIG = (1U << 2), diff --git a/monitor/misc.c b/monitor/misc.c index ffe7966870..3556b177f6 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -230,8 +230,7 @@ static void monitor_init_qmp_commands(void) qmp_init_marshal(&qmp_commands); - qmp_register_command(&qmp_commands, "device_add", qmp_device_add, - QCO_NO_OPTIONS); + qmp_register_command(&qmp_commands, "device_add", qmp_device_add, 0); QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 3654825968..c8a975528f 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -229,15 +229,12 @@ def gen_register_command(name: str, if coroutine: options += ['QCO_COROUTINE'] - if not options: - options = ['QCO_NO_OPTIONS'] - ret = mcgen(''' qmp_register_command(cmds, "%(name)s", qmp_marshal_%(c_name)s, %(opts)s); ''', name=name, c_name=c_name(name), - opts=" | ".join(options)) + opts=' | '.join(options) or 0) return ret From c67db1ed16ff5a7c1b186caa754e0c738aa945b8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:15 +0200 Subject: [PATCH 0859/1334] qapi: Tools for sets of special feature flags in generated code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New enum QapiSpecialFeature enumerates the special feature flags. New helper gen_special_features() returns code to represent a collection of special feature flags as a bitset. The next few commits will put them to use. Signed-off-by: Markus Armbruster Reviewed-by: John Snow Message-Id: <20211028102520.747396-5-armbru@redhat.com> Reviewed-by: Juan Quintela Reviewed-by: Philippe Mathieu-Daudé --- include/qapi/util.h | 4 ++++ scripts/qapi/gen.py | 8 ++++++++ scripts/qapi/schema.py | 3 +++ 3 files changed, 15 insertions(+) diff --git a/include/qapi/util.h b/include/qapi/util.h index 257c600f99..7a8d5c7d72 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -11,6 +11,10 @@ #ifndef QAPI_UTIL_H #define QAPI_UTIL_H +typedef enum { + QAPI_DEPRECATED, +} QapiSpecialFeature; + /* QEnumLookup flags */ #define QAPI_ENUM_DEPRECATED 1 diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 2ec1e7b3b6..995a97d2b8 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -18,6 +18,7 @@ from typing import ( Dict, Iterator, Optional, + Sequence, Tuple, ) @@ -29,6 +30,7 @@ from .common import ( mcgen, ) from .schema import ( + QAPISchemaFeature, QAPISchemaIfCond, QAPISchemaModule, QAPISchemaObjectType, @@ -37,6 +39,12 @@ from .schema import ( from .source import QAPISourceInfo +def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: + special_features = [f"1u << QAPI_{feat.name.upper()}" + for feat in features if feat.is_special()] + return ' | '.join(special_features) or '0' + + class QAPIGen: def __init__(self, fname: str): self.fname = fname diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 6d5f46509a..55f82d7389 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -725,6 +725,9 @@ class QAPISchemaEnumMember(QAPISchemaMember): class QAPISchemaFeature(QAPISchemaMember): role = 'feature' + def is_special(self): + return self.name in ('deprecated') + class QAPISchemaObjectTypeMember(QAPISchemaMember): def __init__(self, name, info, typ, optional, ifcond=None, features=None): From a130728554d0cc19ef0ed4c1c824305c1682e64b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:16 +0200 Subject: [PATCH 0860/1334] qapi: Generalize struct member policy checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated visitor functions call visit_deprecated_accept() and visit_deprecated() when visiting a struct member with special feature flag 'deprecated'. This makes the feature flag visible to the actual visitors. I want to make feature flag 'unstable' visible there as well, so I can add policy for it. To let me make it visible, replace these functions by visit_policy_reject() and visit_policy_skip(), which take the member's special features as an argument. Note that the new functions have the opposite sense, i.e. the return value flips. Signed-off-by: Markus Armbruster Message-Id: <20211028102520.747396-6-armbru@redhat.com> Reviewed-by: Juan Quintela Reviewed-by: Philippe Mathieu-Daudé [Unbreak forward visitor] --- include/qapi/visitor-impl.h | 6 ++++-- include/qapi/visitor.h | 17 +++++++++++++---- qapi/qapi-forward-visitor.c | 20 +++++++++++--------- qapi/qapi-visit-core.c | 22 ++++++++++++---------- qapi/qobject-input-visitor.c | 15 ++++++++++----- qapi/qobject-output-visitor.c | 9 ++++++--- qapi/trace-events | 4 ++-- scripts/qapi/visit.py | 14 +++++++------- 8 files changed, 65 insertions(+), 42 deletions(-) diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index 72b6537bef..2badec5ba4 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -114,10 +114,12 @@ struct Visitor void (*optional)(Visitor *v, const char *name, bool *present); /* Optional */ - bool (*deprecated_accept)(Visitor *v, const char *name, Error **errp); + bool (*policy_reject)(Visitor *v, const char *name, + unsigned special_features, Error **errp); /* Optional */ - bool (*deprecated)(Visitor *v, const char *name); + bool (*policy_skip)(Visitor *v, const char *name, + unsigned special_features); /* Must be set */ VisitorType type; diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index dcb96018a9..d53a84c9ba 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -461,22 +461,31 @@ void visit_end_alternate(Visitor *v, void **obj); bool visit_optional(Visitor *v, const char *name, bool *present); /* - * Should we reject deprecated member @name? + * Should we reject member @name due to policy? + * + * @special_features is the member's special features encoded as a + * bitset of QapiSpecialFeature. * * @name must not be NULL. This function is only useful between * visit_start_struct() and visit_end_struct(), since only objects * have deprecated members. */ -bool visit_deprecated_accept(Visitor *v, const char *name, Error **errp); +bool visit_policy_reject(Visitor *v, const char *name, + unsigned special_features, Error **errp); /* - * Should we visit deprecated member @name? + * + * Should we skip member @name due to policy? + * + * @special_features is the member's special features encoded as a + * bitset of QapiSpecialFeature. * * @name must not be NULL. This function is only useful between * visit_start_struct() and visit_end_struct(), since only objects * have deprecated members. */ -bool visit_deprecated(Visitor *v, const char *name); +bool visit_policy_skip(Visitor *v, const char *name, + unsigned special_features); /* * Set policy for handling deprecated management interfaces. diff --git a/qapi/qapi-forward-visitor.c b/qapi/qapi-forward-visitor.c index a4b111e22a..4ea7e0bec3 100644 --- a/qapi/qapi-forward-visitor.c +++ b/qapi/qapi-forward-visitor.c @@ -246,25 +246,27 @@ static void forward_field_optional(Visitor *v, const char *name, bool *present) visit_optional(ffv->target, name, present); } -static bool forward_field_deprecated_accept(Visitor *v, const char *name, - Error **errp) +static bool forward_field_policy_reject(Visitor *v, const char *name, + unsigned special_features, + Error **errp) { ForwardFieldVisitor *ffv = to_ffv(v); if (!forward_field_translate_name(ffv, &name, errp)) { - return false; + return true; } - return visit_deprecated_accept(ffv->target, name, errp); + return visit_policy_reject(ffv->target, name, special_features, errp); } -static bool forward_field_deprecated(Visitor *v, const char *name) +static bool forward_field_policy_skip(Visitor *v, const char *name, + unsigned special_features) { ForwardFieldVisitor *ffv = to_ffv(v); if (!forward_field_translate_name(ffv, &name, NULL)) { - return false; + return true; } - return visit_deprecated(ffv->target, name); + return visit_policy_skip(ffv->target, name, special_features); } static void forward_field_complete(Visitor *v, void *opaque) @@ -313,8 +315,8 @@ Visitor *visitor_forward_field(Visitor *target, const char *from, const char *to v->visitor.type_any = forward_field_type_any; v->visitor.type_null = forward_field_type_null; v->visitor.optional = forward_field_optional; - v->visitor.deprecated_accept = forward_field_deprecated_accept; - v->visitor.deprecated = forward_field_deprecated; + v->visitor.policy_reject = forward_field_policy_reject; + v->visitor.policy_skip = forward_field_policy_skip; v->visitor.complete = forward_field_complete; v->visitor.free = forward_field_free; diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 617ef3fa46..f95503cbec 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -139,22 +139,24 @@ bool visit_optional(Visitor *v, const char *name, bool *present) return *present; } -bool visit_deprecated_accept(Visitor *v, const char *name, Error **errp) +bool visit_policy_reject(Visitor *v, const char *name, + unsigned special_features, Error **errp) { - trace_visit_deprecated_accept(v, name); - if (v->deprecated_accept) { - return v->deprecated_accept(v, name, errp); + trace_visit_policy_reject(v, name); + if (v->policy_reject) { + return v->policy_reject(v, name, special_features, errp); } - return true; + return false; } -bool visit_deprecated(Visitor *v, const char *name) +bool visit_policy_skip(Visitor *v, const char *name, + unsigned special_features) { - trace_visit_deprecated(v, name); - if (v->deprecated) { - return v->deprecated(v, name); + trace_visit_policy_skip(v, name); + if (v->policy_skip) { + return v->policy_skip(v, name, special_features); } - return true; + return false; } void visit_set_policy(Visitor *v, CompatPolicy *policy) diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 71b24a4429..c120dbdd92 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -662,16 +662,21 @@ static void qobject_input_optional(Visitor *v, const char *name, bool *present) *present = true; } -static bool qobject_input_deprecated_accept(Visitor *v, const char *name, - Error **errp) +static bool qobject_input_policy_reject(Visitor *v, const char *name, + unsigned special_features, + Error **errp) { + if (!(special_features & 1u << QAPI_DEPRECATED)) { + return false; + } + switch (v->compat_policy.deprecated_input) { case COMPAT_POLICY_INPUT_ACCEPT: - return true; + return false; case COMPAT_POLICY_INPUT_REJECT: error_setg(errp, "Deprecated parameter '%s' disabled by policy", name); - return false; + return true; case COMPAT_POLICY_INPUT_CRASH: default: abort(); @@ -712,7 +717,7 @@ static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) v->visitor.end_list = qobject_input_end_list; v->visitor.start_alternate = qobject_input_start_alternate; v->visitor.optional = qobject_input_optional; - v->visitor.deprecated_accept = qobject_input_deprecated_accept; + v->visitor.policy_reject = qobject_input_policy_reject; v->visitor.free = qobject_input_free; v->root = qobject_ref(obj); diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index 9b7f510036..b155bf4149 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "qapi/compat-policy.h" #include "qapi/qobject-output-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" @@ -208,9 +209,11 @@ static bool qobject_output_type_null(Visitor *v, const char *name, return true; } -static bool qobject_output_deprecated(Visitor *v, const char *name) +static bool qobject_output_policy_skip(Visitor *v, const char *name, + unsigned special_features) { - return v->compat_policy.deprecated_output != COMPAT_POLICY_OUTPUT_HIDE; + return !(special_features & 1u << QAPI_DEPRECATED) + || v->compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE; } /* Finish building, and return the root object. @@ -262,7 +265,7 @@ Visitor *qobject_output_visitor_new(QObject **result) v->visitor.type_number = qobject_output_type_number; v->visitor.type_any = qobject_output_type_any; v->visitor.type_null = qobject_output_type_null; - v->visitor.deprecated = qobject_output_deprecated; + v->visitor.policy_skip = qobject_output_policy_skip; v->visitor.complete = qobject_output_complete; v->visitor.free = qobject_output_free; diff --git a/qapi/trace-events b/qapi/trace-events index cccafc07e5..ab108c4f0e 100644 --- a/qapi/trace-events +++ b/qapi/trace-events @@ -17,8 +17,8 @@ visit_start_alternate(void *v, const char *name, void *obj, size_t size) "v=%p n visit_end_alternate(void *v, void *obj) "v=%p obj=%p" visit_optional(void *v, const char *name, bool *present) "v=%p name=%s present=%p" -visit_deprecated_accept(void *v, const char *name) "v=%p name=%s" -visit_deprecated(void *v, const char *name) "v=%p name=%s" +visit_policy_reject(void *v, const char *name) "v=%p name=%s" +visit_policy_skip(void *v, const char *name) "v=%p name=%s" visit_type_enum(void *v, const char *name, int *obj) "v=%p name=%s obj=%p" visit_type_int(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p" diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 9d9196a143..e13bbe4292 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -21,7 +21,7 @@ from .common import ( indent, mcgen, ) -from .gen import QAPISchemaModularCVisitor, ifcontext +from .gen import QAPISchemaModularCVisitor, gen_special_features, ifcontext from .schema import ( QAPISchema, QAPISchemaEnumMember, @@ -76,7 +76,6 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) c_type=base.c_name()) for memb in members: - deprecated = 'deprecated' in [f.name for f in memb.features] ret += memb.ifcond.gen_if() if memb.optional: ret += mcgen(''' @@ -84,14 +83,15 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''', name=memb.name, c_name=c_name(memb.name)) indent.increase() - if deprecated: + special_features = gen_special_features(memb.features) + if special_features != '0': ret += mcgen(''' - if (!visit_deprecated_accept(v, "%(name)s", errp)) { + if (visit_policy_reject(v, "%(name)s", %(special_features)s, errp)) { return false; } - if (visit_deprecated(v, "%(name)s")) { + if (!visit_policy_skip(v, "%(name)s", %(special_features)s)) { ''', - name=memb.name) + name=memb.name, special_features=special_features) indent.increase() ret += mcgen(''' if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) { @@ -100,7 +100,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''', c_type=memb.type.c_name(), name=memb.name, c_name=c_name(memb.name)) - if deprecated: + if special_features != '0': indent.decrease() ret += mcgen(''' } From 6604e4757a1fc5832f87b5f9244efccabb49be8e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:17 +0200 Subject: [PATCH 0861/1334] qapi: Generalize command policy checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code to check command policy can see special feature flag 'deprecated' as command flag QCO_DEPRECATED. I want to make feature flag 'unstable' visible there as well, so I can add policy for it. To let me make it visible, add member @special_features (a bitset of QapiSpecialFeature) to QmpCommand, and adjust the generator to pass it through qmp_register_command(). Then replace "QCO_DEPRECATED in @flags" by QAPI_DEPRECATED in @special_features", and drop QCO_DEPRECATED. Signed-off-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Acked-by: John Snow Message-Id: <20211028102520.747396-7-armbru@redhat.com> Reviewed-by: Juan Quintela Reviewed-by: Eric Blake --- include/qapi/qmp/dispatch.h | 5 +++-- monitor/misc.c | 6 ++++-- qapi/qmp-dispatch.c | 2 +- qapi/qmp-registry.c | 4 +++- scripts/qapi/commands.py | 9 ++++----- storage-daemon/qemu-storage-daemon.c | 3 ++- 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 0ce88200b9..1e4240fd0d 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -25,7 +25,6 @@ typedef enum QmpCommandOptions QCO_ALLOW_OOB = (1U << 1), QCO_ALLOW_PRECONFIG = (1U << 2), QCO_COROUTINE = (1U << 3), - QCO_DEPRECATED = (1U << 4), } QmpCommandOptions; typedef struct QmpCommand @@ -34,6 +33,7 @@ typedef struct QmpCommand /* Runs in coroutine context if QCO_COROUTINE is set */ QmpCommandFunc *fn; QmpCommandOptions options; + unsigned special_features; QTAILQ_ENTRY(QmpCommand) node; bool enabled; const char *disable_reason; @@ -42,7 +42,8 @@ typedef struct QmpCommand typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; void qmp_register_command(QmpCommandList *cmds, const char *name, - QmpCommandFunc *fn, QmpCommandOptions options); + QmpCommandFunc *fn, QmpCommandOptions options, + unsigned special_features); const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name); void qmp_disable_command(QmpCommandList *cmds, const char *name, diff --git a/monitor/misc.c b/monitor/misc.c index 3556b177f6..c2d227a07c 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -230,11 +230,13 @@ static void monitor_init_qmp_commands(void) qmp_init_marshal(&qmp_commands); - qmp_register_command(&qmp_commands, "device_add", qmp_device_add, 0); + qmp_register_command(&qmp_commands, "device_add", + qmp_device_add, 0, 0); QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", - qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); + qmp_marshal_qmp_capabilities, + QCO_ALLOW_PRECONFIG, 0); } /* Set the current CPU defined by the user. Callers must hold BQL. */ diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 7e943a0af5..8cca18c891 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -176,7 +176,7 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, "The command %s has not been found", command); goto out; } - if (cmd->options & QCO_DEPRECATED) { + if (cmd->special_features & 1u << QAPI_DEPRECATED) { switch (compat_policy.deprecated_input) { case COMPAT_POLICY_INPUT_ACCEPT: break; diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index f78c064aae..485bc5e6fc 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -16,7 +16,8 @@ #include "qapi/qmp/dispatch.h" void qmp_register_command(QmpCommandList *cmds, const char *name, - QmpCommandFunc *fn, QmpCommandOptions options) + QmpCommandFunc *fn, QmpCommandOptions options, + unsigned special_features) { QmpCommand *cmd = g_malloc0(sizeof(*cmd)); @@ -27,6 +28,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, cmd->fn = fn; cmd->enabled = true; cmd->options = options; + cmd->special_features = special_features; QTAILQ_INSERT_TAIL(cmds, cmd, node); } diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index c8a975528f..21001bbd6b 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -26,6 +26,7 @@ from .gen import ( QAPISchemaModularCVisitor, build_params, ifcontext, + gen_special_features, ) from .schema import ( QAPISchema, @@ -217,9 +218,6 @@ def gen_register_command(name: str, coroutine: bool) -> str: options = [] - if 'deprecated' in [f.name for f in features]: - options += ['QCO_DEPRECATED'] - if not success_response: options += ['QCO_NO_SUCCESS_RESP'] if allow_oob: @@ -231,10 +229,11 @@ def gen_register_command(name: str, ret = mcgen(''' qmp_register_command(cmds, "%(name)s", - qmp_marshal_%(c_name)s, %(opts)s); + qmp_marshal_%(c_name)s, %(opts)s, %(feats)s); ''', name=name, c_name=c_name(name), - opts=' | '.join(options) or 0) + opts=' | '.join(options) or 0, + feats=gen_special_features(features)) return ret diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index 10a1a33761..52cf17e8ac 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -146,7 +146,8 @@ static void init_qmp_commands(void) QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", - qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); + qmp_marshal_qmp_capabilities, + QCO_ALLOW_PRECONFIG, 0); } static int getopt_set_loc(int argc, char **argv, const char *optstring, From c8688760437aaf4bfa9012ff5aef8ab1c92a38e1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:18 +0200 Subject: [PATCH 0862/1334] qapi: Generalize enum member policy checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code to check enumeration value policy can see special feature flag 'deprecated' in QEnumLookup member flags[value]. I want to make feature flag 'unstable' visible there as well, so I can add policy for it. Instead of extending flags[], replace it by @special_features (a bitset of QapiSpecialFeature), because that's how special features get passed around elsewhere. Signed-off-by: Markus Armbruster Acked-by: John Snow Message-Id: <20211028102520.747396-8-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- include/qapi/util.h | 5 +---- qapi/qapi-visit-core.c | 3 ++- scripts/qapi/types.py | 22 ++++++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/qapi/util.h b/include/qapi/util.h index 7a8d5c7d72..0cc98db9f9 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -15,12 +15,9 @@ typedef enum { QAPI_DEPRECATED, } QapiSpecialFeature; -/* QEnumLookup flags */ -#define QAPI_ENUM_DEPRECATED 1 - typedef struct QEnumLookup { const char *const *array; - const unsigned char *const flags; + const unsigned char *const special_features; const int size; } QEnumLookup; diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index f95503cbec..34c59286b2 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -408,7 +408,8 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, return false; } - if (lookup->flags && (lookup->flags[value] & QAPI_ENUM_DEPRECATED)) { + if (lookup->special_features + && (lookup->special_features[value] & QAPI_DEPRECATED)) { switch (v->compat_policy.deprecated_input) { case COMPAT_POLICY_INPUT_ACCEPT: break; diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index ab2441adc9..3013329c24 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -16,7 +16,7 @@ This work is licensed under the terms of the GNU GPL, version 2. from typing import List, Optional from .common import c_enum_const, c_name, mcgen -from .gen import QAPISchemaModularCVisitor, ifcontext +from .gen import QAPISchemaModularCVisitor, gen_special_features, ifcontext from .schema import ( QAPISchema, QAPISchemaEnumMember, @@ -39,7 +39,7 @@ def gen_enum_lookup(name: str, members: List[QAPISchemaEnumMember], prefix: Optional[str] = None) -> str: max_index = c_enum_const(name, '_MAX', prefix) - flags = '' + feats = '' ret = mcgen(''' const QEnumLookup %(c_name)s_lookup = { @@ -54,19 +54,21 @@ const QEnumLookup %(c_name)s_lookup = { ''', index=index, name=memb.name) ret += memb.ifcond.gen_endif() - if 'deprecated' in (f.name for f in memb.features): - flags += mcgen(''' - [%(index)s] = QAPI_ENUM_DEPRECATED, -''', - index=index) - if flags: + special_features = gen_special_features(memb.features) + if special_features != '0': + feats += mcgen(''' + [%(index)s] = %(special_features)s, +''', + index=index, special_features=special_features) + + if feats: ret += mcgen(''' }, - .flags = (const unsigned char[%(max_index)s]) { + .special_features = (const unsigned char[%(max_index)s]) { ''', max_index=max_index) - ret += flags + ret += feats ret += mcgen(''' }, From 07db29f20a9a845c8408df11936889e5411ff44f Mon Sep 17 00:00:00 2001 From: Chenyi Qiang Date: Fri, 27 Aug 2021 14:48:18 +0800 Subject: [PATCH 0863/1334] target/i386: Remove core-capability in Snowridge CPU model Because core-capability releated features are model-specific and KVM won't support it, remove the core-capability in CPU model to avoid the warning message. Signed-off-by: Chenyi Qiang Message-Id: <20210827064818.4698-3-chenyi.qiang@intel.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index fc3ed80ef1..598d451dcf 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3749,9 +3749,10 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, { .version = 4, - .note = "no split lock detect", + .note = "no split lock detect, no core-capability", .props = (PropValue[]) { { "split-lock-detect", "off" }, + { "core-capability", "off" }, { /* end of list */ }, }, }, From 7ce5fc63c75d0ac756fd0b4d0472774de17f8fec Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:19 +0200 Subject: [PATCH 0864/1334] qapi: Factor out compat_policy_input_ok() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code to check policy for handling deprecated input is triplicated. Factor it out into compat_policy_input_ok() before I mess with it in the next commit. Signed-off-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211028102520.747396-9-armbru@redhat.com> [Policy code moved from qmp-dispatch.c to qapi-util.c to make visitors link without qmp-dispatch.o] --- include/qapi/compat-policy.h | 7 +++++++ qapi/qapi-util.c | 37 ++++++++++++++++++++++++++++++++++++ qapi/qapi-visit-core.c | 18 ++++++------------ qapi/qmp-dispatch.c | 19 ++++-------------- qapi/qobject-input-visitor.c | 19 ++++-------------- 5 files changed, 58 insertions(+), 42 deletions(-) diff --git a/include/qapi/compat-policy.h b/include/qapi/compat-policy.h index 1083f95122..8b7b25c0b5 100644 --- a/include/qapi/compat-policy.h +++ b/include/qapi/compat-policy.h @@ -13,10 +13,17 @@ #ifndef QAPI_COMPAT_POLICY_H #define QAPI_COMPAT_POLICY_H +#include "qapi/error.h" #include "qapi/qapi-types-compat.h" extern CompatPolicy compat_policy; +bool compat_policy_input_ok(unsigned special_features, + const CompatPolicy *policy, + ErrorClass error_class, + const char *kind, const char *name, + Error **errp); + /* * Create a QObject input visitor for @obj for use with QMP * diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c index 3c24bb3d45..53b493cb7e 100644 --- a/qapi/qapi-util.c +++ b/qapi/qapi-util.c @@ -11,10 +11,47 @@ */ #include "qemu/osdep.h" +#include "qapi/compat-policy.h" #include "qapi/error.h" #include "qemu/ctype.h" #include "qapi/qmp/qerror.h" +CompatPolicy compat_policy; + +static bool compat_policy_input_ok1(const char *adjective, + CompatPolicyInput policy, + ErrorClass error_class, + const char *kind, const char *name, + Error **errp) +{ + switch (policy) { + case COMPAT_POLICY_INPUT_ACCEPT: + return true; + case COMPAT_POLICY_INPUT_REJECT: + error_set(errp, error_class, "%s %s %s disabled by policy", + adjective, kind, name); + return false; + case COMPAT_POLICY_INPUT_CRASH: + default: + abort(); + } +} + +bool compat_policy_input_ok(unsigned special_features, + const CompatPolicy *policy, + ErrorClass error_class, + const char *kind, const char *name, + Error **errp) +{ + if ((special_features & 1u << QAPI_DEPRECATED) + && !compat_policy_input_ok1("Deprecated", + policy->deprecated_input, + error_class, kind, name, errp)) { + return false; + } + return true; +} + const char *qapi_enum_lookup(const QEnumLookup *lookup, int val) { assert(val >= 0 && val < lookup->size); diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 34c59286b2..6c13510a2b 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "qapi/compat-policy.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qapi/visitor.h" @@ -409,18 +410,11 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, } if (lookup->special_features - && (lookup->special_features[value] & QAPI_DEPRECATED)) { - switch (v->compat_policy.deprecated_input) { - case COMPAT_POLICY_INPUT_ACCEPT: - break; - case COMPAT_POLICY_INPUT_REJECT: - error_setg(errp, "Deprecated value '%s' disabled by policy", - enum_str); - return false; - case COMPAT_POLICY_INPUT_CRASH: - default: - abort(); - } + && !compat_policy_input_ok(lookup->special_features[value], + &v->compat_policy, + ERROR_CLASS_GENERIC_ERROR, + "value", enum_str, errp)) { + return false; } *obj = value; diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 8cca18c891..d378bccac7 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -26,8 +26,6 @@ #include "qemu/coroutine.h" #include "qemu/main-loop.h" -CompatPolicy compat_policy; - Visitor *qobject_input_visitor_new_qmp(QObject *obj) { Visitor *v = qobject_input_visitor_new(obj); @@ -176,19 +174,10 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, "The command %s has not been found", command); goto out; } - if (cmd->special_features & 1u << QAPI_DEPRECATED) { - switch (compat_policy.deprecated_input) { - case COMPAT_POLICY_INPUT_ACCEPT: - break; - case COMPAT_POLICY_INPUT_REJECT: - error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, - "Deprecated command %s disabled by policy", - command); - goto out; - case COMPAT_POLICY_INPUT_CRASH: - default: - abort(); - } + if (!compat_policy_input_ok(cmd->special_features, &compat_policy, + ERROR_CLASS_COMMAND_NOT_FOUND, + "command", command, &err)) { + goto out; } if (!cmd->enabled) { error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index c120dbdd92..f0b4c7ca9d 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include +#include "qapi/compat-policy.h" #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" #include "qapi/visitor-impl.h" @@ -666,21 +667,9 @@ static bool qobject_input_policy_reject(Visitor *v, const char *name, unsigned special_features, Error **errp) { - if (!(special_features & 1u << QAPI_DEPRECATED)) { - return false; - } - - switch (v->compat_policy.deprecated_input) { - case COMPAT_POLICY_INPUT_ACCEPT: - return false; - case COMPAT_POLICY_INPUT_REJECT: - error_setg(errp, "Deprecated parameter '%s' disabled by policy", - name); - return true; - case COMPAT_POLICY_INPUT_CRASH: - default: - abort(); - } + return !compat_policy_input_ok(special_features, &v->compat_policy, + ERROR_CLASS_GENERIC_ERROR, + "parameter", name, errp); } static void qobject_input_free(Visitor *v) From 57df0dff1a1f4c846aa74a082bfd595a8a990015 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 28 Oct 2021 12:25:20 +0200 Subject: [PATCH 0865/1334] qapi: Extend -compat to set policy for unstable interfaces New option parameters unstable-input and unstable-output set policy for unstable interfaces just like deprecated-input and deprecated-output set policy for deprecated interfaces (see commit 6dd75472d5 "qemu-options: New -compat to set policy for deprecated interfaces"). This is intended for testing users of the management interfaces. It is experimental. For now, this covers only syntactic aspects of QMP, i.e. stuff tagged with feature 'unstable'. We may want to extend it to cover semantic aspects, or the command line. Note that there is no good way for management application to detect presence of these new option parameters: they are not visible output of query-qmp-schema or query-command-line-options. Tolerable, because it's meant for testing. If running with -compat fails, skip the test. Signed-off-by: Markus Armbruster Acked-by: John Snow Message-Id: <20211028102520.747396-10-armbru@redhat.com> Reviewed-by: Eric Blake [Doc comments fixed up] --- include/qapi/util.h | 1 + qapi/compat.json | 8 +++++++- qapi/qapi-util.c | 6 ++++++ qapi/qobject-output-visitor.c | 8 ++++++-- qemu-options.hx | 20 +++++++++++++++++++- scripts/qapi/events.py | 10 ++++++---- scripts/qapi/schema.py | 10 ++++++---- 7 files changed, 51 insertions(+), 12 deletions(-) diff --git a/include/qapi/util.h b/include/qapi/util.h index 0cc98db9f9..81a2b13a33 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -13,6 +13,7 @@ typedef enum { QAPI_DEPRECATED, + QAPI_UNSTABLE, } QapiSpecialFeature; typedef struct QEnumLookup { diff --git a/qapi/compat.json b/qapi/compat.json index 74a8493d3d..dd7261ae2a 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -47,9 +47,15 @@ # # @deprecated-input: how to handle deprecated input (default 'accept') # @deprecated-output: how to handle deprecated output (default 'accept') +# @unstable-input: how to handle unstable input (default 'accept') +# (since 6.2) +# @unstable-output: how to handle unstable output (default 'accept') +# (since 6.2) # # Since: 6.0 ## { 'struct': 'CompatPolicy', 'data': { '*deprecated-input': 'CompatPolicyInput', - '*deprecated-output': 'CompatPolicyOutput' } } + '*deprecated-output': 'CompatPolicyOutput', + '*unstable-input': 'CompatPolicyInput', + '*unstable-output': 'CompatPolicyOutput' } } diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c index 53b493cb7e..fda7044539 100644 --- a/qapi/qapi-util.c +++ b/qapi/qapi-util.c @@ -49,6 +49,12 @@ bool compat_policy_input_ok(unsigned special_features, error_class, kind, name, errp)) { return false; } + if ((special_features & (1u << QAPI_UNSTABLE)) + && !compat_policy_input_ok1("Unstable", + policy->unstable_input, + error_class, kind, name, errp)) { + return false; + } return true; } diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index b155bf4149..74770edd73 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -212,8 +212,12 @@ static bool qobject_output_type_null(Visitor *v, const char *name, static bool qobject_output_policy_skip(Visitor *v, const char *name, unsigned special_features) { - return !(special_features & 1u << QAPI_DEPRECATED) - || v->compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE; + CompatPolicy *pol = &v->compat_policy; + + return ((special_features & 1u << QAPI_DEPRECATED) + && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) + || ((special_features & 1u << QAPI_UNSTABLE) + && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE); } /* Finish building, and return the root object. diff --git a/qemu-options.hx b/qemu-options.hx index 5f375bbfa6..f051536b63 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3641,7 +3641,9 @@ DEFHEADING(Debug/Expert options:) DEF("compat", HAS_ARG, QEMU_OPTION_compat, "-compat [deprecated-input=accept|reject|crash][,deprecated-output=accept|hide]\n" - " Policy for handling deprecated management interfaces\n", + " Policy for handling deprecated management interfaces\n" + "-compat [unstable-input=accept|reject|crash][,unstable-output=accept|hide]\n" + " Policy for handling unstable management interfaces\n", QEMU_ARCH_ALL) SRST ``-compat [deprecated-input=@var{input-policy}][,deprecated-output=@var{output-policy}]`` @@ -3659,6 +3661,22 @@ SRST Suppress deprecated command results and events Limitation: covers only syntactic aspects of QMP. + +``-compat [unstable-input=@var{input-policy}][,unstable-output=@var{output-policy}]`` + Set policy for handling unstable management interfaces (experimental): + + ``unstable-input=accept`` (default) + Accept unstable commands and arguments + ``unstable-input=reject`` + Reject unstable commands and arguments + ``unstable-input=crash`` + Crash on unstable commands and arguments + ``unstable-output=accept`` (default) + Emit unstable command results and events + ``unstable-output=hide`` + Suppress unstable command results and events + + Limitation: covers only syntactic aspects of QMP. ERST DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg, diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 82475e84ec..27b44c49f5 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -109,13 +109,15 @@ def gen_event_send(name: str, if not boxed: ret += gen_param_var(arg_type) - if 'deprecated' in [f.name for f in features]: - ret += mcgen(''' + for f in features: + if f.is_special(): + ret += mcgen(''' - if (compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) { + if (compat_policy.%(feat)s_output == COMPAT_POLICY_OUTPUT_HIDE) { return; } -''') +''', + feat=f.name) ret += mcgen(''' diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 55f82d7389..b7b3fc0ce4 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -254,9 +254,11 @@ class QAPISchemaType(QAPISchemaEntity): def check(self, schema): QAPISchemaEntity.check(self, schema) - if 'deprecated' in [f.name for f in self.features]: - raise QAPISemError( - self.info, "feature 'deprecated' is not supported for types") + for feat in self.features: + if feat.is_special(): + raise QAPISemError( + self.info, + f"feature '{feat.name}' is not supported for types") def describe(self): assert self.meta @@ -726,7 +728,7 @@ class QAPISchemaFeature(QAPISchemaMember): role = 'feature' def is_special(self): - return self.name in ('deprecated') + return self.name in ('deprecated', 'unstable') class QAPISchemaObjectTypeMember(QAPISchemaMember): From b3793b8a9183d6019f557263bbddd647e1918394 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0866/1334] hw/sh4: Coding style: Remove tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Yoshinori Sato Message-Id: <2d9b2c470ec022cc85a25b3e5de337b5e794f7f6.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 220 +++--- hw/sh4/r2d.c | 34 +- hw/sh4/sh7750.c | 509 +++++++------ hw/sh4/sh7750_regnames.c | 144 ++-- hw/sh4/sh7750_regs.h | 1522 +++++++++++++++++++------------------- include/hw/sh4/sh.h | 20 +- 6 files changed, 1224 insertions(+), 1225 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 72a55e32dd..a269b8fbd4 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -20,7 +20,7 @@ #define INTC_A7(x) ((x) & 0x1fffffff) void sh_intc_toggle_source(struct intc_source *source, - int enable_adj, int assert_adj) + int enable_adj, int assert_adj) { int enable_changed = 0; int pending_changed = 0; @@ -54,22 +54,22 @@ void sh_intc_toggle_source(struct intc_source *source, if (source->parent->pending == 0) { cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD); } - } + } } if (enable_changed || assert_adj || pending_changed) { #ifdef DEBUG_INTC_SOURCES printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n", - source->parent->pending, - source->asserted, - source->enable_count, - source->enable_max, - source->vect, - source->asserted ? "asserted " : - assert_adj ? "deasserted" : "", - enable_changed == 1 ? "enabled " : - enable_changed == -1 ? "disabled " : "", - source->pending ? "pending" : ""); + source->parent->pending, + source->asserted, + source->enable_count, + source->enable_max, + source->vect, + source->asserted ? "asserted " : + assert_adj ? "deasserted" : "", + enable_changed == 1 ? "enabled " : + enable_changed == -1 ? "disabled " : "", + source->pending ? "pending" : ""); #endif } } @@ -99,13 +99,13 @@ int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) for (i = 0; i < desc->nr_sources; i++) { struct intc_source *source = desc->sources + i; - if (source->pending) { + if (source->pending) { #ifdef DEBUG_INTC_SOURCES printf("sh_intc: (%d) returning interrupt source 0x%x\n", - desc->pending, source->vect); + desc->pending, source->vect); #endif return source->vect; - } + } } abort(); @@ -119,16 +119,16 @@ int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) #define INTC_MODE_IS_PRIO 8 static unsigned int sh_intc_mode(unsigned long address, - unsigned long set_reg, unsigned long clr_reg) + unsigned long set_reg, unsigned long clr_reg) { if ((address != INTC_A7(set_reg)) && - (address != INTC_A7(clr_reg))) + (address != INTC_A7(clr_reg))) return INTC_MODE_NONE; if (set_reg && clr_reg) { if (address == INTC_A7(set_reg)) return INTC_MODE_DUAL_SET; - else + else return INTC_MODE_DUAL_CLR; } @@ -139,12 +139,12 @@ static unsigned int sh_intc_mode(unsigned long address, } static void sh_intc_locate(struct intc_desc *desc, - unsigned long address, - unsigned long **datap, - intc_enum **enums, - unsigned int *first, - unsigned int *width, - unsigned int *modep) + unsigned long address, + unsigned long **datap, + intc_enum **enums, + unsigned int *first, + unsigned int *width, + unsigned int *modep) { unsigned int i, mode; @@ -152,54 +152,54 @@ static void sh_intc_locate(struct intc_desc *desc, if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; + struct intc_mask_reg *mr = desc->mask_regs + i; - mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); - if (mode == INTC_MODE_NONE) + mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); + if (mode == INTC_MODE_NONE) continue; - *modep = mode; - *datap = &mr->value; - *enums = mr->enum_ids; - *first = mr->reg_width - 1; - *width = 1; - return; - } + *modep = mode; + *datap = &mr->value; + *enums = mr->enum_ids; + *first = mr->reg_width - 1; + *width = 1; + return; + } } if (desc->prio_regs) { for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; + struct intc_prio_reg *pr = desc->prio_regs + i; - mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); - if (mode == INTC_MODE_NONE) + mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); + if (mode == INTC_MODE_NONE) continue; - *modep = mode | INTC_MODE_IS_PRIO; - *datap = &pr->value; - *enums = pr->enum_ids; - *first = (pr->reg_width / pr->field_width) - 1; - *width = pr->field_width; - return; - } + *modep = mode | INTC_MODE_IS_PRIO; + *datap = &pr->value; + *enums = pr->enum_ids; + *first = (pr->reg_width / pr->field_width) - 1; + *width = pr->field_width; + return; + } } abort(); } static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, - int enable, int is_group) + int enable, int is_group) { struct intc_source *source = desc->sources + id; if (!id) - return; + return; if (!source->next_enum_id && (!source->enable_max || !source->vect)) { #ifdef DEBUG_INTC_SOURCES printf("sh_intc: reserved interrupt source %d modified\n", id); #endif - return; + return; } if (source->vect) @@ -237,7 +237,7 @@ static uint64_t sh_intc_read(void *opaque, hwaddr offset, #endif sh_intc_locate(desc, (unsigned long)offset, &valuep, - &enum_ids, &first, &width, &mode); + &enum_ids, &first, &width, &mode); return *valuep; } @@ -258,7 +258,7 @@ static void sh_intc_write(void *opaque, hwaddr offset, #endif sh_intc_locate(desc, (unsigned long)offset, &valuep, - &enum_ids, &first, &width, &mode); + &enum_ids, &first, &width, &mode); switch (mode) { case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break; @@ -270,11 +270,11 @@ static void sh_intc_write(void *opaque, hwaddr offset, for (k = 0; k <= first; k++) { mask = ((1 << width) - 1) << ((first - k) * width); - if ((*valuep & mask) == (value & mask)) + if ((*valuep & mask) == (value & mask)) continue; #if 0 - printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", - k, first, enum_ids[k], (unsigned int)mask); + printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", + k, first, enum_ids[k], (unsigned int)mask); #endif sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0); } @@ -301,11 +301,11 @@ struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) } static unsigned int sh_intc_register(MemoryRegion *sysmem, - struct intc_desc *desc, - const unsigned long address, - const char *type, - const char *action, - const unsigned int index) + struct intc_desc *desc, + const unsigned long address, + const char *type, + const char *action, + const unsigned int index) { char name[60]; MemoryRegion *iomem, *iomem_p4, *iomem_a7; @@ -333,74 +333,74 @@ static unsigned int sh_intc_register(MemoryRegion *sysmem, } static void sh_intc_register_source(struct intc_desc *desc, - intc_enum source, - struct intc_group *groups, - int nr_groups) + intc_enum source, + struct intc_group *groups, + int nr_groups) { unsigned int i, k; struct intc_source *s; if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; + struct intc_mask_reg *mr = desc->mask_regs + i; - for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) { + for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) { if (mr->enum_ids[k] != source) continue; - s = sh_intc_source(desc, mr->enum_ids[k]); - if (s) + s = sh_intc_source(desc, mr->enum_ids[k]); + if (s) s->enable_max++; - } - } + } + } } if (desc->prio_regs) { for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; + struct intc_prio_reg *pr = desc->prio_regs + i; - for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) { + for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) { if (pr->enum_ids[k] != source) continue; - s = sh_intc_source(desc, pr->enum_ids[k]); - if (s) + s = sh_intc_source(desc, pr->enum_ids[k]); + if (s) s->enable_max++; - } - } + } + } } if (groups) { for (i = 0; i < nr_groups; i++) { - struct intc_group *gr = groups + i; + struct intc_group *gr = groups + i; - for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) { + for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) { if (gr->enum_ids[k] != source) continue; - s = sh_intc_source(desc, gr->enum_ids[k]); - if (s) + s = sh_intc_source(desc, gr->enum_ids[k]); + if (s) s->enable_max++; - } - } + } + } } } void sh_intc_register_sources(struct intc_desc *desc, - struct intc_vect *vectors, - int nr_vectors, - struct intc_group *groups, - int nr_groups) + struct intc_vect *vectors, + int nr_vectors, + struct intc_group *groups, + int nr_groups) { unsigned int i, k; struct intc_source *s; for (i = 0; i < nr_vectors; i++) { - struct intc_vect *vect = vectors + i; + struct intc_vect *vect = vectors + i; - sh_intc_register_source(desc, vect->enum_id, groups, nr_groups); - s = sh_intc_source(desc, vect->enum_id); + sh_intc_register_source(desc, vect->enum_id, groups, nr_groups); + s = sh_intc_source(desc, vect->enum_id); if (s) { s->vect = vect->vect; @@ -413,34 +413,34 @@ void sh_intc_register_sources(struct intc_desc *desc, if (groups) { for (i = 0; i < nr_groups; i++) { - struct intc_group *gr = groups + i; + struct intc_group *gr = groups + i; - s = sh_intc_source(desc, gr->enum_id); - s->next_enum_id = gr->enum_ids[0]; + s = sh_intc_source(desc, gr->enum_id); + s->next_enum_id = gr->enum_ids[0]; - for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) { + for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) { if (!gr->enum_ids[k]) continue; - s = sh_intc_source(desc, gr->enum_ids[k - 1]); - s->next_enum_id = gr->enum_ids[k]; - } + s = sh_intc_source(desc, gr->enum_ids[k - 1]); + s->next_enum_id = gr->enum_ids[k]; + } #ifdef DEBUG_INTC_SOURCES - printf("sh_intc: registered group %d (%d/%d)\n", - gr->enum_id, s->enable_count, s->enable_max); + printf("sh_intc: registered group %d (%d/%d)\n", + gr->enum_id, s->enable_count, s->enable_max); #endif - } + } } } int sh_intc_init(MemoryRegion *sysmem, - struct intc_desc *desc, - int nr_sources, - struct intc_mask_reg *mask_regs, - int nr_mask_regs, - struct intc_prio_reg *prio_regs, - int nr_prio_regs) + struct intc_desc *desc, + int nr_sources, + struct intc_mask_reg *mask_regs, + int nr_mask_regs, + struct intc_prio_reg *prio_regs, + int nr_prio_regs) { unsigned int i, j; @@ -474,24 +474,24 @@ int sh_intc_init(MemoryRegion *sysmem, reg_struct->action##_reg, #type, #action, j if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; + struct intc_mask_reg *mr = desc->mask_regs + i; j += sh_intc_register(sysmem, desc, INT_REG_PARAMS(mr, mask, set, j)); j += sh_intc_register(sysmem, desc, INT_REG_PARAMS(mr, mask, clr, j)); - } + } } if (desc->prio_regs) { for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; + struct intc_prio_reg *pr = desc->prio_regs + i; j += sh_intc_register(sysmem, desc, INT_REG_PARAMS(pr, prio, set, j)); j += sh_intc_register(sysmem, desc, INT_REG_PARAMS(pr, prio, clr, j)); - } + } } #undef INT_REG_PARAMS @@ -505,10 +505,10 @@ void sh_intc_set_irl(void *opaque, int n, int level) struct intc_source *s = opaque; int i, irl = level ^ 15; for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) { - if (i == irl) - sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1); - else - if (s->asserted) - sh_intc_toggle_source(s, 0, -1); + if (i == irl) + sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1); + else + if (s->asserted) + sh_intc_toggle_source(s, 0, -1); } } diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 006010f30a..8f0d373b09 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -56,10 +56,10 @@ #define LINUX_LOAD_OFFSET 0x0800000 #define INITRD_LOAD_OFFSET 0x1800000 -#define PA_IRLMSK 0x00 -#define PA_POWOFF 0x30 -#define PA_VERREG 0x32 -#define PA_OUTPORT 0x36 +#define PA_IRLMSK 0x00 +#define PA_POWOFF 0x30 +#define PA_VERREG 0x32 +#define PA_OUTPORT 0x36 typedef struct { uint16_t bcr; @@ -96,19 +96,19 @@ enum r2d_fpga_irq { }; static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = { - [CF_IDE] = { 1, 1<<9 }, - [CF_CD] = { 2, 1<<8 }, - [PCI_INTA] = { 9, 1<<14 }, - [PCI_INTB] = { 10, 1<<13 }, - [PCI_INTC] = { 3, 1<<12 }, - [PCI_INTD] = { 0, 1<<11 }, - [SM501] = { 4, 1<<10 }, - [KEY] = { 5, 1<<6 }, - [RTC_A] = { 6, 1<<5 }, - [RTC_T] = { 7, 1<<4 }, - [SDCARD] = { 8, 1<<7 }, - [EXT] = { 11, 1<<0 }, - [TP] = { 12, 1<<15 }, + [CF_IDE] = { 1, 1<<9 }, + [CF_CD] = { 2, 1<<8 }, + [PCI_INTA] = { 9, 1<<14 }, + [PCI_INTB] = { 10, 1<<13 }, + [PCI_INTC] = { 3, 1<<12 }, + [PCI_INTD] = { 0, 1<<11 }, + [SM501] = { 4, 1<<10 }, + [KEY] = { 5, 1<<6 }, + [RTC_A] = { 6, 1<<5 }, + [RTC_T] = { 7, 1<<4 }, + [SDCARD] = { 8, 1<<7 }, + [EXT] = { 11, 1<<0 }, + [TP] = { 12, 1<<15 }, }; static void update_irl(r2d_fpga_t *fpga) diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index d53a436d8c..2a175bfa74 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -60,17 +60,17 @@ typedef struct SH7750State { uint16_t gpioic; uint32_t pctra; uint32_t pctrb; - uint16_t portdira; /* Cached */ - uint16_t portpullupa; /* Cached */ - uint16_t portdirb; /* Cached */ - uint16_t portpullupb; /* Cached */ + uint16_t portdira; /* Cached */ + uint16_t portpullupa; /* Cached */ + uint16_t portdirb; /* Cached */ + uint16_t portpullupb; /* Cached */ uint16_t pdtra; uint16_t pdtrb; - uint16_t periph_pdtra; /* Imposed by the peripherals */ - uint16_t periph_portdira; /* Direction seen from the peripherals */ - uint16_t periph_pdtrb; /* Imposed by the peripherals */ - uint16_t periph_portdirb; /* Direction seen from the peripherals */ - sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ + uint16_t periph_pdtra; /* Imposed by the peripherals */ + uint16_t periph_portdira; /* Direction seen from the peripherals */ + uint16_t periph_pdtrb; /* Imposed by the peripherals */ + uint16_t periph_portdirb; /* Direction seen from the peripherals */ + sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ /* Cache */ uint32_t ccr; @@ -91,10 +91,10 @@ int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device) int i; for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] == NULL) { - s->devices[i] = device; - return 0; - } + if (s->devices[i] == NULL) { + s->devices[i] = device; + return 0; + } } return -1; } @@ -103,37 +103,37 @@ static uint16_t portdir(uint32_t v) { #define EVENPORTMASK(n) ((v & (1<<((n)<<1))) >> (n)) return - EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) | - EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) | - EVENPORTMASK(9) | EVENPORTMASK(8) | EVENPORTMASK(7) | - EVENPORTMASK(6) | EVENPORTMASK(5) | EVENPORTMASK(4) | - EVENPORTMASK(3) | EVENPORTMASK(2) | EVENPORTMASK(1) | - EVENPORTMASK(0); + EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) | + EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) | + EVENPORTMASK(9) | EVENPORTMASK(8) | EVENPORTMASK(7) | + EVENPORTMASK(6) | EVENPORTMASK(5) | EVENPORTMASK(4) | + EVENPORTMASK(3) | EVENPORTMASK(2) | EVENPORTMASK(1) | + EVENPORTMASK(0); } static uint16_t portpullup(uint32_t v) { #define ODDPORTMASK(n) ((v & (1<<(((n)<<1)+1))) >> (n)) return - ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) | - ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) | - ODDPORTMASK(9) | ODDPORTMASK(8) | ODDPORTMASK(7) | ODDPORTMASK(6) | - ODDPORTMASK(5) | ODDPORTMASK(4) | ODDPORTMASK(3) | ODDPORTMASK(2) | - ODDPORTMASK(1) | ODDPORTMASK(0); + ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) | + ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) | + ODDPORTMASK(9) | ODDPORTMASK(8) | ODDPORTMASK(7) | ODDPORTMASK(6) | + ODDPORTMASK(5) | ODDPORTMASK(4) | ODDPORTMASK(3) | ODDPORTMASK(2) | + ODDPORTMASK(1) | ODDPORTMASK(0); } static uint16_t porta_lines(SH7750State * s) { - return (s->portdira & s->pdtra) | /* CPU */ - (s->periph_portdira & s->periph_pdtra) | /* Peripherals */ - (~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */ + return (s->portdira & s->pdtra) | /* CPU */ + (s->periph_portdira & s->periph_pdtra) | /* Peripherals */ + (~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */ } static uint16_t portb_lines(SH7750State * s) { - return (s->portdirb & s->pdtrb) | /* CPU */ - (s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */ - (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */ + return (s->portdirb & s->pdtrb) | /* CPU */ + (s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */ + (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */ } static void gen_port_interrupts(SH7750State * s) @@ -148,26 +148,26 @@ static void porta_changed(SH7750State * s, uint16_t prev) #if 0 fprintf(stderr, "porta changed from 0x%04x to 0x%04x\n", - prev, porta_lines(s)); + prev, porta_lines(s)); fprintf(stderr, "pdtra=0x%04x, pctra=0x%08x\n", s->pdtra, s->pctra); #endif currenta = porta_lines(s); if (currenta == prev) - return; + return; changes = currenta ^ prev; for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) { - r |= s->devices[i]->port_change_cb(currenta, portb_lines(s), - &s->periph_pdtra, - &s->periph_portdira, - &s->periph_pdtrb, - &s->periph_portdirb); - } + if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) { + r |= s->devices[i]->port_change_cb(currenta, portb_lines(s), + &s->periph_pdtra, + &s->periph_portdira, + &s->periph_pdtrb, + &s->periph_portdirb); + } } if (r) - gen_port_interrupts(s); + gen_port_interrupts(s); } static void portb_changed(SH7750State * s, uint16_t prev) @@ -177,21 +177,21 @@ static void portb_changed(SH7750State * s, uint16_t prev) currentb = portb_lines(s); if (currentb == prev) - return; + return; changes = currentb ^ prev; for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) { - r |= s->devices[i]->port_change_cb(portb_lines(s), currentb, - &s->periph_pdtra, - &s->periph_portdira, - &s->periph_pdtrb, - &s->periph_portdirb); - } + if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) { + r |= s->devices[i]->port_change_cb(portb_lines(s), currentb, + &s->periph_pdtra, + &s->periph_portdira, + &s->periph_pdtrb, + &s->periph_portdirb); + } } if (r) - gen_port_interrupts(s); + gen_port_interrupts(s); } /********************************************************************** @@ -201,20 +201,20 @@ static void portb_changed(SH7750State * s, uint16_t prev) static void error_access(const char *kind, hwaddr addr) { fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") not supported\n", - kind, regname(addr), addr); + kind, regname(addr), addr); } static void ignore_access(const char *kind, hwaddr addr) { fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") ignored\n", - kind, regname(addr), addr); + kind, regname(addr), addr); } static uint32_t sh7750_mem_readb(void *opaque, hwaddr addr) { switch (addr) { default: - error_access("byte read", addr); + error_access("byte read", addr); abort(); } } @@ -225,30 +225,30 @@ static uint32_t sh7750_mem_readw(void *opaque, hwaddr addr) switch (addr) { case SH7750_BCR2_A7: - return s->bcr2; + return s->bcr2; case SH7750_BCR3_A7: - if(!has_bcr3_and_bcr4(s)) - error_access("word read", addr); - return s->bcr3; + if(!has_bcr3_and_bcr4(s)) + error_access("word read", addr); + return s->bcr3; case SH7750_FRQCR_A7: - return 0; + return 0; case SH7750_PCR_A7: - return s->pcr; + return s->pcr; case SH7750_RFCR_A7: - fprintf(stderr, - "Read access to refresh count register, incrementing\n"); - return s->rfcr++; + fprintf(stderr, + "Read access to refresh count register, incrementing\n"); + return s->rfcr++; case SH7750_PDTRA_A7: - return porta_lines(s); + return porta_lines(s); case SH7750_PDTRB_A7: - return portb_lines(s); + return portb_lines(s); case SH7750_RTCOR_A7: case SH7750_RTCNT_A7: case SH7750_RTCSR_A7: - ignore_access("word read", addr); - return 0; + ignore_access("word read", addr); + return 0; default: - error_access("word read", addr); + error_access("word read", addr); abort(); } } @@ -260,11 +260,11 @@ static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr) switch (addr) { case SH7750_BCR1_A7: - return s->bcr1; + return s->bcr1; case SH7750_BCR4_A7: - if(!has_bcr3_and_bcr4(s)) - error_access("long read", addr); - return s->bcr4; + if(!has_bcr3_and_bcr4(s)) + error_access("long read", addr); + return s->bcr4; case SH7750_WCR1_A7: case SH7750_WCR2_A7: case SH7750_WCR3_A7: @@ -288,31 +288,31 @@ static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr) case SH7750_INTEVT_A7: return s->cpu->env.intevt; case SH7750_CCR_A7: - return s->ccr; - case 0x1f000030: /* Processor version */ + return s->ccr; + case 0x1f000030: /* Processor version */ scc = SUPERH_CPU_GET_CLASS(s->cpu); return scc->pvr; - case 0x1f000040: /* Cache version */ + case 0x1f000040: /* Cache version */ scc = SUPERH_CPU_GET_CLASS(s->cpu); return scc->cvr; - case 0x1f000044: /* Processor revision */ + case 0x1f000044: /* Processor revision */ scc = SUPERH_CPU_GET_CLASS(s->cpu); return scc->prr; default: - error_access("long read", addr); + error_access("long read", addr); abort(); } } #define is_in_sdrmx(a, x) (a >= SH7750_SDMR ## x ## _A7 \ - && a <= (SH7750_SDMR ## x ## _A7 + SH7750_SDMR ## x ## _REGNB)) + && a <= (SH7750_SDMR ## x ## _A7 + SH7750_SDMR ## x ## _REGNB)) static void sh7750_mem_writeb(void *opaque, hwaddr addr, - uint32_t mem_value) + uint32_t mem_value) { if (is_in_sdrmx(addr, 2) || is_in_sdrmx(addr, 3)) { - ignore_access("byte write", addr); - return; + ignore_access("byte write", addr); + return; } error_access("byte write", addr); @@ -320,94 +320,94 @@ static void sh7750_mem_writeb(void *opaque, hwaddr addr, } static void sh7750_mem_writew(void *opaque, hwaddr addr, - uint32_t mem_value) + uint32_t mem_value) { SH7750State *s = opaque; uint16_t temp; switch (addr) { - /* SDRAM controller */ + /* SDRAM controller */ case SH7750_BCR2_A7: s->bcr2 = mem_value; return; case SH7750_BCR3_A7: - if(!has_bcr3_and_bcr4(s)) - error_access("word write", addr); - s->bcr3 = mem_value; - return; + if(!has_bcr3_and_bcr4(s)) + error_access("word write", addr); + s->bcr3 = mem_value; + return; case SH7750_PCR_A7: - s->pcr = mem_value; - return; + s->pcr = mem_value; + return; case SH7750_RTCNT_A7: case SH7750_RTCOR_A7: case SH7750_RTCSR_A7: - ignore_access("word write", addr); - return; - /* IO ports */ + ignore_access("word write", addr); + return; + /* IO ports */ case SH7750_PDTRA_A7: - temp = porta_lines(s); - s->pdtra = mem_value; - porta_changed(s, temp); - return; + temp = porta_lines(s); + s->pdtra = mem_value; + porta_changed(s, temp); + return; case SH7750_PDTRB_A7: - temp = portb_lines(s); - s->pdtrb = mem_value; - portb_changed(s, temp); - return; + temp = portb_lines(s); + s->pdtrb = mem_value; + portb_changed(s, temp); + return; case SH7750_RFCR_A7: - fprintf(stderr, "Write access to refresh count register\n"); - s->rfcr = mem_value; - return; + fprintf(stderr, "Write access to refresh count register\n"); + s->rfcr = mem_value; + return; case SH7750_GPIOIC_A7: - s->gpioic = mem_value; - if (mem_value != 0) { - fprintf(stderr, "I/O interrupts not implemented\n"); + s->gpioic = mem_value; + if (mem_value != 0) { + fprintf(stderr, "I/O interrupts not implemented\n"); abort(); - } - return; + } + return; default: - error_access("word write", addr); + error_access("word write", addr); abort(); } } static void sh7750_mem_writel(void *opaque, hwaddr addr, - uint32_t mem_value) + uint32_t mem_value) { SH7750State *s = opaque; uint16_t temp; switch (addr) { - /* SDRAM controller */ + /* SDRAM controller */ case SH7750_BCR1_A7: s->bcr1 = mem_value; return; case SH7750_BCR4_A7: - if(!has_bcr3_and_bcr4(s)) - error_access("long write", addr); - s->bcr4 = mem_value; - return; + if(!has_bcr3_and_bcr4(s)) + error_access("long write", addr); + s->bcr4 = mem_value; + return; case SH7750_WCR1_A7: case SH7750_WCR2_A7: case SH7750_WCR3_A7: case SH7750_MCR_A7: - ignore_access("long write", addr); - return; - /* IO ports */ + ignore_access("long write", addr); + return; + /* IO ports */ case SH7750_PCTRA_A7: - temp = porta_lines(s); - s->pctra = mem_value; - s->portdira = portdir(mem_value); - s->portpullupa = portpullup(mem_value); - porta_changed(s, temp); - return; + temp = porta_lines(s); + s->pctra = mem_value; + s->portdira = portdir(mem_value); + s->portpullupa = portpullup(mem_value); + porta_changed(s, temp); + return; case SH7750_PCTRB_A7: - temp = portb_lines(s); - s->pctrb = mem_value; - s->portdirb = portdir(mem_value); - s->portpullupb = portpullup(mem_value); - portb_changed(s, temp); - return; + temp = portb_lines(s); + s->pctrb = mem_value; + s->portdirb = portdir(mem_value); + s->portpullupb = portpullup(mem_value); + portb_changed(s, temp); + return; case SH7750_MMUCR_A7: if (mem_value & MMUCR_TI) { cpu_sh4_invalidate_tlb(&s->cpu->env); @@ -443,10 +443,10 @@ static void sh7750_mem_writel(void *opaque, hwaddr addr, s->cpu->env.intevt = mem_value & 0x000007ff; return; case SH7750_CCR_A7: - s->ccr = mem_value; - return; + s->ccr = mem_value; + return; default: - error_access("long write", addr); + error_access("long write", addr); abort(); } } @@ -496,151 +496,150 @@ static const MemoryRegionOps sh7750_mem_ops = { */ enum { - UNUSED = 0, + UNUSED = 0, - /* interrupt sources */ - IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, IRL_7, - IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E, - IRL0, IRL1, IRL2, IRL3, - HUDI, GPIOI, - DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3, - DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7, - DMAC_DMAE, - PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, - PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3, - TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI, - RTC_ATI, RTC_PRI, RTC_CUI, - SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI, - SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI, - WDT, - REF_RCMI, REF_ROVI, + /* interrupt sources */ + IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, IRL_7, + IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E, + IRL0, IRL1, IRL2, IRL3, + HUDI, GPIOI, + DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3, + DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7, + DMAC_DMAE, + PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3, + TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI, + RTC_ATI, RTC_PRI, RTC_CUI, + SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI, + SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI, + WDT, + REF_RCMI, REF_ROVI, - /* interrupt groups */ - DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF, - /* irl bundle */ - IRL, + /* interrupt groups */ + DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF, + /* irl bundle */ + IRL, - NR_SOURCES, + NR_SOURCES, }; static struct intc_vect vectors[] = { - INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620), - INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), - INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460), - INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0), - INTC_VECT(RTC_CUI, 0x4c0), - INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500), - INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540), - INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720), - INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760), - INTC_VECT(WDT, 0x560), - INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0), + INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460), + INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0), + INTC_VECT(RTC_CUI, 0x4c0), + INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500), + INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540), + INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720), + INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760), + INTC_VECT(WDT, 0x560), + INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0), }; static struct intc_group groups[] = { - INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI), - INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI), - INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI), - INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI), - INTC_GROUP(REF, REF_RCMI, REF_ROVI), + INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI), + INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI), + INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI), + INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI), + INTC_GROUP(REF, REF_RCMI, REF_ROVI), }; static struct intc_prio_reg prio_registers[] = { - { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, - { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } }, - { 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } }, - { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } }, - { 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0, - TMU4, TMU3, - PCIC1, PCIC0_PCISERR } }, + { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, + { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } }, + { 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } }, + { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } }, + { 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0, TMU4, TMU3, + PCIC1, PCIC0_PCISERR } }, }; /* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */ static struct intc_vect vectors_dma4[] = { - INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), - INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), - INTC_VECT(DMAC_DMAE, 0x6c0), + INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), + INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), + INTC_VECT(DMAC_DMAE, 0x6c0), }; static struct intc_group groups_dma4[] = { - INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, - DMAC_DMTE3, DMAC_DMAE), + INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, + DMAC_DMTE3, DMAC_DMAE), }; /* SH7750R and SH7751R both have 8-channel DMA controllers */ static struct intc_vect vectors_dma8[] = { - INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), - INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), - INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0), - INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0), - INTC_VECT(DMAC_DMAE, 0x6c0), + INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), + INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), + INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0), + INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0), + INTC_VECT(DMAC_DMAE, 0x6c0), }; static struct intc_group groups_dma8[] = { - INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, - DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5, - DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE), + INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, + DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5, + DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE), }; /* SH7750R, SH7751 and SH7751R all have two extra timer channels */ static struct intc_vect vectors_tmu34[] = { - INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80), + INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80), }; static struct intc_mask_reg mask_registers[] = { - { 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, TMU4, TMU3, - PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, - PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, - PCIC1_PCIDMA3, PCIC0_PCISERR } }, + { 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, TMU4, TMU3, + PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, + PCIC1_PCIDMA3, PCIC0_PCISERR } }, }; /* SH7750S, SH7750R, SH7751 and SH7751R all have IRLM priority registers */ static struct intc_vect vectors_irlm[] = { - INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0), - INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360), + INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0), + INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360), }; /* SH7751 and SH7751R both have PCI */ static struct intc_vect vectors_pci[] = { - INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0), - INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0), - INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60), - INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20), + INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0), + INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0), + INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60), + INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20), }; static struct intc_group groups_pci[] = { - INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, - PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3), + INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3), }; static struct intc_vect vectors_irl[] = { - INTC_VECT(IRL_0, 0x200), - INTC_VECT(IRL_1, 0x220), - INTC_VECT(IRL_2, 0x240), - INTC_VECT(IRL_3, 0x260), - INTC_VECT(IRL_4, 0x280), - INTC_VECT(IRL_5, 0x2a0), - INTC_VECT(IRL_6, 0x2c0), - INTC_VECT(IRL_7, 0x2e0), - INTC_VECT(IRL_8, 0x300), - INTC_VECT(IRL_9, 0x320), - INTC_VECT(IRL_A, 0x340), - INTC_VECT(IRL_B, 0x360), - INTC_VECT(IRL_C, 0x380), - INTC_VECT(IRL_D, 0x3a0), - INTC_VECT(IRL_E, 0x3c0), + INTC_VECT(IRL_0, 0x200), + INTC_VECT(IRL_1, 0x220), + INTC_VECT(IRL_2, 0x240), + INTC_VECT(IRL_3, 0x260), + INTC_VECT(IRL_4, 0x280), + INTC_VECT(IRL_5, 0x2a0), + INTC_VECT(IRL_6, 0x2c0), + INTC_VECT(IRL_7, 0x2e0), + INTC_VECT(IRL_8, 0x300), + INTC_VECT(IRL_9, 0x320), + INTC_VECT(IRL_A, 0x340), + INTC_VECT(IRL_B, 0x360), + INTC_VECT(IRL_C, 0x380), + INTC_VECT(IRL_D, 0x3a0), + INTC_VECT(IRL_E, 0x3c0), }; static struct intc_group groups_irl[] = { - INTC_GROUP(IRL, IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, - IRL_7, IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E), + INTC_GROUP(IRL, IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, + IRL_7, IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E), }; /********************************************************************** @@ -679,7 +678,7 @@ static uint64_t sh7750_mmct_read(void *opaque, hwaddr addr, case MM_ICACHE_ADDR: case MM_ICACHE_DATA: /* do nothing */ - break; + break; case MM_ITLB_ADDR: ret = cpu_sh4_read_mmaped_itlb_addr(&s->cpu->env, addr); break; @@ -689,7 +688,7 @@ static uint64_t sh7750_mmct_read(void *opaque, hwaddr addr, case MM_OCACHE_ADDR: case MM_OCACHE_DATA: /* do nothing */ - break; + break; case MM_UTLB_ADDR: ret = cpu_sh4_read_mmaped_utlb_addr(&s->cpu->env, addr); break; @@ -722,27 +721,27 @@ static void sh7750_mmct_write(void *opaque, hwaddr addr, case MM_ICACHE_ADDR: case MM_ICACHE_DATA: /* do nothing */ - break; + break; case MM_ITLB_ADDR: cpu_sh4_write_mmaped_itlb_addr(&s->cpu->env, addr, mem_value); break; case MM_ITLB_DATA: cpu_sh4_write_mmaped_itlb_data(&s->cpu->env, addr, mem_value); abort(); - break; + break; case MM_OCACHE_ADDR: case MM_OCACHE_DATA: /* do nothing */ - break; + break; case MM_UTLB_ADDR: cpu_sh4_write_mmaped_utlb_addr(&s->cpu->env, addr, mem_value); - break; + break; case MM_UTLB_DATA: cpu_sh4_write_mmaped_utlb_data(&s->cpu->env, addr, mem_value); - break; + break; default: abort(); - break; + break; } } @@ -758,7 +757,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) s = g_malloc0(sizeof(SH7750State)); s->cpu = cpu; - s->periph_freq = 60000000; /* 60MHz */ + s->periph_freq = 60000000; /* 60MHz */ memory_region_init_io(&s->iomem, NULL, &sh7750_mem_ops, s, "memory", 0x1fc01000); @@ -791,12 +790,12 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) memory_region_add_subregion(sysmem, 0xf0000000, &s->mmct_iomem); sh_intc_init(sysmem, &s->intc, NR_SOURCES, - _INTC_ARRAY(mask_registers), - _INTC_ARRAY(prio_registers)); + _INTC_ARRAY(mask_registers), + _INTC_ARRAY(prio_registers)); sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors), - _INTC_ARRAY(groups)); + _INTC_ARRAY(vectors), + _INTC_ARRAY(groups)); cpu->env.intc_handle = &s->intc; @@ -817,50 +816,50 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) s->intc.irqs[SCIF_BRI]); tmu012_init(sysmem, 0x1fd80000, - TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK, - s->periph_freq, - s->intc.irqs[TMU0], - s->intc.irqs[TMU1], - s->intc.irqs[TMU2_TUNI], - s->intc.irqs[TMU2_TICPI]); + TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK, + s->periph_freq, + s->intc.irqs[TMU0], + s->intc.irqs[TMU1], + s->intc.irqs[TMU2_TUNI], + s->intc.irqs[TMU2_TICPI]); if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) { sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_dma4), - _INTC_ARRAY(groups_dma4)); + _INTC_ARRAY(vectors_dma4), + _INTC_ARRAY(groups_dma4)); } if (cpu->env.id & (SH_CPU_SH7750R | SH_CPU_SH7751R)) { sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_dma8), - _INTC_ARRAY(groups_dma8)); + _INTC_ARRAY(vectors_dma8), + _INTC_ARRAY(groups_dma8)); } if (cpu->env.id & (SH_CPU_SH7750R | SH_CPU_SH7751 | SH_CPU_SH7751R)) { sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_tmu34), - NULL, 0); + _INTC_ARRAY(vectors_tmu34), + NULL, 0); tmu012_init(sysmem, 0x1e100000, 0, s->periph_freq, - s->intc.irqs[TMU3], - s->intc.irqs[TMU4], - NULL, NULL); + s->intc.irqs[TMU3], + s->intc.irqs[TMU4], + NULL, NULL); } if (cpu->env.id & (SH_CPU_SH7751_ALL)) { sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_pci), - _INTC_ARRAY(groups_pci)); + _INTC_ARRAY(vectors_pci), + _INTC_ARRAY(groups_pci)); } if (cpu->env.id & (SH_CPU_SH7750S | SH_CPU_SH7750R | SH_CPU_SH7751_ALL)) { sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_irlm), - NULL, 0); + _INTC_ARRAY(vectors_irlm), + NULL, 0); } sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_irl), - _INTC_ARRAY(groups_irl)); + _INTC_ARRAY(vectors_irl), + _INTC_ARRAY(groups_irl)); return s; } diff --git a/hw/sh4/sh7750_regnames.c b/hw/sh4/sh7750_regnames.c index 0630fe3cf4..b1f112df3e 100644 --- a/hw/sh4/sh7750_regnames.c +++ b/hw/sh4/sh7750_regnames.c @@ -12,76 +12,76 @@ typedef struct { static regname_t regnames[] = { REGNAME(SH7750_PTEH_A7) - REGNAME(SH7750_PTEL_A7) - REGNAME(SH7750_PTEA_A7) - REGNAME(SH7750_TTB_A7) - REGNAME(SH7750_TEA_A7) - REGNAME(SH7750_MMUCR_A7) - REGNAME(SH7750_CCR_A7) - REGNAME(SH7750_QACR0_A7) - REGNAME(SH7750_QACR1_A7) - REGNAME(SH7750_TRA_A7) - REGNAME(SH7750_EXPEVT_A7) - REGNAME(SH7750_INTEVT_A7) - REGNAME(SH7750_STBCR_A7) - REGNAME(SH7750_STBCR2_A7) - REGNAME(SH7750_FRQCR_A7) - REGNAME(SH7750_WTCNT_A7) - REGNAME(SH7750_WTCSR_A7) - REGNAME(SH7750_R64CNT_A7) - REGNAME(SH7750_RSECCNT_A7) - REGNAME(SH7750_RMINCNT_A7) - REGNAME(SH7750_RHRCNT_A7) - REGNAME(SH7750_RWKCNT_A7) - REGNAME(SH7750_RDAYCNT_A7) - REGNAME(SH7750_RMONCNT_A7) - REGNAME(SH7750_RYRCNT_A7) - REGNAME(SH7750_RSECAR_A7) - REGNAME(SH7750_RMINAR_A7) - REGNAME(SH7750_RHRAR_A7) - REGNAME(SH7750_RWKAR_A7) - REGNAME(SH7750_RDAYAR_A7) - REGNAME(SH7750_RMONAR_A7) - REGNAME(SH7750_RCR1_A7) - REGNAME(SH7750_RCR2_A7) - REGNAME(SH7750_BCR1_A7) - REGNAME(SH7750_BCR2_A7) - REGNAME(SH7750_WCR1_A7) - REGNAME(SH7750_WCR2_A7) - REGNAME(SH7750_WCR3_A7) - REGNAME(SH7750_MCR_A7) - REGNAME(SH7750_PCR_A7) - REGNAME(SH7750_RTCSR_A7) - REGNAME(SH7750_RTCNT_A7) - REGNAME(SH7750_RTCOR_A7) - REGNAME(SH7750_RFCR_A7) - REGNAME(SH7750_SAR0_A7) - REGNAME(SH7750_SAR1_A7) - REGNAME(SH7750_SAR2_A7) - REGNAME(SH7750_SAR3_A7) - REGNAME(SH7750_DAR0_A7) - REGNAME(SH7750_DAR1_A7) - REGNAME(SH7750_DAR2_A7) - REGNAME(SH7750_DAR3_A7) - REGNAME(SH7750_DMATCR0_A7) - REGNAME(SH7750_DMATCR1_A7) - REGNAME(SH7750_DMATCR2_A7) - REGNAME(SH7750_DMATCR3_A7) - REGNAME(SH7750_CHCR0_A7) - REGNAME(SH7750_CHCR1_A7) - REGNAME(SH7750_CHCR2_A7) - REGNAME(SH7750_CHCR3_A7) - REGNAME(SH7750_DMAOR_A7) - REGNAME(SH7750_PCTRA_A7) - REGNAME(SH7750_PDTRA_A7) - REGNAME(SH7750_PCTRB_A7) - REGNAME(SH7750_PDTRB_A7) - REGNAME(SH7750_GPIOIC_A7) - REGNAME(SH7750_ICR_A7) - REGNAME(SH7750_BCR3_A7) - REGNAME(SH7750_BCR4_A7) - REGNAME(SH7750_SDMR2_A7) - REGNAME(SH7750_SDMR3_A7) {(uint32_t) - 1, NULL} + REGNAME(SH7750_PTEL_A7) + REGNAME(SH7750_PTEA_A7) + REGNAME(SH7750_TTB_A7) + REGNAME(SH7750_TEA_A7) + REGNAME(SH7750_MMUCR_A7) + REGNAME(SH7750_CCR_A7) + REGNAME(SH7750_QACR0_A7) + REGNAME(SH7750_QACR1_A7) + REGNAME(SH7750_TRA_A7) + REGNAME(SH7750_EXPEVT_A7) + REGNAME(SH7750_INTEVT_A7) + REGNAME(SH7750_STBCR_A7) + REGNAME(SH7750_STBCR2_A7) + REGNAME(SH7750_FRQCR_A7) + REGNAME(SH7750_WTCNT_A7) + REGNAME(SH7750_WTCSR_A7) + REGNAME(SH7750_R64CNT_A7) + REGNAME(SH7750_RSECCNT_A7) + REGNAME(SH7750_RMINCNT_A7) + REGNAME(SH7750_RHRCNT_A7) + REGNAME(SH7750_RWKCNT_A7) + REGNAME(SH7750_RDAYCNT_A7) + REGNAME(SH7750_RMONCNT_A7) + REGNAME(SH7750_RYRCNT_A7) + REGNAME(SH7750_RSECAR_A7) + REGNAME(SH7750_RMINAR_A7) + REGNAME(SH7750_RHRAR_A7) + REGNAME(SH7750_RWKAR_A7) + REGNAME(SH7750_RDAYAR_A7) + REGNAME(SH7750_RMONAR_A7) + REGNAME(SH7750_RCR1_A7) + REGNAME(SH7750_RCR2_A7) + REGNAME(SH7750_BCR1_A7) + REGNAME(SH7750_BCR2_A7) + REGNAME(SH7750_WCR1_A7) + REGNAME(SH7750_WCR2_A7) + REGNAME(SH7750_WCR3_A7) + REGNAME(SH7750_MCR_A7) + REGNAME(SH7750_PCR_A7) + REGNAME(SH7750_RTCSR_A7) + REGNAME(SH7750_RTCNT_A7) + REGNAME(SH7750_RTCOR_A7) + REGNAME(SH7750_RFCR_A7) + REGNAME(SH7750_SAR0_A7) + REGNAME(SH7750_SAR1_A7) + REGNAME(SH7750_SAR2_A7) + REGNAME(SH7750_SAR3_A7) + REGNAME(SH7750_DAR0_A7) + REGNAME(SH7750_DAR1_A7) + REGNAME(SH7750_DAR2_A7) + REGNAME(SH7750_DAR3_A7) + REGNAME(SH7750_DMATCR0_A7) + REGNAME(SH7750_DMATCR1_A7) + REGNAME(SH7750_DMATCR2_A7) + REGNAME(SH7750_DMATCR3_A7) + REGNAME(SH7750_CHCR0_A7) + REGNAME(SH7750_CHCR1_A7) + REGNAME(SH7750_CHCR2_A7) + REGNAME(SH7750_CHCR3_A7) + REGNAME(SH7750_DMAOR_A7) + REGNAME(SH7750_PCTRA_A7) + REGNAME(SH7750_PDTRA_A7) + REGNAME(SH7750_PCTRB_A7) + REGNAME(SH7750_PDTRB_A7) + REGNAME(SH7750_GPIOIC_A7) + REGNAME(SH7750_ICR_A7) + REGNAME(SH7750_BCR3_A7) + REGNAME(SH7750_BCR4_A7) + REGNAME(SH7750_SDMR2_A7) + REGNAME(SH7750_SDMR3_A7) {(uint32_t) - 1, NULL} }; const char *regname(uint32_t addr) @@ -89,8 +89,8 @@ const char *regname(uint32_t addr) unsigned int i; for (i = 0; regnames[i].regaddr != (uint32_t) - 1; i++) { - if (regnames[i].regaddr == addr) - return regnames[i].regname; + if (regnames[i].regaddr == addr) + return regnames[i].regname; } return ""; diff --git a/hw/sh4/sh7750_regs.h b/hw/sh4/sh7750_regs.h index ab073dadc7..fd1050646f 100644 --- a/hw/sh4/sh7750_regs.h +++ b/hw/sh4/sh7750_regs.h @@ -43,9 +43,9 @@ * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and * in 0x1f000000 - 0x1fffffff (area 7 address) */ -#define SH7750_P4_BASE 0xff000000 /* Accessible only in - privileged mode */ -#define SH7750_A7_BASE 0x1f000000 /* Accessible only using TLB */ +#define SH7750_P4_BASE 0xff000000 /* Accessible only in + privileged mode */ +#define SH7750_A7_BASE 0x1f000000 /* Accessible only using TLB */ #define SH7750_P4_REG32(ofs) (SH7750_P4_BASE + (ofs)) #define SH7750_A7_REG32(ofs) (SH7750_A7_BASE + (ofs)) @@ -55,84 +55,84 @@ */ /* Page Table Entry High register - PTEH */ -#define SH7750_PTEH_REGOFS 0x000000 /* offset */ +#define SH7750_PTEH_REGOFS 0x000000 /* offset */ #define SH7750_PTEH SH7750_P4_REG32(SH7750_PTEH_REGOFS) #define SH7750_PTEH_A7 SH7750_A7_REG32(SH7750_PTEH_REGOFS) -#define SH7750_PTEH_VPN 0xfffffd00 /* Virtual page number */ +#define SH7750_PTEH_VPN 0xfffffd00 /* Virtual page number */ #define SH7750_PTEH_VPN_S 10 -#define SH7750_PTEH_ASID 0x000000ff /* Address space identifier */ +#define SH7750_PTEH_ASID 0x000000ff /* Address space identifier */ #define SH7750_PTEH_ASID_S 0 /* Page Table Entry Low register - PTEL */ -#define SH7750_PTEL_REGOFS 0x000004 /* offset */ +#define SH7750_PTEL_REGOFS 0x000004 /* offset */ #define SH7750_PTEL SH7750_P4_REG32(SH7750_PTEL_REGOFS) #define SH7750_PTEL_A7 SH7750_A7_REG32(SH7750_PTEL_REGOFS) -#define SH7750_PTEL_PPN 0x1ffffc00 /* Physical page number */ +#define SH7750_PTEL_PPN 0x1ffffc00 /* Physical page number */ #define SH7750_PTEL_PPN_S 10 -#define SH7750_PTEL_V 0x00000100 /* Validity (0-entry is invalid) */ -#define SH7750_PTEL_SZ1 0x00000080 /* Page size bit 1 */ -#define SH7750_PTEL_SZ0 0x00000010 /* Page size bit 0 */ -#define SH7750_PTEL_SZ_1KB 0x00000000 /* 1-kbyte page */ -#define SH7750_PTEL_SZ_4KB 0x00000010 /* 4-kbyte page */ -#define SH7750_PTEL_SZ_64KB 0x00000080 /* 64-kbyte page */ -#define SH7750_PTEL_SZ_1MB 0x00000090 /* 1-Mbyte page */ -#define SH7750_PTEL_PR 0x00000060 /* Protection Key Data */ -#define SH7750_PTEL_PR_ROPO 0x00000000 /* read-only in priv mode */ -#define SH7750_PTEL_PR_RWPO 0x00000020 /* read-write in priv mode */ -#define SH7750_PTEL_PR_ROPU 0x00000040 /* read-only in priv or user mode */ -#define SH7750_PTEL_PR_RWPU 0x00000060 /* read-write in priv or user mode */ -#define SH7750_PTEL_C 0x00000008 /* Cacheability - (0 - page not cacheable) */ -#define SH7750_PTEL_D 0x00000004 /* Dirty bit (1 - write has been - performed to a page) */ -#define SH7750_PTEL_SH 0x00000002 /* Share Status bit (1 - page are - shared by processes) */ -#define SH7750_PTEL_WT 0x00000001 /* Write-through bit, specifies the - cache write mode: - 0 - Copy-back mode - 1 - Write-through mode */ +#define SH7750_PTEL_V 0x00000100 /* Validity (0-entry is invalid) */ +#define SH7750_PTEL_SZ1 0x00000080 /* Page size bit 1 */ +#define SH7750_PTEL_SZ0 0x00000010 /* Page size bit 0 */ +#define SH7750_PTEL_SZ_1KB 0x00000000 /* 1-kbyte page */ +#define SH7750_PTEL_SZ_4KB 0x00000010 /* 4-kbyte page */ +#define SH7750_PTEL_SZ_64KB 0x00000080 /* 64-kbyte page */ +#define SH7750_PTEL_SZ_1MB 0x00000090 /* 1-Mbyte page */ +#define SH7750_PTEL_PR 0x00000060 /* Protection Key Data */ +#define SH7750_PTEL_PR_ROPO 0x00000000 /* read-only in priv mode */ +#define SH7750_PTEL_PR_RWPO 0x00000020 /* read-write in priv mode */ +#define SH7750_PTEL_PR_ROPU 0x00000040 /* read-only in priv or user mode */ +#define SH7750_PTEL_PR_RWPU 0x00000060 /* read-write in priv or user mode */ +#define SH7750_PTEL_C 0x00000008 /* Cacheability + (0 - page not cacheable) */ +#define SH7750_PTEL_D 0x00000004 /* Dirty bit (1 - write has been + performed to a page) */ +#define SH7750_PTEL_SH 0x00000002 /* Share Status bit (1 - page are + shared by processes) */ +#define SH7750_PTEL_WT 0x00000001 /* Write-through bit, specifies the + cache write mode: + 0 - Copy-back mode + 1 - Write-through mode */ /* Page Table Entry Assistance register - PTEA */ -#define SH7750_PTEA_REGOFS 0x000034 /* offset */ +#define SH7750_PTEA_REGOFS 0x000034 /* offset */ #define SH7750_PTEA SH7750_P4_REG32(SH7750_PTEA_REGOFS) #define SH7750_PTEA_A7 SH7750_A7_REG32(SH7750_PTEA_REGOFS) -#define SH7750_PTEA_TC 0x00000008 /* Timing Control bit - 0 - use area 5 wait states - 1 - use area 6 wait states */ -#define SH7750_PTEA_SA 0x00000007 /* Space Attribute bits: */ -#define SH7750_PTEA_SA_UNDEF 0x00000000 /* 0 - undefined */ -#define SH7750_PTEA_SA_IOVAR 0x00000001 /* 1 - variable-size I/O space */ -#define SH7750_PTEA_SA_IO8 0x00000002 /* 2 - 8-bit I/O space */ -#define SH7750_PTEA_SA_IO16 0x00000003 /* 3 - 16-bit I/O space */ -#define SH7750_PTEA_SA_CMEM8 0x00000004 /* 4 - 8-bit common memory space */ -#define SH7750_PTEA_SA_CMEM16 0x00000005 /* 5 - 16-bit common memory space */ -#define SH7750_PTEA_SA_AMEM8 0x00000006 /* 6 - 8-bit attr memory space */ -#define SH7750_PTEA_SA_AMEM16 0x00000007 /* 7 - 16-bit attr memory space */ +#define SH7750_PTEA_TC 0x00000008 /* Timing Control bit + 0 - use area 5 wait states + 1 - use area 6 wait states */ +#define SH7750_PTEA_SA 0x00000007 /* Space Attribute bits: */ +#define SH7750_PTEA_SA_UNDEF 0x00000000 /* 0 - undefined */ +#define SH7750_PTEA_SA_IOVAR 0x00000001 /* 1 - variable-size I/O space */ +#define SH7750_PTEA_SA_IO8 0x00000002 /* 2 - 8-bit I/O space */ +#define SH7750_PTEA_SA_IO16 0x00000003 /* 3 - 16-bit I/O space */ +#define SH7750_PTEA_SA_CMEM8 0x00000004 /* 4 - 8-bit common memory space */ +#define SH7750_PTEA_SA_CMEM16 0x00000005 /* 5 - 16-bit common memory space */ +#define SH7750_PTEA_SA_AMEM8 0x00000006 /* 6 - 8-bit attr memory space */ +#define SH7750_PTEA_SA_AMEM16 0x00000007 /* 7 - 16-bit attr memory space */ /* Translation table base register */ -#define SH7750_TTB_REGOFS 0x000008 /* offset */ +#define SH7750_TTB_REGOFS 0x000008 /* offset */ #define SH7750_TTB SH7750_P4_REG32(SH7750_TTB_REGOFS) #define SH7750_TTB_A7 SH7750_A7_REG32(SH7750_TTB_REGOFS) /* TLB exeption address register - TEA */ -#define SH7750_TEA_REGOFS 0x00000c /* offset */ +#define SH7750_TEA_REGOFS 0x00000c /* offset */ #define SH7750_TEA SH7750_P4_REG32(SH7750_TEA_REGOFS) #define SH7750_TEA_A7 SH7750_A7_REG32(SH7750_TEA_REGOFS) /* MMU control register - MMUCR */ -#define SH7750_MMUCR_REGOFS 0x000010 /* offset */ +#define SH7750_MMUCR_REGOFS 0x000010 /* offset */ #define SH7750_MMUCR SH7750_P4_REG32(SH7750_MMUCR_REGOFS) #define SH7750_MMUCR_A7 SH7750_A7_REG32(SH7750_MMUCR_REGOFS) -#define SH7750_MMUCR_AT 0x00000001 /* Address translation bit */ -#define SH7750_MMUCR_TI 0x00000004 /* TLB invalidate */ -#define SH7750_MMUCR_SV 0x00000100 /* Single Virtual Mode bit */ -#define SH7750_MMUCR_SQMD 0x00000200 /* Store Queue Mode bit */ -#define SH7750_MMUCR_URC 0x0000FC00 /* UTLB Replace Counter */ +#define SH7750_MMUCR_AT 0x00000001 /* Address translation bit */ +#define SH7750_MMUCR_TI 0x00000004 /* TLB invalidate */ +#define SH7750_MMUCR_SV 0x00000100 /* Single Virtual Mode bit */ +#define SH7750_MMUCR_SQMD 0x00000200 /* Store Queue Mode bit */ +#define SH7750_MMUCR_URC 0x0000FC00 /* UTLB Replace Counter */ #define SH7750_MMUCR_URC_S 10 -#define SH7750_MMUCR_URB 0x00FC0000 /* UTLB Replace Boundary */ +#define SH7750_MMUCR_URB 0x00FC0000 /* UTLB Replace Boundary */ #define SH7750_MMUCR_URB_S 18 -#define SH7750_MMUCR_LRUI 0xFC000000 /* Least Recently Used ITLB */ +#define SH7750_MMUCR_LRUI 0xFC000000 /* Least Recently Used ITLB */ #define SH7750_MMUCR_LRUI_S 26 @@ -145,30 +145,30 @@ */ /* Cache Control Register - CCR */ -#define SH7750_CCR_REGOFS 0x00001c /* offset */ +#define SH7750_CCR_REGOFS 0x00001c /* offset */ #define SH7750_CCR SH7750_P4_REG32(SH7750_CCR_REGOFS) #define SH7750_CCR_A7 SH7750_A7_REG32(SH7750_CCR_REGOFS) -#define SH7750_CCR_IIX 0x00008000 /* IC index enable bit */ -#define SH7750_CCR_ICI 0x00000800 /* IC invalidation bit: - set it to clear IC */ -#define SH7750_CCR_ICE 0x00000100 /* IC enable bit */ -#define SH7750_CCR_OIX 0x00000080 /* OC index enable bit */ -#define SH7750_CCR_ORA 0x00000020 /* OC RAM enable bit - if you set OCE = 0, - you should set ORA = 0 */ -#define SH7750_CCR_OCI 0x00000008 /* OC invalidation bit */ -#define SH7750_CCR_CB 0x00000004 /* Copy-back bit for P1 area */ -#define SH7750_CCR_WT 0x00000002 /* Write-through bit for P0,U0,P3 area */ -#define SH7750_CCR_OCE 0x00000001 /* OC enable bit */ +#define SH7750_CCR_IIX 0x00008000 /* IC index enable bit */ +#define SH7750_CCR_ICI 0x00000800 /* IC invalidation bit: + set it to clear IC */ +#define SH7750_CCR_ICE 0x00000100 /* IC enable bit */ +#define SH7750_CCR_OIX 0x00000080 /* OC index enable bit */ +#define SH7750_CCR_ORA 0x00000020 /* OC RAM enable bit + if you set OCE = 0, + you should set ORA = 0 */ +#define SH7750_CCR_OCI 0x00000008 /* OC invalidation bit */ +#define SH7750_CCR_CB 0x00000004 /* Copy-back bit for P1 area */ +#define SH7750_CCR_WT 0x00000002 /* Write-through bit for P0,U0,P3 area */ +#define SH7750_CCR_OCE 0x00000001 /* OC enable bit */ /* Queue address control register 0 - QACR0 */ -#define SH7750_QACR0_REGOFS 0x000038 /* offset */ +#define SH7750_QACR0_REGOFS 0x000038 /* offset */ #define SH7750_QACR0 SH7750_P4_REG32(SH7750_QACR0_REGOFS) #define SH7750_QACR0_A7 SH7750_A7_REG32(SH7750_QACR0_REGOFS) /* Queue address control register 1 - QACR1 */ -#define SH7750_QACR1_REGOFS 0x00003c /* offset */ +#define SH7750_QACR1_REGOFS 0x00003c /* offset */ #define SH7750_QACR1 SH7750_P4_REG32(SH7750_QACR1_REGOFS) #define SH7750_QACR1_A7 SH7750_A7_REG32(SH7750_QACR1_REGOFS) @@ -178,11 +178,11 @@ */ /* Immediate data for TRAPA instruction - TRA */ -#define SH7750_TRA_REGOFS 0x000020 /* offset */ +#define SH7750_TRA_REGOFS 0x000020 /* offset */ #define SH7750_TRA SH7750_P4_REG32(SH7750_TRA_REGOFS) #define SH7750_TRA_A7 SH7750_A7_REG32(SH7750_TRA_REGOFS) -#define SH7750_TRA_IMM 0x000003fd /* Immediate data operand */ +#define SH7750_TRA_IMM 0x000003fd /* Immediate data operand */ #define SH7750_TRA_IMM_S 2 /* Exeption event register - EXPEVT */ @@ -190,14 +190,14 @@ #define SH7750_EXPEVT SH7750_P4_REG32(SH7750_EXPEVT_REGOFS) #define SH7750_EXPEVT_A7 SH7750_A7_REG32(SH7750_EXPEVT_REGOFS) -#define SH7750_EXPEVT_EX 0x00000fff /* Exeption code */ +#define SH7750_EXPEVT_EX 0x00000fff /* Exeption code */ #define SH7750_EXPEVT_EX_S 0 /* Interrupt event register */ #define SH7750_INTEVT_REGOFS 0x000028 #define SH7750_INTEVT SH7750_P4_REG32(SH7750_INTEVT_REGOFS) #define SH7750_INTEVT_A7 SH7750_A7_REG32(SH7750_INTEVT_REGOFS) -#define SH7750_INTEVT_EX 0x00000fff /* Exeption code */ +#define SH7750_INTEVT_EX 0x00000fff /* Exeption code */ #define SH7750_INTEVT_EX_S 0 /* @@ -206,683 +206,683 @@ #define SH7750_EVT_TO_NUM(evt) ((evt) >> 5) /* Reset exception category */ -#define SH7750_EVT_POWER_ON_RST 0x000 /* Power-on reset */ -#define SH7750_EVT_MANUAL_RST 0x020 /* Manual reset */ -#define SH7750_EVT_TLB_MULT_HIT 0x140 /* TLB multiple-hit exception */ +#define SH7750_EVT_POWER_ON_RST 0x000 /* Power-on reset */ +#define SH7750_EVT_MANUAL_RST 0x020 /* Manual reset */ +#define SH7750_EVT_TLB_MULT_HIT 0x140 /* TLB multiple-hit exception */ /* General exception category */ -#define SH7750_EVT_USER_BREAK 0x1E0 /* User break */ -#define SH7750_EVT_IADDR_ERR 0x0E0 /* Instruction address error */ -#define SH7750_EVT_TLB_READ_MISS 0x040 /* ITLB miss exception / - DTLB miss exception (read) */ -#define SH7750_EVT_TLB_READ_PROTV 0x0A0 /* ITLB protection violation / - DTLB protection violation (read) */ -#define SH7750_EVT_ILLEGAL_INSTR 0x180 /* General Illegal Instruction - exception */ -#define SH7750_EVT_SLOT_ILLEGAL_INSTR 0x1A0 /* Slot Illegal Instruction - exception */ -#define SH7750_EVT_FPU_DISABLE 0x800 /* General FPU disable exception */ -#define SH7750_EVT_SLOT_FPU_DISABLE 0x820 /* Slot FPU disable exception */ -#define SH7750_EVT_DATA_READ_ERR 0x0E0 /* Data address error (read) */ -#define SH7750_EVT_DATA_WRITE_ERR 0x100 /* Data address error (write) */ -#define SH7750_EVT_DTLB_WRITE_MISS 0x060 /* DTLB miss exception (write) */ -#define SH7750_EVT_DTLB_WRITE_PROTV 0x0C0 /* DTLB protection violation - exception (write) */ -#define SH7750_EVT_FPU_EXCEPTION 0x120 /* FPU exception */ -#define SH7750_EVT_INITIAL_PGWRITE 0x080 /* Initial Page Write exception */ -#define SH7750_EVT_TRAPA 0x160 /* Unconditional trap (TRAPA) */ +#define SH7750_EVT_USER_BREAK 0x1E0 /* User break */ +#define SH7750_EVT_IADDR_ERR 0x0E0 /* Instruction address error */ +#define SH7750_EVT_TLB_READ_MISS 0x040 /* ITLB miss exception / + DTLB miss exception (read) */ +#define SH7750_EVT_TLB_READ_PROTV 0x0A0 /* ITLB protection violation / + DTLB protection violation (read) */ +#define SH7750_EVT_ILLEGAL_INSTR 0x180 /* General Illegal Instruction + exception */ +#define SH7750_EVT_SLOT_ILLEGAL_INSTR 0x1A0 /* Slot Illegal Instruction + exception */ +#define SH7750_EVT_FPU_DISABLE 0x800 /* General FPU disable exception */ +#define SH7750_EVT_SLOT_FPU_DISABLE 0x820 /* Slot FPU disable exception */ +#define SH7750_EVT_DATA_READ_ERR 0x0E0 /* Data address error (read) */ +#define SH7750_EVT_DATA_WRITE_ERR 0x100 /* Data address error (write) */ +#define SH7750_EVT_DTLB_WRITE_MISS 0x060 /* DTLB miss exception (write) */ +#define SH7750_EVT_DTLB_WRITE_PROTV 0x0C0 /* DTLB protection violation + exception (write) */ +#define SH7750_EVT_FPU_EXCEPTION 0x120 /* FPU exception */ +#define SH7750_EVT_INITIAL_PGWRITE 0x080 /* Initial Page Write exception */ +#define SH7750_EVT_TRAPA 0x160 /* Unconditional trap (TRAPA) */ /* Interrupt exception category */ -#define SH7750_EVT_NMI 0x1C0 /* Non-maskable interrupt */ -#define SH7750_EVT_IRQ0 0x200 /* External Interrupt 0 */ -#define SH7750_EVT_IRQ1 0x220 /* External Interrupt 1 */ -#define SH7750_EVT_IRQ2 0x240 /* External Interrupt 2 */ -#define SH7750_EVT_IRQ3 0x260 /* External Interrupt 3 */ -#define SH7750_EVT_IRQ4 0x280 /* External Interrupt 4 */ -#define SH7750_EVT_IRQ5 0x2A0 /* External Interrupt 5 */ -#define SH7750_EVT_IRQ6 0x2C0 /* External Interrupt 6 */ -#define SH7750_EVT_IRQ7 0x2E0 /* External Interrupt 7 */ -#define SH7750_EVT_IRQ8 0x300 /* External Interrupt 8 */ -#define SH7750_EVT_IRQ9 0x320 /* External Interrupt 9 */ -#define SH7750_EVT_IRQA 0x340 /* External Interrupt A */ -#define SH7750_EVT_IRQB 0x360 /* External Interrupt B */ -#define SH7750_EVT_IRQC 0x380 /* External Interrupt C */ -#define SH7750_EVT_IRQD 0x3A0 /* External Interrupt D */ -#define SH7750_EVT_IRQE 0x3C0 /* External Interrupt E */ +#define SH7750_EVT_NMI 0x1C0 /* Non-maskable interrupt */ +#define SH7750_EVT_IRQ0 0x200 /* External Interrupt 0 */ +#define SH7750_EVT_IRQ1 0x220 /* External Interrupt 1 */ +#define SH7750_EVT_IRQ2 0x240 /* External Interrupt 2 */ +#define SH7750_EVT_IRQ3 0x260 /* External Interrupt 3 */ +#define SH7750_EVT_IRQ4 0x280 /* External Interrupt 4 */ +#define SH7750_EVT_IRQ5 0x2A0 /* External Interrupt 5 */ +#define SH7750_EVT_IRQ6 0x2C0 /* External Interrupt 6 */ +#define SH7750_EVT_IRQ7 0x2E0 /* External Interrupt 7 */ +#define SH7750_EVT_IRQ8 0x300 /* External Interrupt 8 */ +#define SH7750_EVT_IRQ9 0x320 /* External Interrupt 9 */ +#define SH7750_EVT_IRQA 0x340 /* External Interrupt A */ +#define SH7750_EVT_IRQB 0x360 /* External Interrupt B */ +#define SH7750_EVT_IRQC 0x380 /* External Interrupt C */ +#define SH7750_EVT_IRQD 0x3A0 /* External Interrupt D */ +#define SH7750_EVT_IRQE 0x3C0 /* External Interrupt E */ /* Peripheral Module Interrupts - Timer Unit (TMU) */ -#define SH7750_EVT_TUNI0 0x400 /* TMU Underflow Interrupt 0 */ -#define SH7750_EVT_TUNI1 0x420 /* TMU Underflow Interrupt 1 */ -#define SH7750_EVT_TUNI2 0x440 /* TMU Underflow Interrupt 2 */ -#define SH7750_EVT_TICPI2 0x460 /* TMU Input Capture Interrupt 2 */ +#define SH7750_EVT_TUNI0 0x400 /* TMU Underflow Interrupt 0 */ +#define SH7750_EVT_TUNI1 0x420 /* TMU Underflow Interrupt 1 */ +#define SH7750_EVT_TUNI2 0x440 /* TMU Underflow Interrupt 2 */ +#define SH7750_EVT_TICPI2 0x460 /* TMU Input Capture Interrupt 2 */ /* Peripheral Module Interrupts - Real-Time Clock (RTC) */ -#define SH7750_EVT_RTC_ATI 0x480 /* Alarm Interrupt Request */ -#define SH7750_EVT_RTC_PRI 0x4A0 /* Periodic Interrupt Request */ -#define SH7750_EVT_RTC_CUI 0x4C0 /* Carry Interrupt Request */ +#define SH7750_EVT_RTC_ATI 0x480 /* Alarm Interrupt Request */ +#define SH7750_EVT_RTC_PRI 0x4A0 /* Periodic Interrupt Request */ +#define SH7750_EVT_RTC_CUI 0x4C0 /* Carry Interrupt Request */ /* Peripheral Module Interrupts - Serial Communication Interface (SCI) */ -#define SH7750_EVT_SCI_ERI 0x4E0 /* Receive Error */ -#define SH7750_EVT_SCI_RXI 0x500 /* Receive Data Register Full */ -#define SH7750_EVT_SCI_TXI 0x520 /* Transmit Data Register Empty */ -#define SH7750_EVT_SCI_TEI 0x540 /* Transmit End */ +#define SH7750_EVT_SCI_ERI 0x4E0 /* Receive Error */ +#define SH7750_EVT_SCI_RXI 0x500 /* Receive Data Register Full */ +#define SH7750_EVT_SCI_TXI 0x520 /* Transmit Data Register Empty */ +#define SH7750_EVT_SCI_TEI 0x540 /* Transmit End */ /* Peripheral Module Interrupts - Watchdog Timer (WDT) */ -#define SH7750_EVT_WDT_ITI 0x560 /* Interval Timer Interrupt - (used when WDT operates in - interval timer mode) */ +#define SH7750_EVT_WDT_ITI 0x560 /* Interval Timer Interrupt + (used when WDT operates in + interval timer mode) */ /* Peripheral Module Interrupts - Memory Refresh Unit (REF) */ -#define SH7750_EVT_REF_RCMI 0x580 /* Compare-match Interrupt */ -#define SH7750_EVT_REF_ROVI 0x5A0 /* Refresh Counter Overflow - interrupt */ +#define SH7750_EVT_REF_RCMI 0x580 /* Compare-match Interrupt */ +#define SH7750_EVT_REF_ROVI 0x5A0 /* Refresh Counter Overflow + interrupt */ /* Peripheral Module Interrupts - Hitachi User Debug Interface (H-UDI) */ -#define SH7750_EVT_HUDI 0x600 /* UDI interrupt */ +#define SH7750_EVT_HUDI 0x600 /* UDI interrupt */ /* Peripheral Module Interrupts - General-Purpose I/O (GPIO) */ -#define SH7750_EVT_GPIO 0x620 /* GPIO Interrupt */ +#define SH7750_EVT_GPIO 0x620 /* GPIO Interrupt */ /* Peripheral Module Interrupts - DMA Controller (DMAC) */ -#define SH7750_EVT_DMAC_DMTE0 0x640 /* DMAC 0 Transfer End Interrupt */ -#define SH7750_EVT_DMAC_DMTE1 0x660 /* DMAC 1 Transfer End Interrupt */ -#define SH7750_EVT_DMAC_DMTE2 0x680 /* DMAC 2 Transfer End Interrupt */ -#define SH7750_EVT_DMAC_DMTE3 0x6A0 /* DMAC 3 Transfer End Interrupt */ -#define SH7750_EVT_DMAC_DMAE 0x6C0 /* DMAC Address Error Interrupt */ +#define SH7750_EVT_DMAC_DMTE0 0x640 /* DMAC 0 Transfer End Interrupt */ +#define SH7750_EVT_DMAC_DMTE1 0x660 /* DMAC 1 Transfer End Interrupt */ +#define SH7750_EVT_DMAC_DMTE2 0x680 /* DMAC 2 Transfer End Interrupt */ +#define SH7750_EVT_DMAC_DMTE3 0x6A0 /* DMAC 3 Transfer End Interrupt */ +#define SH7750_EVT_DMAC_DMAE 0x6C0 /* DMAC Address Error Interrupt */ /* Peripheral Module Interrupts - Serial Communication Interface with FIFO */ /* (SCIF) */ -#define SH7750_EVT_SCIF_ERI 0x700 /* Receive Error */ -#define SH7750_EVT_SCIF_RXI 0x720 /* Receive FIFO Data Full or - Receive Data ready interrupt */ -#define SH7750_EVT_SCIF_BRI 0x740 /* Break or overrun error */ -#define SH7750_EVT_SCIF_TXI 0x760 /* Transmit FIFO Data Empty */ +#define SH7750_EVT_SCIF_ERI 0x700 /* Receive Error */ +#define SH7750_EVT_SCIF_RXI 0x720 /* Receive FIFO Data Full or + Receive Data ready interrupt */ +#define SH7750_EVT_SCIF_BRI 0x740 /* Break or overrun error */ +#define SH7750_EVT_SCIF_TXI 0x760 /* Transmit FIFO Data Empty */ /* * Power Management */ -#define SH7750_STBCR_REGOFS 0xC00004 /* offset */ +#define SH7750_STBCR_REGOFS 0xC00004 /* offset */ #define SH7750_STBCR SH7750_P4_REG32(SH7750_STBCR_REGOFS) #define SH7750_STBCR_A7 SH7750_A7_REG32(SH7750_STBCR_REGOFS) -#define SH7750_STBCR_STBY 0x80 /* Specifies a transition to standby mode: - 0 - Transition to SLEEP mode on SLEEP - 1 - Transition to STANDBY mode on SLEEP */ -#define SH7750_STBCR_PHZ 0x40 /* State of peripheral module pins in - standby mode: - 0 - normal state - 1 - high-impendance state */ +#define SH7750_STBCR_STBY 0x80 /* Specifies a transition to standby mode: + 0 - Transition to SLEEP mode on SLEEP + 1 - Transition to STANDBY mode on SLEEP */ +#define SH7750_STBCR_PHZ 0x40 /* State of peripheral module pins in + standby mode: + 0 - normal state + 1 - high-impendance state */ -#define SH7750_STBCR_PPU 0x20 /* Peripheral module pins pull-up controls */ -#define SH7750_STBCR_MSTP4 0x10 /* Stopping the clock supply to DMAC */ +#define SH7750_STBCR_PPU 0x20 /* Peripheral module pins pull-up controls */ +#define SH7750_STBCR_MSTP4 0x10 /* Stopping the clock supply to DMAC */ #define SH7750_STBCR_DMAC_STP SH7750_STBCR_MSTP4 -#define SH7750_STBCR_MSTP3 0x08 /* Stopping the clock supply to SCIF */ +#define SH7750_STBCR_MSTP3 0x08 /* Stopping the clock supply to SCIF */ #define SH7750_STBCR_SCIF_STP SH7750_STBCR_MSTP3 -#define SH7750_STBCR_MSTP2 0x04 /* Stopping the clock supply to TMU */ +#define SH7750_STBCR_MSTP2 0x04 /* Stopping the clock supply to TMU */ #define SH7750_STBCR_TMU_STP SH7750_STBCR_MSTP2 -#define SH7750_STBCR_MSTP1 0x02 /* Stopping the clock supply to RTC */ +#define SH7750_STBCR_MSTP1 0x02 /* Stopping the clock supply to RTC */ #define SH7750_STBCR_RTC_STP SH7750_STBCR_MSTP1 -#define SH7750_STBCR_MSPT0 0x01 /* Stopping the clock supply to SCI */ +#define SH7750_STBCR_MSPT0 0x01 /* Stopping the clock supply to SCI */ #define SH7750_STBCR_SCI_STP SH7750_STBCR_MSTP0 #define SH7750_STBCR_STBY 0x80 -#define SH7750_STBCR2_REGOFS 0xC00010 /* offset */ +#define SH7750_STBCR2_REGOFS 0xC00010 /* offset */ #define SH7750_STBCR2 SH7750_P4_REG32(SH7750_STBCR2_REGOFS) #define SH7750_STBCR2_A7 SH7750_A7_REG32(SH7750_STBCR2_REGOFS) -#define SH7750_STBCR2_DSLP 0x80 /* Specifies transition to deep sleep mode: - 0 - transition to sleep or standby mode - as it is specified in STBY bit - 1 - transition to deep sleep mode on - execution of SLEEP instruction */ -#define SH7750_STBCR2_MSTP6 0x02 /* Stopping the clock supply to Store Queue - in the cache controller */ +#define SH7750_STBCR2_DSLP 0x80 /* Specifies transition to deep sleep mode: + 0 - transition to sleep or standby mode + as it is specified in STBY bit + 1 - transition to deep sleep mode on + execution of SLEEP instruction */ +#define SH7750_STBCR2_MSTP6 0x02 /* Stopping the clock supply to Store Queue + in the cache controller */ #define SH7750_STBCR2_SQ_STP SH7750_STBCR2_MSTP6 -#define SH7750_STBCR2_MSTP5 0x01 /* Stopping the clock supply to the User - Break Controller (UBC) */ +#define SH7750_STBCR2_MSTP5 0x01 /* Stopping the clock supply to the User + Break Controller (UBC) */ #define SH7750_STBCR2_UBC_STP SH7750_STBCR2_MSTP5 /* * Clock Pulse Generator (CPG) */ -#define SH7750_FRQCR_REGOFS 0xC00000 /* offset */ +#define SH7750_FRQCR_REGOFS 0xC00000 /* offset */ #define SH7750_FRQCR SH7750_P4_REG32(SH7750_FRQCR_REGOFS) #define SH7750_FRQCR_A7 SH7750_A7_REG32(SH7750_FRQCR_REGOFS) -#define SH7750_FRQCR_CKOEN 0x0800 /* Clock Output Enable - 0 - CKIO pin goes to HiZ/pullup - 1 - Clock is output from CKIO */ -#define SH7750_FRQCR_PLL1EN 0x0400 /* PLL circuit 1 enable */ -#define SH7750_FRQCR_PLL2EN 0x0200 /* PLL circuit 2 enable */ +#define SH7750_FRQCR_CKOEN 0x0800 /* Clock Output Enable + 0 - CKIO pin goes to HiZ/pullup + 1 - Clock is output from CKIO */ +#define SH7750_FRQCR_PLL1EN 0x0400 /* PLL circuit 1 enable */ +#define SH7750_FRQCR_PLL2EN 0x0200 /* PLL circuit 2 enable */ -#define SH7750_FRQCR_IFC 0x01C0 /* CPU clock frequency division ratio: */ -#define SH7750_FRQCR_IFCDIV1 0x0000 /* 0 - * 1 */ -#define SH7750_FRQCR_IFCDIV2 0x0040 /* 1 - * 1/2 */ -#define SH7750_FRQCR_IFCDIV3 0x0080 /* 2 - * 1/3 */ -#define SH7750_FRQCR_IFCDIV4 0x00C0 /* 3 - * 1/4 */ -#define SH7750_FRQCR_IFCDIV6 0x0100 /* 4 - * 1/6 */ -#define SH7750_FRQCR_IFCDIV8 0x0140 /* 5 - * 1/8 */ +#define SH7750_FRQCR_IFC 0x01C0 /* CPU clock frequency division ratio: */ +#define SH7750_FRQCR_IFCDIV1 0x0000 /* 0 - * 1 */ +#define SH7750_FRQCR_IFCDIV2 0x0040 /* 1 - * 1/2 */ +#define SH7750_FRQCR_IFCDIV3 0x0080 /* 2 - * 1/3 */ +#define SH7750_FRQCR_IFCDIV4 0x00C0 /* 3 - * 1/4 */ +#define SH7750_FRQCR_IFCDIV6 0x0100 /* 4 - * 1/6 */ +#define SH7750_FRQCR_IFCDIV8 0x0140 /* 5 - * 1/8 */ -#define SH7750_FRQCR_BFC 0x0038 /* Bus clock frequency division ratio: */ -#define SH7750_FRQCR_BFCDIV1 0x0000 /* 0 - * 1 */ -#define SH7750_FRQCR_BFCDIV2 0x0008 /* 1 - * 1/2 */ -#define SH7750_FRQCR_BFCDIV3 0x0010 /* 2 - * 1/3 */ -#define SH7750_FRQCR_BFCDIV4 0x0018 /* 3 - * 1/4 */ -#define SH7750_FRQCR_BFCDIV6 0x0020 /* 4 - * 1/6 */ -#define SH7750_FRQCR_BFCDIV8 0x0028 /* 5 - * 1/8 */ +#define SH7750_FRQCR_BFC 0x0038 /* Bus clock frequency division ratio: */ +#define SH7750_FRQCR_BFCDIV1 0x0000 /* 0 - * 1 */ +#define SH7750_FRQCR_BFCDIV2 0x0008 /* 1 - * 1/2 */ +#define SH7750_FRQCR_BFCDIV3 0x0010 /* 2 - * 1/3 */ +#define SH7750_FRQCR_BFCDIV4 0x0018 /* 3 - * 1/4 */ +#define SH7750_FRQCR_BFCDIV6 0x0020 /* 4 - * 1/6 */ +#define SH7750_FRQCR_BFCDIV8 0x0028 /* 5 - * 1/8 */ -#define SH7750_FRQCR_PFC 0x0007 /* Peripheral module clock frequency - division ratio: */ -#define SH7750_FRQCR_PFCDIV2 0x0000 /* 0 - * 1/2 */ -#define SH7750_FRQCR_PFCDIV3 0x0001 /* 1 - * 1/3 */ -#define SH7750_FRQCR_PFCDIV4 0x0002 /* 2 - * 1/4 */ -#define SH7750_FRQCR_PFCDIV6 0x0003 /* 3 - * 1/6 */ -#define SH7750_FRQCR_PFCDIV8 0x0004 /* 4 - * 1/8 */ +#define SH7750_FRQCR_PFC 0x0007 /* Peripheral module clock frequency + division ratio: */ +#define SH7750_FRQCR_PFCDIV2 0x0000 /* 0 - * 1/2 */ +#define SH7750_FRQCR_PFCDIV3 0x0001 /* 1 - * 1/3 */ +#define SH7750_FRQCR_PFCDIV4 0x0002 /* 2 - * 1/4 */ +#define SH7750_FRQCR_PFCDIV6 0x0003 /* 3 - * 1/6 */ +#define SH7750_FRQCR_PFCDIV8 0x0004 /* 4 - * 1/8 */ /* * Watchdog Timer (WDT) */ /* Watchdog Timer Counter register - WTCNT */ -#define SH7750_WTCNT_REGOFS 0xC00008 /* offset */ +#define SH7750_WTCNT_REGOFS 0xC00008 /* offset */ #define SH7750_WTCNT SH7750_P4_REG32(SH7750_WTCNT_REGOFS) #define SH7750_WTCNT_A7 SH7750_A7_REG32(SH7750_WTCNT_REGOFS) -#define SH7750_WTCNT_KEY 0x5A00 /* When WTCNT byte register written, - you have to set the upper byte to - 0x5A */ +#define SH7750_WTCNT_KEY 0x5A00 /* When WTCNT byte register written, + you have to set the upper byte to + 0x5A */ /* Watchdog Timer Control/Status register - WTCSR */ -#define SH7750_WTCSR_REGOFS 0xC0000C /* offset */ +#define SH7750_WTCSR_REGOFS 0xC0000C /* offset */ #define SH7750_WTCSR SH7750_P4_REG32(SH7750_WTCSR_REGOFS) #define SH7750_WTCSR_A7 SH7750_A7_REG32(SH7750_WTCSR_REGOFS) -#define SH7750_WTCSR_KEY 0xA500 /* When WTCSR byte register written, - you have to set the upper byte to - 0xA5 */ -#define SH7750_WTCSR_TME 0x80 /* Timer enable (1-upcount start) */ -#define SH7750_WTCSR_MODE 0x40 /* Timer Mode Select: */ -#define SH7750_WTCSR_MODE_WT 0x40 /* Watchdog Timer Mode */ -#define SH7750_WTCSR_MODE_IT 0x00 /* Interval Timer Mode */ -#define SH7750_WTCSR_RSTS 0x20 /* Reset Select: */ -#define SH7750_WTCSR_RST_MAN 0x20 /* Manual Reset */ -#define SH7750_WTCSR_RST_PWR 0x00 /* Power-on Reset */ -#define SH7750_WTCSR_WOVF 0x10 /* Watchdog Timer Overflow Flag */ -#define SH7750_WTCSR_IOVF 0x08 /* Interval Timer Overflow Flag */ -#define SH7750_WTCSR_CKS 0x07 /* Clock Select: */ -#define SH7750_WTCSR_CKS_DIV32 0x00 /* 1/32 of frequency divider 2 input */ -#define SH7750_WTCSR_CKS_DIV64 0x01 /* 1/64 */ -#define SH7750_WTCSR_CKS_DIV128 0x02 /* 1/128 */ -#define SH7750_WTCSR_CKS_DIV256 0x03 /* 1/256 */ -#define SH7750_WTCSR_CKS_DIV512 0x04 /* 1/512 */ -#define SH7750_WTCSR_CKS_DIV1024 0x05 /* 1/1024 */ -#define SH7750_WTCSR_CKS_DIV2048 0x06 /* 1/2048 */ -#define SH7750_WTCSR_CKS_DIV4096 0x07 /* 1/4096 */ +#define SH7750_WTCSR_KEY 0xA500 /* When WTCSR byte register written, + you have to set the upper byte to + 0xA5 */ +#define SH7750_WTCSR_TME 0x80 /* Timer enable (1-upcount start) */ +#define SH7750_WTCSR_MODE 0x40 /* Timer Mode Select: */ +#define SH7750_WTCSR_MODE_WT 0x40 /* Watchdog Timer Mode */ +#define SH7750_WTCSR_MODE_IT 0x00 /* Interval Timer Mode */ +#define SH7750_WTCSR_RSTS 0x20 /* Reset Select: */ +#define SH7750_WTCSR_RST_MAN 0x20 /* Manual Reset */ +#define SH7750_WTCSR_RST_PWR 0x00 /* Power-on Reset */ +#define SH7750_WTCSR_WOVF 0x10 /* Watchdog Timer Overflow Flag */ +#define SH7750_WTCSR_IOVF 0x08 /* Interval Timer Overflow Flag */ +#define SH7750_WTCSR_CKS 0x07 /* Clock Select: */ +#define SH7750_WTCSR_CKS_DIV32 0x00 /* 1/32 of frequency divider 2 input */ +#define SH7750_WTCSR_CKS_DIV64 0x01 /* 1/64 */ +#define SH7750_WTCSR_CKS_DIV128 0x02 /* 1/128 */ +#define SH7750_WTCSR_CKS_DIV256 0x03 /* 1/256 */ +#define SH7750_WTCSR_CKS_DIV512 0x04 /* 1/512 */ +#define SH7750_WTCSR_CKS_DIV1024 0x05 /* 1/1024 */ +#define SH7750_WTCSR_CKS_DIV2048 0x06 /* 1/2048 */ +#define SH7750_WTCSR_CKS_DIV4096 0x07 /* 1/4096 */ /* * Real-Time Clock (RTC) */ /* 64-Hz Counter Register (byte, read-only) - R64CNT */ -#define SH7750_R64CNT_REGOFS 0xC80000 /* offset */ +#define SH7750_R64CNT_REGOFS 0xC80000 /* offset */ #define SH7750_R64CNT SH7750_P4_REG32(SH7750_R64CNT_REGOFS) #define SH7750_R64CNT_A7 SH7750_A7_REG32(SH7750_R64CNT_REGOFS) /* Second Counter Register (byte, BCD-coded) - RSECCNT */ -#define SH7750_RSECCNT_REGOFS 0xC80004 /* offset */ +#define SH7750_RSECCNT_REGOFS 0xC80004 /* offset */ #define SH7750_RSECCNT SH7750_P4_REG32(SH7750_RSECCNT_REGOFS) #define SH7750_RSECCNT_A7 SH7750_A7_REG32(SH7750_RSECCNT_REGOFS) /* Minute Counter Register (byte, BCD-coded) - RMINCNT */ -#define SH7750_RMINCNT_REGOFS 0xC80008 /* offset */ +#define SH7750_RMINCNT_REGOFS 0xC80008 /* offset */ #define SH7750_RMINCNT SH7750_P4_REG32(SH7750_RMINCNT_REGOFS) #define SH7750_RMINCNT_A7 SH7750_A7_REG32(SH7750_RMINCNT_REGOFS) /* Hour Counter Register (byte, BCD-coded) - RHRCNT */ -#define SH7750_RHRCNT_REGOFS 0xC8000C /* offset */ +#define SH7750_RHRCNT_REGOFS 0xC8000C /* offset */ #define SH7750_RHRCNT SH7750_P4_REG32(SH7750_RHRCNT_REGOFS) #define SH7750_RHRCNT_A7 SH7750_A7_REG32(SH7750_RHRCNT_REGOFS) /* Day-of-Week Counter Register (byte) - RWKCNT */ -#define SH7750_RWKCNT_REGOFS 0xC80010 /* offset */ +#define SH7750_RWKCNT_REGOFS 0xC80010 /* offset */ #define SH7750_RWKCNT SH7750_P4_REG32(SH7750_RWKCNT_REGOFS) #define SH7750_RWKCNT_A7 SH7750_A7_REG32(SH7750_RWKCNT_REGOFS) -#define SH7750_RWKCNT_SUN 0 /* Sunday */ -#define SH7750_RWKCNT_MON 1 /* Monday */ -#define SH7750_RWKCNT_TUE 2 /* Tuesday */ -#define SH7750_RWKCNT_WED 3 /* Wednesday */ -#define SH7750_RWKCNT_THU 4 /* Thursday */ -#define SH7750_RWKCNT_FRI 5 /* Friday */ -#define SH7750_RWKCNT_SAT 6 /* Saturday */ +#define SH7750_RWKCNT_SUN 0 /* Sunday */ +#define SH7750_RWKCNT_MON 1 /* Monday */ +#define SH7750_RWKCNT_TUE 2 /* Tuesday */ +#define SH7750_RWKCNT_WED 3 /* Wednesday */ +#define SH7750_RWKCNT_THU 4 /* Thursday */ +#define SH7750_RWKCNT_FRI 5 /* Friday */ +#define SH7750_RWKCNT_SAT 6 /* Saturday */ /* Day Counter Register (byte, BCD-coded) - RDAYCNT */ -#define SH7750_RDAYCNT_REGOFS 0xC80014 /* offset */ +#define SH7750_RDAYCNT_REGOFS 0xC80014 /* offset */ #define SH7750_RDAYCNT SH7750_P4_REG32(SH7750_RDAYCNT_REGOFS) #define SH7750_RDAYCNT_A7 SH7750_A7_REG32(SH7750_RDAYCNT_REGOFS) /* Month Counter Register (byte, BCD-coded) - RMONCNT */ -#define SH7750_RMONCNT_REGOFS 0xC80018 /* offset */ +#define SH7750_RMONCNT_REGOFS 0xC80018 /* offset */ #define SH7750_RMONCNT SH7750_P4_REG32(SH7750_RMONCNT_REGOFS) #define SH7750_RMONCNT_A7 SH7750_A7_REG32(SH7750_RMONCNT_REGOFS) /* Year Counter Register (half, BCD-coded) - RYRCNT */ -#define SH7750_RYRCNT_REGOFS 0xC8001C /* offset */ +#define SH7750_RYRCNT_REGOFS 0xC8001C /* offset */ #define SH7750_RYRCNT SH7750_P4_REG32(SH7750_RYRCNT_REGOFS) #define SH7750_RYRCNT_A7 SH7750_A7_REG32(SH7750_RYRCNT_REGOFS) /* Second Alarm Register (byte, BCD-coded) - RSECAR */ -#define SH7750_RSECAR_REGOFS 0xC80020 /* offset */ +#define SH7750_RSECAR_REGOFS 0xC80020 /* offset */ #define SH7750_RSECAR SH7750_P4_REG32(SH7750_RSECAR_REGOFS) #define SH7750_RSECAR_A7 SH7750_A7_REG32(SH7750_RSECAR_REGOFS) -#define SH7750_RSECAR_ENB 0x80 /* Second Alarm Enable */ +#define SH7750_RSECAR_ENB 0x80 /* Second Alarm Enable */ /* Minute Alarm Register (byte, BCD-coded) - RMINAR */ -#define SH7750_RMINAR_REGOFS 0xC80024 /* offset */ +#define SH7750_RMINAR_REGOFS 0xC80024 /* offset */ #define SH7750_RMINAR SH7750_P4_REG32(SH7750_RMINAR_REGOFS) #define SH7750_RMINAR_A7 SH7750_A7_REG32(SH7750_RMINAR_REGOFS) -#define SH7750_RMINAR_ENB 0x80 /* Minute Alarm Enable */ +#define SH7750_RMINAR_ENB 0x80 /* Minute Alarm Enable */ /* Hour Alarm Register (byte, BCD-coded) - RHRAR */ -#define SH7750_RHRAR_REGOFS 0xC80028 /* offset */ +#define SH7750_RHRAR_REGOFS 0xC80028 /* offset */ #define SH7750_RHRAR SH7750_P4_REG32(SH7750_RHRAR_REGOFS) #define SH7750_RHRAR_A7 SH7750_A7_REG32(SH7750_RHRAR_REGOFS) -#define SH7750_RHRAR_ENB 0x80 /* Hour Alarm Enable */ +#define SH7750_RHRAR_ENB 0x80 /* Hour Alarm Enable */ /* Day-of-Week Alarm Register (byte) - RWKAR */ -#define SH7750_RWKAR_REGOFS 0xC8002C /* offset */ +#define SH7750_RWKAR_REGOFS 0xC8002C /* offset */ #define SH7750_RWKAR SH7750_P4_REG32(SH7750_RWKAR_REGOFS) #define SH7750_RWKAR_A7 SH7750_A7_REG32(SH7750_RWKAR_REGOFS) -#define SH7750_RWKAR_ENB 0x80 /* Day-of-week Alarm Enable */ +#define SH7750_RWKAR_ENB 0x80 /* Day-of-week Alarm Enable */ -#define SH7750_RWKAR_SUN 0 /* Sunday */ -#define SH7750_RWKAR_MON 1 /* Monday */ -#define SH7750_RWKAR_TUE 2 /* Tuesday */ -#define SH7750_RWKAR_WED 3 /* Wednesday */ -#define SH7750_RWKAR_THU 4 /* Thursday */ -#define SH7750_RWKAR_FRI 5 /* Friday */ -#define SH7750_RWKAR_SAT 6 /* Saturday */ +#define SH7750_RWKAR_SUN 0 /* Sunday */ +#define SH7750_RWKAR_MON 1 /* Monday */ +#define SH7750_RWKAR_TUE 2 /* Tuesday */ +#define SH7750_RWKAR_WED 3 /* Wednesday */ +#define SH7750_RWKAR_THU 4 /* Thursday */ +#define SH7750_RWKAR_FRI 5 /* Friday */ +#define SH7750_RWKAR_SAT 6 /* Saturday */ /* Day Alarm Register (byte, BCD-coded) - RDAYAR */ -#define SH7750_RDAYAR_REGOFS 0xC80030 /* offset */ +#define SH7750_RDAYAR_REGOFS 0xC80030 /* offset */ #define SH7750_RDAYAR SH7750_P4_REG32(SH7750_RDAYAR_REGOFS) #define SH7750_RDAYAR_A7 SH7750_A7_REG32(SH7750_RDAYAR_REGOFS) -#define SH7750_RDAYAR_ENB 0x80 /* Day Alarm Enable */ +#define SH7750_RDAYAR_ENB 0x80 /* Day Alarm Enable */ /* Month Counter Register (byte, BCD-coded) - RMONAR */ -#define SH7750_RMONAR_REGOFS 0xC80034 /* offset */ +#define SH7750_RMONAR_REGOFS 0xC80034 /* offset */ #define SH7750_RMONAR SH7750_P4_REG32(SH7750_RMONAR_REGOFS) #define SH7750_RMONAR_A7 SH7750_A7_REG32(SH7750_RMONAR_REGOFS) -#define SH7750_RMONAR_ENB 0x80 /* Month Alarm Enable */ +#define SH7750_RMONAR_ENB 0x80 /* Month Alarm Enable */ /* RTC Control Register 1 (byte) - RCR1 */ -#define SH7750_RCR1_REGOFS 0xC80038 /* offset */ +#define SH7750_RCR1_REGOFS 0xC80038 /* offset */ #define SH7750_RCR1 SH7750_P4_REG32(SH7750_RCR1_REGOFS) #define SH7750_RCR1_A7 SH7750_A7_REG32(SH7750_RCR1_REGOFS) -#define SH7750_RCR1_CF 0x80 /* Carry Flag */ -#define SH7750_RCR1_CIE 0x10 /* Carry Interrupt Enable */ -#define SH7750_RCR1_AIE 0x08 /* Alarm Interrupt Enable */ -#define SH7750_RCR1_AF 0x01 /* Alarm Flag */ +#define SH7750_RCR1_CF 0x80 /* Carry Flag */ +#define SH7750_RCR1_CIE 0x10 /* Carry Interrupt Enable */ +#define SH7750_RCR1_AIE 0x08 /* Alarm Interrupt Enable */ +#define SH7750_RCR1_AF 0x01 /* Alarm Flag */ /* RTC Control Register 2 (byte) - RCR2 */ -#define SH7750_RCR2_REGOFS 0xC8003C /* offset */ +#define SH7750_RCR2_REGOFS 0xC8003C /* offset */ #define SH7750_RCR2 SH7750_P4_REG32(SH7750_RCR2_REGOFS) #define SH7750_RCR2_A7 SH7750_A7_REG32(SH7750_RCR2_REGOFS) -#define SH7750_RCR2_PEF 0x80 /* Periodic Interrupt Flag */ -#define SH7750_RCR2_PES 0x70 /* Periodic Interrupt Enable: */ -#define SH7750_RCR2_PES_DIS 0x00 /* Periodic Interrupt Disabled */ -#define SH7750_RCR2_PES_DIV256 0x10 /* Generated at 1/256 sec interval */ -#define SH7750_RCR2_PES_DIV64 0x20 /* Generated at 1/64 sec interval */ -#define SH7750_RCR2_PES_DIV16 0x30 /* Generated at 1/16 sec interval */ -#define SH7750_RCR2_PES_DIV4 0x40 /* Generated at 1/4 sec interval */ -#define SH7750_RCR2_PES_DIV2 0x50 /* Generated at 1/2 sec interval */ -#define SH7750_RCR2_PES_x1 0x60 /* Generated at 1 sec interval */ -#define SH7750_RCR2_PES_x2 0x70 /* Generated at 2 sec interval */ -#define SH7750_RCR2_RTCEN 0x08 /* RTC Crystal Oscillator is Operated */ -#define SH7750_RCR2_ADJ 0x04 /* 30-Second Adjastment */ -#define SH7750_RCR2_RESET 0x02 /* Frequency divider circuits are reset */ -#define SH7750_RCR2_START 0x01 /* 0 - sec, min, hr, day-of-week, month, - year counters are stopped - 1 - sec, min, hr, day-of-week, month, - year counters operate normally */ +#define SH7750_RCR2_PEF 0x80 /* Periodic Interrupt Flag */ +#define SH7750_RCR2_PES 0x70 /* Periodic Interrupt Enable: */ +#define SH7750_RCR2_PES_DIS 0x00 /* Periodic Interrupt Disabled */ +#define SH7750_RCR2_PES_DIV256 0x10 /* Generated at 1/256 sec interval */ +#define SH7750_RCR2_PES_DIV64 0x20 /* Generated at 1/64 sec interval */ +#define SH7750_RCR2_PES_DIV16 0x30 /* Generated at 1/16 sec interval */ +#define SH7750_RCR2_PES_DIV4 0x40 /* Generated at 1/4 sec interval */ +#define SH7750_RCR2_PES_DIV2 0x50 /* Generated at 1/2 sec interval */ +#define SH7750_RCR2_PES_x1 0x60 /* Generated at 1 sec interval */ +#define SH7750_RCR2_PES_x2 0x70 /* Generated at 2 sec interval */ +#define SH7750_RCR2_RTCEN 0x08 /* RTC Crystal Oscillator is Operated */ +#define SH7750_RCR2_ADJ 0x04 /* 30-Second Adjastment */ +#define SH7750_RCR2_RESET 0x02 /* Frequency divider circuits are reset */ +#define SH7750_RCR2_START 0x01 /* 0 - sec, min, hr, day-of-week, month, + year counters are stopped + 1 - sec, min, hr, day-of-week, month, + year counters operate normally */ /* * Bus State Controller - BSC */ /* Bus Control Register 1 - BCR1 */ -#define SH7750_BCR1_REGOFS 0x800000 /* offset */ +#define SH7750_BCR1_REGOFS 0x800000 /* offset */ #define SH7750_BCR1 SH7750_P4_REG32(SH7750_BCR1_REGOFS) #define SH7750_BCR1_A7 SH7750_A7_REG32(SH7750_BCR1_REGOFS) -#define SH7750_BCR1_ENDIAN 0x80000000 /* Endianness (1 - little endian) */ -#define SH7750_BCR1_MASTER 0x40000000 /* Master/Slave mode (1-master) */ -#define SH7750_BCR1_A0MPX 0x20000000 /* Area 0 Memory Type (0-SRAM,1-MPX) */ -#define SH7750_BCR1_IPUP 0x02000000 /* Input Pin Pull-up Control: - 0 - pull-up resistor is on for - control input pins - 1 - pull-up resistor is off */ -#define SH7750_BCR1_OPUP 0x01000000 /* Output Pin Pull-up Control: - 0 - pull-up resistor is on for - control output pins - 1 - pull-up resistor is off */ -#define SH7750_BCR1_A1MBC 0x00200000 /* Area 1 SRAM Byte Control Mode: - 0 - Area 1 SRAM is set to - normal mode - 1 - Area 1 SRAM is set to byte - control mode */ -#define SH7750_BCR1_A4MBC 0x00100000 /* Area 4 SRAM Byte Control Mode: - 0 - Area 4 SRAM is set to - normal mode - 1 - Area 4 SRAM is set to byte - control mode */ -#define SH7750_BCR1_BREQEN 0x00080000 /* BREQ Enable: - 0 - External requests are not - accepted - 1 - External requests are - accepted */ -#define SH7750_BCR1_PSHR 0x00040000 /* Partial Sharing Bit: - 0 - Master Mode - 1 - Partial-sharing Mode */ -#define SH7750_BCR1_MEMMPX 0x00020000 /* Area 1 to 6 MPX Interface: - 0 - SRAM/burst ROM interface - 1 - MPX interface */ -#define SH7750_BCR1_HIZMEM 0x00008000 /* High Impendance Control. Specifies - the state of A[25:0], BS\, CSn\, - RD/WR\, CE2A\, CE2B\ in standby - mode and when bus is released: - 0 - signals go to High-Z mode - 1 - signals driven */ -#define SH7750_BCR1_HIZCNT 0x00004000 /* High Impendance Control. Specifies - the state of the RAS\, RAS2\, WEn\, - CASn\, DQMn, RD\, CASS\, FRAME\, - RD2\ signals in standby mode and - when bus is released: - 0 - signals go to High-Z mode - 1 - signals driven */ -#define SH7750_BCR1_A0BST 0x00003800 /* Area 0 Burst ROM Control */ -#define SH7750_BCR1_A0BST_SRAM 0x0000 /* Area 0 accessed as SRAM i/f */ -#define SH7750_BCR1_A0BST_ROM4 0x0800 /* Area 0 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM8 0x1000 /* Area 0 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM16 0x1800 /* Area 0 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM32 0x2000 /* Area 0 accessed as burst ROM - interface, 32 cosequtive access */ +#define SH7750_BCR1_ENDIAN 0x80000000 /* Endianness (1 - little endian) */ +#define SH7750_BCR1_MASTER 0x40000000 /* Master/Slave mode (1-master) */ +#define SH7750_BCR1_A0MPX 0x20000000 /* Area 0 Memory Type (0-SRAM,1-MPX) */ +#define SH7750_BCR1_IPUP 0x02000000 /* Input Pin Pull-up Control: + 0 - pull-up resistor is on for + control input pins + 1 - pull-up resistor is off */ +#define SH7750_BCR1_OPUP 0x01000000 /* Output Pin Pull-up Control: + 0 - pull-up resistor is on for + control output pins + 1 - pull-up resistor is off */ +#define SH7750_BCR1_A1MBC 0x00200000 /* Area 1 SRAM Byte Control Mode: + 0 - Area 1 SRAM is set to + normal mode + 1 - Area 1 SRAM is set to byte + control mode */ +#define SH7750_BCR1_A4MBC 0x00100000 /* Area 4 SRAM Byte Control Mode: + 0 - Area 4 SRAM is set to + normal mode + 1 - Area 4 SRAM is set to byte + control mode */ +#define SH7750_BCR1_BREQEN 0x00080000 /* BREQ Enable: + 0 - External requests are not + accepted + 1 - External requests are + accepted */ +#define SH7750_BCR1_PSHR 0x00040000 /* Partial Sharing Bit: + 0 - Master Mode + 1 - Partial-sharing Mode */ +#define SH7750_BCR1_MEMMPX 0x00020000 /* Area 1 to 6 MPX Interface: + 0 - SRAM/burst ROM interface + 1 - MPX interface */ +#define SH7750_BCR1_HIZMEM 0x00008000 /* High Impendance Control. Specifies + the state of A[25:0], BS\, CSn\, + RD/WR\, CE2A\, CE2B\ in standby + mode and when bus is released: + 0 - signals go to High-Z mode + 1 - signals driven */ +#define SH7750_BCR1_HIZCNT 0x00004000 /* High Impendance Control. Specifies + the state of the RAS\, RAS2\, WEn\, + CASn\, DQMn, RD\, CASS\, FRAME\, + RD2\ signals in standby mode and + when bus is released: + 0 - signals go to High-Z mode + 1 - signals driven */ +#define SH7750_BCR1_A0BST 0x00003800 /* Area 0 Burst ROM Control */ +#define SH7750_BCR1_A0BST_SRAM 0x0000 /* Area 0 accessed as SRAM i/f */ +#define SH7750_BCR1_A0BST_ROM4 0x0800 /* Area 0 accessed as burst ROM + interface, 4 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM8 0x1000 /* Area 0 accessed as burst ROM + interface, 8 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM16 0x1800 /* Area 0 accessed as burst ROM + interface, 16 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM32 0x2000 /* Area 0 accessed as burst ROM + interface, 32 cosequtive access */ -#define SH7750_BCR1_A5BST 0x00000700 /* Area 5 Burst ROM Control */ -#define SH7750_BCR1_A5BST_SRAM 0x0000 /* Area 5 accessed as SRAM i/f */ -#define SH7750_BCR1_A5BST_ROM4 0x0100 /* Area 5 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM8 0x0200 /* Area 5 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM16 0x0300 /* Area 5 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM32 0x0400 /* Area 5 accessed as burst ROM - interface, 32 cosequtive access */ +#define SH7750_BCR1_A5BST 0x00000700 /* Area 5 Burst ROM Control */ +#define SH7750_BCR1_A5BST_SRAM 0x0000 /* Area 5 accessed as SRAM i/f */ +#define SH7750_BCR1_A5BST_ROM4 0x0100 /* Area 5 accessed as burst ROM + interface, 4 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM8 0x0200 /* Area 5 accessed as burst ROM + interface, 8 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM16 0x0300 /* Area 5 accessed as burst ROM + interface, 16 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM32 0x0400 /* Area 5 accessed as burst ROM + interface, 32 cosequtive access */ -#define SH7750_BCR1_A6BST 0x000000E0 /* Area 6 Burst ROM Control */ -#define SH7750_BCR1_A6BST_SRAM 0x0000 /* Area 6 accessed as SRAM i/f */ -#define SH7750_BCR1_A6BST_ROM4 0x0020 /* Area 6 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM8 0x0040 /* Area 6 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM16 0x0060 /* Area 6 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM32 0x0080 /* Area 6 accessed as burst ROM - interface, 32 cosequtive access */ +#define SH7750_BCR1_A6BST 0x000000E0 /* Area 6 Burst ROM Control */ +#define SH7750_BCR1_A6BST_SRAM 0x0000 /* Area 6 accessed as SRAM i/f */ +#define SH7750_BCR1_A6BST_ROM4 0x0020 /* Area 6 accessed as burst ROM + interface, 4 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM8 0x0040 /* Area 6 accessed as burst ROM + interface, 8 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM16 0x0060 /* Area 6 accessed as burst ROM + interface, 16 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM32 0x0080 /* Area 6 accessed as burst ROM + interface, 32 cosequtive access */ -#define SH7750_BCR1_DRAMTP 0x001C /* Area 2 and 3 Memory Type */ -#define SH7750_BCR1_DRAMTP_2SRAM_3SRAM 0x0000 /* Area 2 and 3 are SRAM or MPX - interface. */ -#define SH7750_BCR1_DRAMTP_2SRAM_3SDRAM 0x0008 /* Area 2 - SRAM/MPX, Area 3 - - synchronous DRAM */ -#define SH7750_BCR1_DRAMTP_2SDRAM_3SDRAM 0x000C /* Area 2 and 3 are synchronous - DRAM interface */ -#define SH7750_BCR1_DRAMTP_2SRAM_3DRAM 0x0010 /* Area 2 - SRAM/MPX, Area 3 - - DRAM interface */ -#define SH7750_BCR1_DRAMTP_2DRAM_3DRAM 0x0014 /* Area 2 and 3 are DRAM - interface */ +#define SH7750_BCR1_DRAMTP 0x001C /* Area 2 and 3 Memory Type */ +#define SH7750_BCR1_DRAMTP_2SRAM_3SRAM 0x0000 /* Area 2 and 3 are SRAM or MPX + interface. */ +#define SH7750_BCR1_DRAMTP_2SRAM_3SDRAM 0x0008 /* Area 2 - SRAM/MPX, Area 3 - + synchronous DRAM */ +#define SH7750_BCR1_DRAMTP_2SDRAM_3SDRAM 0x000C /* Area 2 and 3 are synchronous + DRAM interface */ +#define SH7750_BCR1_DRAMTP_2SRAM_3DRAM 0x0010 /* Area 2 - SRAM/MPX, Area 3 - + DRAM interface */ +#define SH7750_BCR1_DRAMTP_2DRAM_3DRAM 0x0014 /* Area 2 and 3 are DRAM + interface */ -#define SH7750_BCR1_A56PCM 0x00000001 /* Area 5 and 6 Bus Type: - 0 - SRAM interface - 1 - PCMCIA interface */ +#define SH7750_BCR1_A56PCM 0x00000001 /* Area 5 and 6 Bus Type: + 0 - SRAM interface + 1 - PCMCIA interface */ /* Bus Control Register 2 (half) - BCR2 */ -#define SH7750_BCR2_REGOFS 0x800004 /* offset */ +#define SH7750_BCR2_REGOFS 0x800004 /* offset */ #define SH7750_BCR2 SH7750_P4_REG32(SH7750_BCR2_REGOFS) #define SH7750_BCR2_A7 SH7750_A7_REG32(SH7750_BCR2_REGOFS) -#define SH7750_BCR2_A0SZ 0xC000 /* Area 0 Bus Width */ +#define SH7750_BCR2_A0SZ 0xC000 /* Area 0 Bus Width */ #define SH7750_BCR2_A0SZ_S 14 -#define SH7750_BCR2_A6SZ 0x3000 /* Area 6 Bus Width */ +#define SH7750_BCR2_A6SZ 0x3000 /* Area 6 Bus Width */ #define SH7750_BCR2_A6SZ_S 12 -#define SH7750_BCR2_A5SZ 0x0C00 /* Area 5 Bus Width */ +#define SH7750_BCR2_A5SZ 0x0C00 /* Area 5 Bus Width */ #define SH7750_BCR2_A5SZ_S 10 -#define SH7750_BCR2_A4SZ 0x0300 /* Area 4 Bus Width */ +#define SH7750_BCR2_A4SZ 0x0300 /* Area 4 Bus Width */ #define SH7750_BCR2_A4SZ_S 8 -#define SH7750_BCR2_A3SZ 0x00C0 /* Area 3 Bus Width */ +#define SH7750_BCR2_A3SZ 0x00C0 /* Area 3 Bus Width */ #define SH7750_BCR2_A3SZ_S 6 -#define SH7750_BCR2_A2SZ 0x0030 /* Area 2 Bus Width */ +#define SH7750_BCR2_A2SZ 0x0030 /* Area 2 Bus Width */ #define SH7750_BCR2_A2SZ_S 4 -#define SH7750_BCR2_A1SZ 0x000C /* Area 1 Bus Width */ +#define SH7750_BCR2_A1SZ 0x000C /* Area 1 Bus Width */ #define SH7750_BCR2_A1SZ_S 2 -#define SH7750_BCR2_SZ_64 0 /* 64 bits */ -#define SH7750_BCR2_SZ_8 1 /* 8 bits */ -#define SH7750_BCR2_SZ_16 2 /* 16 bits */ -#define SH7750_BCR2_SZ_32 3 /* 32 bits */ -#define SH7750_BCR2_PORTEN 0x0001 /* Port Function Enable : - 0 - D51-D32 are not used as a port - 1 - D51-D32 are used as a port */ +#define SH7750_BCR2_SZ_64 0 /* 64 bits */ +#define SH7750_BCR2_SZ_8 1 /* 8 bits */ +#define SH7750_BCR2_SZ_16 2 /* 16 bits */ +#define SH7750_BCR2_SZ_32 3 /* 32 bits */ +#define SH7750_BCR2_PORTEN 0x0001 /* Port Function Enable : + 0 - D51-D32 are not used as a port + 1 - D51-D32 are used as a port */ /* Wait Control Register 1 - WCR1 */ -#define SH7750_WCR1_REGOFS 0x800008 /* offset */ +#define SH7750_WCR1_REGOFS 0x800008 /* offset */ #define SH7750_WCR1 SH7750_P4_REG32(SH7750_WCR1_REGOFS) #define SH7750_WCR1_A7 SH7750_A7_REG32(SH7750_WCR1_REGOFS) -#define SH7750_WCR1_DMAIW 0x70000000 /* DACK Device Inter-Cycle Idle - specification */ +#define SH7750_WCR1_DMAIW 0x70000000 /* DACK Device Inter-Cycle Idle + specification */ #define SH7750_WCR1_DMAIW_S 28 -#define SH7750_WCR1_A6IW 0x07000000 /* Area 6 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A6IW 0x07000000 /* Area 6 Inter-Cycle Idle spec. */ #define SH7750_WCR1_A6IW_S 24 -#define SH7750_WCR1_A5IW 0x00700000 /* Area 5 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A5IW 0x00700000 /* Area 5 Inter-Cycle Idle spec. */ #define SH7750_WCR1_A5IW_S 20 -#define SH7750_WCR1_A4IW 0x00070000 /* Area 4 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A4IW 0x00070000 /* Area 4 Inter-Cycle Idle spec. */ #define SH7750_WCR1_A4IW_S 16 -#define SH7750_WCR1_A3IW 0x00007000 /* Area 3 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A3IW 0x00007000 /* Area 3 Inter-Cycle Idle spec. */ #define SH7750_WCR1_A3IW_S 12 -#define SH7750_WCR1_A2IW 0x00000700 /* Area 2 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A2IW 0x00000700 /* Area 2 Inter-Cycle Idle spec. */ #define SH7750_WCR1_A2IW_S 8 -#define SH7750_WCR1_A1IW 0x00000070 /* Area 1 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A1IW 0x00000070 /* Area 1 Inter-Cycle Idle spec. */ #define SH7750_WCR1_A1IW_S 4 -#define SH7750_WCR1_A0IW 0x00000007 /* Area 0 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A0IW 0x00000007 /* Area 0 Inter-Cycle Idle spec. */ #define SH7750_WCR1_A0IW_S 0 /* Wait Control Register 2 - WCR2 */ -#define SH7750_WCR2_REGOFS 0x80000C /* offset */ +#define SH7750_WCR2_REGOFS 0x80000C /* offset */ #define SH7750_WCR2 SH7750_P4_REG32(SH7750_WCR2_REGOFS) #define SH7750_WCR2_A7 SH7750_A7_REG32(SH7750_WCR2_REGOFS) -#define SH7750_WCR2_A6W 0xE0000000 /* Area 6 Wait Control */ +#define SH7750_WCR2_A6W 0xE0000000 /* Area 6 Wait Control */ #define SH7750_WCR2_A6W_S 29 -#define SH7750_WCR2_A6B 0x1C000000 /* Area 6 Burst Pitch */ +#define SH7750_WCR2_A6B 0x1C000000 /* Area 6 Burst Pitch */ #define SH7750_WCR2_A6B_S 26 -#define SH7750_WCR2_A5W 0x03800000 /* Area 5 Wait Control */ +#define SH7750_WCR2_A5W 0x03800000 /* Area 5 Wait Control */ #define SH7750_WCR2_A5W_S 23 -#define SH7750_WCR2_A5B 0x00700000 /* Area 5 Burst Pitch */ +#define SH7750_WCR2_A5B 0x00700000 /* Area 5 Burst Pitch */ #define SH7750_WCR2_A5B_S 20 -#define SH7750_WCR2_A4W 0x000E0000 /* Area 4 Wait Control */ +#define SH7750_WCR2_A4W 0x000E0000 /* Area 4 Wait Control */ #define SH7750_WCR2_A4W_S 17 -#define SH7750_WCR2_A3W 0x0000E000 /* Area 3 Wait Control */ +#define SH7750_WCR2_A3W 0x0000E000 /* Area 3 Wait Control */ #define SH7750_WCR2_A3W_S 13 -#define SH7750_WCR2_A2W 0x00000E00 /* Area 2 Wait Control */ +#define SH7750_WCR2_A2W 0x00000E00 /* Area 2 Wait Control */ #define SH7750_WCR2_A2W_S 9 -#define SH7750_WCR2_A1W 0x000001C0 /* Area 1 Wait Control */ +#define SH7750_WCR2_A1W 0x000001C0 /* Area 1 Wait Control */ #define SH7750_WCR2_A1W_S 6 -#define SH7750_WCR2_A0W 0x00000038 /* Area 0 Wait Control */ +#define SH7750_WCR2_A0W 0x00000038 /* Area 0 Wait Control */ #define SH7750_WCR2_A0W_S 3 -#define SH7750_WCR2_A0B 0x00000007 /* Area 0 Burst Pitch */ +#define SH7750_WCR2_A0B 0x00000007 /* Area 0 Burst Pitch */ #define SH7750_WCR2_A0B_S 0 -#define SH7750_WCR2_WS0 0 /* 0 wait states inserted */ -#define SH7750_WCR2_WS1 1 /* 1 wait states inserted */ -#define SH7750_WCR2_WS2 2 /* 2 wait states inserted */ -#define SH7750_WCR2_WS3 3 /* 3 wait states inserted */ -#define SH7750_WCR2_WS6 4 /* 6 wait states inserted */ -#define SH7750_WCR2_WS9 5 /* 9 wait states inserted */ -#define SH7750_WCR2_WS12 6 /* 12 wait states inserted */ -#define SH7750_WCR2_WS15 7 /* 15 wait states inserted */ +#define SH7750_WCR2_WS0 0 /* 0 wait states inserted */ +#define SH7750_WCR2_WS1 1 /* 1 wait states inserted */ +#define SH7750_WCR2_WS2 2 /* 2 wait states inserted */ +#define SH7750_WCR2_WS3 3 /* 3 wait states inserted */ +#define SH7750_WCR2_WS6 4 /* 6 wait states inserted */ +#define SH7750_WCR2_WS9 5 /* 9 wait states inserted */ +#define SH7750_WCR2_WS12 6 /* 12 wait states inserted */ +#define SH7750_WCR2_WS15 7 /* 15 wait states inserted */ -#define SH7750_WCR2_BPWS0 0 /* 0 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS1 1 /* 1 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS2 2 /* 2 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS3 3 /* 3 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS4 4 /* 4 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS5 5 /* 5 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS6 6 /* 6 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS7 7 /* 7 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS0 0 /* 0 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS1 1 /* 1 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS2 2 /* 2 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS3 3 /* 3 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS4 4 /* 4 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS5 5 /* 5 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS6 6 /* 6 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS7 7 /* 7 wait states inserted from 2nd access */ /* DRAM CAS\ Assertion Delay (area 3,2) */ -#define SH7750_WCR2_DRAM_CAS_ASW1 0 /* 1 cycle */ -#define SH7750_WCR2_DRAM_CAS_ASW2 1 /* 2 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW3 2 /* 3 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW4 3 /* 4 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW7 4 /* 7 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW10 5 /* 10 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW13 6 /* 13 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW16 7 /* 16 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW1 0 /* 1 cycle */ +#define SH7750_WCR2_DRAM_CAS_ASW2 1 /* 2 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW3 2 /* 3 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW4 3 /* 4 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW7 4 /* 7 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW10 5 /* 10 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW13 6 /* 13 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW16 7 /* 16 cycles */ /* SDRAM CAS\ Latency Cycles */ -#define SH7750_WCR2_SDRAM_CAS_LAT1 1 /* 1 cycle */ -#define SH7750_WCR2_SDRAM_CAS_LAT2 2 /* 2 cycles */ -#define SH7750_WCR2_SDRAM_CAS_LAT3 3 /* 3 cycles */ -#define SH7750_WCR2_SDRAM_CAS_LAT4 4 /* 4 cycles */ -#define SH7750_WCR2_SDRAM_CAS_LAT5 5 /* 5 cycles */ +#define SH7750_WCR2_SDRAM_CAS_LAT1 1 /* 1 cycle */ +#define SH7750_WCR2_SDRAM_CAS_LAT2 2 /* 2 cycles */ +#define SH7750_WCR2_SDRAM_CAS_LAT3 3 /* 3 cycles */ +#define SH7750_WCR2_SDRAM_CAS_LAT4 4 /* 4 cycles */ +#define SH7750_WCR2_SDRAM_CAS_LAT5 5 /* 5 cycles */ /* Wait Control Register 3 - WCR3 */ -#define SH7750_WCR3_REGOFS 0x800010 /* offset */ +#define SH7750_WCR3_REGOFS 0x800010 /* offset */ #define SH7750_WCR3 SH7750_P4_REG32(SH7750_WCR3_REGOFS) #define SH7750_WCR3_A7 SH7750_A7_REG32(SH7750_WCR3_REGOFS) -#define SH7750_WCR3_A6S 0x04000000 /* Area 6 Write Strobe Setup time */ -#define SH7750_WCR3_A6H 0x03000000 /* Area 6 Data Hold Time */ +#define SH7750_WCR3_A6S 0x04000000 /* Area 6 Write Strobe Setup time */ +#define SH7750_WCR3_A6H 0x03000000 /* Area 6 Data Hold Time */ #define SH7750_WCR3_A6H_S 24 -#define SH7750_WCR3_A5S 0x00400000 /* Area 5 Write Strobe Setup time */ -#define SH7750_WCR3_A5H 0x00300000 /* Area 5 Data Hold Time */ +#define SH7750_WCR3_A5S 0x00400000 /* Area 5 Write Strobe Setup time */ +#define SH7750_WCR3_A5H 0x00300000 /* Area 5 Data Hold Time */ #define SH7750_WCR3_A5H_S 20 -#define SH7750_WCR3_A4S 0x00040000 /* Area 4 Write Strobe Setup time */ -#define SH7750_WCR3_A4H 0x00030000 /* Area 4 Data Hold Time */ +#define SH7750_WCR3_A4S 0x00040000 /* Area 4 Write Strobe Setup time */ +#define SH7750_WCR3_A4H 0x00030000 /* Area 4 Data Hold Time */ #define SH7750_WCR3_A4H_S 16 -#define SH7750_WCR3_A3S 0x00004000 /* Area 3 Write Strobe Setup time */ -#define SH7750_WCR3_A3H 0x00003000 /* Area 3 Data Hold Time */ +#define SH7750_WCR3_A3S 0x00004000 /* Area 3 Write Strobe Setup time */ +#define SH7750_WCR3_A3H 0x00003000 /* Area 3 Data Hold Time */ #define SH7750_WCR3_A3H_S 12 -#define SH7750_WCR3_A2S 0x00000400 /* Area 2 Write Strobe Setup time */ -#define SH7750_WCR3_A2H 0x00000300 /* Area 2 Data Hold Time */ +#define SH7750_WCR3_A2S 0x00000400 /* Area 2 Write Strobe Setup time */ +#define SH7750_WCR3_A2H 0x00000300 /* Area 2 Data Hold Time */ #define SH7750_WCR3_A2H_S 8 -#define SH7750_WCR3_A1S 0x00000040 /* Area 1 Write Strobe Setup time */ -#define SH7750_WCR3_A1H 0x00000030 /* Area 1 Data Hold Time */ +#define SH7750_WCR3_A1S 0x00000040 /* Area 1 Write Strobe Setup time */ +#define SH7750_WCR3_A1H 0x00000030 /* Area 1 Data Hold Time */ #define SH7750_WCR3_A1H_S 4 -#define SH7750_WCR3_A0S 0x00000004 /* Area 0 Write Strobe Setup time */ -#define SH7750_WCR3_A0H 0x00000003 /* Area 0 Data Hold Time */ +#define SH7750_WCR3_A0S 0x00000004 /* Area 0 Write Strobe Setup time */ +#define SH7750_WCR3_A0H 0x00000003 /* Area 0 Data Hold Time */ #define SH7750_WCR3_A0H_S 0 -#define SH7750_WCR3_DHWS_0 0 /* 0 wait states data hold time */ -#define SH7750_WCR3_DHWS_1 1 /* 1 wait states data hold time */ -#define SH7750_WCR3_DHWS_2 2 /* 2 wait states data hold time */ -#define SH7750_WCR3_DHWS_3 3 /* 3 wait states data hold time */ +#define SH7750_WCR3_DHWS_0 0 /* 0 wait states data hold time */ +#define SH7750_WCR3_DHWS_1 1 /* 1 wait states data hold time */ +#define SH7750_WCR3_DHWS_2 2 /* 2 wait states data hold time */ +#define SH7750_WCR3_DHWS_3 3 /* 3 wait states data hold time */ -#define SH7750_MCR_REGOFS 0x800014 /* offset */ +#define SH7750_MCR_REGOFS 0x800014 /* offset */ #define SH7750_MCR SH7750_P4_REG32(SH7750_MCR_REGOFS) #define SH7750_MCR_A7 SH7750_A7_REG32(SH7750_MCR_REGOFS) -#define SH7750_MCR_RASD 0x80000000 /* RAS Down mode */ -#define SH7750_MCR_MRSET 0x40000000 /* SDRAM Mode Register Set */ -#define SH7750_MCR_PALL 0x00000000 /* SDRAM Precharge All cmd. Mode */ -#define SH7750_MCR_TRC 0x38000000 /* RAS Precharge Time at End of - Refresh: */ -#define SH7750_MCR_TRC_0 0x00000000 /* 0 */ -#define SH7750_MCR_TRC_3 0x08000000 /* 3 */ -#define SH7750_MCR_TRC_6 0x10000000 /* 6 */ -#define SH7750_MCR_TRC_9 0x18000000 /* 9 */ -#define SH7750_MCR_TRC_12 0x20000000 /* 12 */ -#define SH7750_MCR_TRC_15 0x28000000 /* 15 */ -#define SH7750_MCR_TRC_18 0x30000000 /* 18 */ -#define SH7750_MCR_TRC_21 0x38000000 /* 21 */ +#define SH7750_MCR_RASD 0x80000000 /* RAS Down mode */ +#define SH7750_MCR_MRSET 0x40000000 /* SDRAM Mode Register Set */ +#define SH7750_MCR_PALL 0x00000000 /* SDRAM Precharge All cmd. Mode */ +#define SH7750_MCR_TRC 0x38000000 /* RAS Precharge Time at End of + Refresh: */ +#define SH7750_MCR_TRC_0 0x00000000 /* 0 */ +#define SH7750_MCR_TRC_3 0x08000000 /* 3 */ +#define SH7750_MCR_TRC_6 0x10000000 /* 6 */ +#define SH7750_MCR_TRC_9 0x18000000 /* 9 */ +#define SH7750_MCR_TRC_12 0x20000000 /* 12 */ +#define SH7750_MCR_TRC_15 0x28000000 /* 15 */ +#define SH7750_MCR_TRC_18 0x30000000 /* 18 */ +#define SH7750_MCR_TRC_21 0x38000000 /* 21 */ -#define SH7750_MCR_TCAS 0x00800000 /* CAS Negation Period */ -#define SH7750_MCR_TCAS_1 0x00000000 /* 1 */ -#define SH7750_MCR_TCAS_2 0x00800000 /* 2 */ +#define SH7750_MCR_TCAS 0x00800000 /* CAS Negation Period */ +#define SH7750_MCR_TCAS_1 0x00000000 /* 1 */ +#define SH7750_MCR_TCAS_2 0x00800000 /* 2 */ -#define SH7750_MCR_TPC 0x00380000 /* DRAM: RAS Precharge Period - SDRAM: minimum number of cycles - until the next bank active cmd - is output after precharging */ +#define SH7750_MCR_TPC 0x00380000 /* DRAM: RAS Precharge Period + SDRAM: minimum number of cycles + until the next bank active cmd + is output after precharging */ #define SH7750_MCR_TPC_S 19 -#define SH7750_MCR_TPC_SDRAM_1 0x00000000 /* 1 cycle */ -#define SH7750_MCR_TPC_SDRAM_2 0x00080000 /* 2 cycles */ -#define SH7750_MCR_TPC_SDRAM_3 0x00100000 /* 3 cycles */ -#define SH7750_MCR_TPC_SDRAM_4 0x00180000 /* 4 cycles */ -#define SH7750_MCR_TPC_SDRAM_5 0x00200000 /* 5 cycles */ -#define SH7750_MCR_TPC_SDRAM_6 0x00280000 /* 6 cycles */ -#define SH7750_MCR_TPC_SDRAM_7 0x00300000 /* 7 cycles */ -#define SH7750_MCR_TPC_SDRAM_8 0x00380000 /* 8 cycles */ +#define SH7750_MCR_TPC_SDRAM_1 0x00000000 /* 1 cycle */ +#define SH7750_MCR_TPC_SDRAM_2 0x00080000 /* 2 cycles */ +#define SH7750_MCR_TPC_SDRAM_3 0x00100000 /* 3 cycles */ +#define SH7750_MCR_TPC_SDRAM_4 0x00180000 /* 4 cycles */ +#define SH7750_MCR_TPC_SDRAM_5 0x00200000 /* 5 cycles */ +#define SH7750_MCR_TPC_SDRAM_6 0x00280000 /* 6 cycles */ +#define SH7750_MCR_TPC_SDRAM_7 0x00300000 /* 7 cycles */ +#define SH7750_MCR_TPC_SDRAM_8 0x00380000 /* 8 cycles */ -#define SH7750_MCR_RCD 0x00030000 /* DRAM: RAS-CAS Assertion Delay time - SDRAM: bank active-read/write cmd - delay time */ -#define SH7750_MCR_RCD_DRAM_2 0x00000000 /* DRAM delay 2 clocks */ -#define SH7750_MCR_RCD_DRAM_3 0x00010000 /* DRAM delay 3 clocks */ -#define SH7750_MCR_RCD_DRAM_4 0x00020000 /* DRAM delay 4 clocks */ -#define SH7750_MCR_RCD_DRAM_5 0x00030000 /* DRAM delay 5 clocks */ -#define SH7750_MCR_RCD_SDRAM_2 0x00010000 /* DRAM delay 2 clocks */ -#define SH7750_MCR_RCD_SDRAM_3 0x00020000 /* DRAM delay 3 clocks */ -#define SH7750_MCR_RCD_SDRAM_4 0x00030000 /* DRAM delay 4 clocks */ +#define SH7750_MCR_RCD 0x00030000 /* DRAM: RAS-CAS Assertion Delay time + SDRAM: bank active-read/write cmd + delay time */ +#define SH7750_MCR_RCD_DRAM_2 0x00000000 /* DRAM delay 2 clocks */ +#define SH7750_MCR_RCD_DRAM_3 0x00010000 /* DRAM delay 3 clocks */ +#define SH7750_MCR_RCD_DRAM_4 0x00020000 /* DRAM delay 4 clocks */ +#define SH7750_MCR_RCD_DRAM_5 0x00030000 /* DRAM delay 5 clocks */ +#define SH7750_MCR_RCD_SDRAM_2 0x00010000 /* DRAM delay 2 clocks */ +#define SH7750_MCR_RCD_SDRAM_3 0x00020000 /* DRAM delay 3 clocks */ +#define SH7750_MCR_RCD_SDRAM_4 0x00030000 /* DRAM delay 4 clocks */ -#define SH7750_MCR_TRWL 0x0000E000 /* SDRAM Write Precharge Delay */ -#define SH7750_MCR_TRWL_1 0x00000000 /* 1 */ -#define SH7750_MCR_TRWL_2 0x00002000 /* 2 */ -#define SH7750_MCR_TRWL_3 0x00004000 /* 3 */ -#define SH7750_MCR_TRWL_4 0x00006000 /* 4 */ -#define SH7750_MCR_TRWL_5 0x00008000 /* 5 */ +#define SH7750_MCR_TRWL 0x0000E000 /* SDRAM Write Precharge Delay */ +#define SH7750_MCR_TRWL_1 0x00000000 /* 1 */ +#define SH7750_MCR_TRWL_2 0x00002000 /* 2 */ +#define SH7750_MCR_TRWL_3 0x00004000 /* 3 */ +#define SH7750_MCR_TRWL_4 0x00006000 /* 4 */ +#define SH7750_MCR_TRWL_5 0x00008000 /* 5 */ -#define SH7750_MCR_TRAS 0x00001C00 /* DRAM: CAS-Before-RAS Refresh RAS - asserting period - SDRAM: Command interval after - synchronous DRAM refresh */ -#define SH7750_MCR_TRAS_DRAM_2 0x00000000 /* 2 */ -#define SH7750_MCR_TRAS_DRAM_3 0x00000400 /* 3 */ -#define SH7750_MCR_TRAS_DRAM_4 0x00000800 /* 4 */ -#define SH7750_MCR_TRAS_DRAM_5 0x00000C00 /* 5 */ -#define SH7750_MCR_TRAS_DRAM_6 0x00001000 /* 6 */ -#define SH7750_MCR_TRAS_DRAM_7 0x00001400 /* 7 */ -#define SH7750_MCR_TRAS_DRAM_8 0x00001800 /* 8 */ -#define SH7750_MCR_TRAS_DRAM_9 0x00001C00 /* 9 */ +#define SH7750_MCR_TRAS 0x00001C00 /* DRAM: CAS-Before-RAS Refresh RAS + asserting period + SDRAM: Command interval after + synchronous DRAM refresh */ +#define SH7750_MCR_TRAS_DRAM_2 0x00000000 /* 2 */ +#define SH7750_MCR_TRAS_DRAM_3 0x00000400 /* 3 */ +#define SH7750_MCR_TRAS_DRAM_4 0x00000800 /* 4 */ +#define SH7750_MCR_TRAS_DRAM_5 0x00000C00 /* 5 */ +#define SH7750_MCR_TRAS_DRAM_6 0x00001000 /* 6 */ +#define SH7750_MCR_TRAS_DRAM_7 0x00001400 /* 7 */ +#define SH7750_MCR_TRAS_DRAM_8 0x00001800 /* 8 */ +#define SH7750_MCR_TRAS_DRAM_9 0x00001C00 /* 9 */ -#define SH7750_MCR_TRAS_SDRAM_TRC_4 0x00000000 /* 4 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_5 0x00000400 /* 5 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_6 0x00000800 /* 6 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_7 0x00000C00 /* 7 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_8 0x00001000 /* 8 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_9 0x00001400 /* 9 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_10 0x00001800 /* 10 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_11 0x00001C00 /* 11 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_4 0x00000000 /* 4 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_5 0x00000400 /* 5 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_6 0x00000800 /* 6 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_7 0x00000C00 /* 7 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_8 0x00001000 /* 8 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_9 0x00001400 /* 9 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_10 0x00001800 /* 10 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_11 0x00001C00 /* 11 + TRC */ -#define SH7750_MCR_BE 0x00000200 /* Burst Enable */ -#define SH7750_MCR_SZ 0x00000180 /* Memory Data Size */ -#define SH7750_MCR_SZ_64 0x00000000 /* 64 bits */ -#define SH7750_MCR_SZ_16 0x00000100 /* 16 bits */ -#define SH7750_MCR_SZ_32 0x00000180 /* 32 bits */ +#define SH7750_MCR_BE 0x00000200 /* Burst Enable */ +#define SH7750_MCR_SZ 0x00000180 /* Memory Data Size */ +#define SH7750_MCR_SZ_64 0x00000000 /* 64 bits */ +#define SH7750_MCR_SZ_16 0x00000100 /* 16 bits */ +#define SH7750_MCR_SZ_32 0x00000180 /* 32 bits */ -#define SH7750_MCR_AMX 0x00000078 /* Address Multiplexing */ +#define SH7750_MCR_AMX 0x00000078 /* Address Multiplexing */ #define SH7750_MCR_AMX_S 3 -#define SH7750_MCR_AMX_DRAM_8BIT_COL 0x00000000 /* 8-bit column addr */ -#define SH7750_MCR_AMX_DRAM_9BIT_COL 0x00000008 /* 9-bit column addr */ -#define SH7750_MCR_AMX_DRAM_10BIT_COL 0x00000010 /* 10-bit column addr */ -#define SH7750_MCR_AMX_DRAM_11BIT_COL 0x00000018 /* 11-bit column addr */ -#define SH7750_MCR_AMX_DRAM_12BIT_COL 0x00000020 /* 12-bit column addr */ +#define SH7750_MCR_AMX_DRAM_8BIT_COL 0x00000000 /* 8-bit column addr */ +#define SH7750_MCR_AMX_DRAM_9BIT_COL 0x00000008 /* 9-bit column addr */ +#define SH7750_MCR_AMX_DRAM_10BIT_COL 0x00000010 /* 10-bit column addr */ +#define SH7750_MCR_AMX_DRAM_11BIT_COL 0x00000018 /* 11-bit column addr */ +#define SH7750_MCR_AMX_DRAM_12BIT_COL 0x00000020 /* 12-bit column addr */ /* See SH7750 Hardware Manual for SDRAM address multiplexor selection */ -#define SH7750_MCR_RFSH 0x00000004 /* Refresh Control */ -#define SH7750_MCR_RMODE 0x00000002 /* Refresh Mode: */ -#define SH7750_MCR_RMODE_NORMAL 0x00000000 /* Normal Refresh Mode */ -#define SH7750_MCR_RMODE_SELF 0x00000002 /* Self-Refresh Mode */ -#define SH7750_MCR_RMODE_EDO 0x00000001 /* EDO Mode */ +#define SH7750_MCR_RFSH 0x00000004 /* Refresh Control */ +#define SH7750_MCR_RMODE 0x00000002 /* Refresh Mode: */ +#define SH7750_MCR_RMODE_NORMAL 0x00000000 /* Normal Refresh Mode */ +#define SH7750_MCR_RMODE_SELF 0x00000002 /* Self-Refresh Mode */ +#define SH7750_MCR_RMODE_EDO 0x00000001 /* EDO Mode */ /* SDRAM Mode Set address */ #define SH7750_SDRAM_MODE_A2_BASE 0xFF900000 @@ -894,119 +894,119 @@ /* PCMCIA Control Register (half) - PCR */ -#define SH7750_PCR_REGOFS 0x800018 /* offset */ +#define SH7750_PCR_REGOFS 0x800018 /* offset */ #define SH7750_PCR SH7750_P4_REG32(SH7750_PCR_REGOFS) #define SH7750_PCR_A7 SH7750_A7_REG32(SH7750_PCR_REGOFS) -#define SH7750_PCR_A5PCW 0xC000 /* Area 5 PCMCIA Wait - Number of wait - states to be added to the number of - waits specified by WCR2 in a low-speed - PCMCIA wait cycle */ -#define SH7750_PCR_A5PCW_0 0x0000 /* 0 waits inserted */ -#define SH7750_PCR_A5PCW_15 0x4000 /* 15 waits inserted */ -#define SH7750_PCR_A5PCW_30 0x8000 /* 30 waits inserted */ -#define SH7750_PCR_A5PCW_50 0xC000 /* 50 waits inserted */ +#define SH7750_PCR_A5PCW 0xC000 /* Area 5 PCMCIA Wait - Number of wait + states to be added to the number of + waits specified by WCR2 in a low-speed + PCMCIA wait cycle */ +#define SH7750_PCR_A5PCW_0 0x0000 /* 0 waits inserted */ +#define SH7750_PCR_A5PCW_15 0x4000 /* 15 waits inserted */ +#define SH7750_PCR_A5PCW_30 0x8000 /* 30 waits inserted */ +#define SH7750_PCR_A5PCW_50 0xC000 /* 50 waits inserted */ -#define SH7750_PCR_A6PCW 0x3000 /* Area 6 PCMCIA Wait - Number of wait - states to be added to the number of - waits specified by WCR2 in a low-speed - PCMCIA wait cycle */ -#define SH7750_PCR_A6PCW_0 0x0000 /* 0 waits inserted */ -#define SH7750_PCR_A6PCW_15 0x1000 /* 15 waits inserted */ -#define SH7750_PCR_A6PCW_30 0x2000 /* 30 waits inserted */ -#define SH7750_PCR_A6PCW_50 0x3000 /* 50 waits inserted */ +#define SH7750_PCR_A6PCW 0x3000 /* Area 6 PCMCIA Wait - Number of wait + states to be added to the number of + waits specified by WCR2 in a low-speed + PCMCIA wait cycle */ +#define SH7750_PCR_A6PCW_0 0x0000 /* 0 waits inserted */ +#define SH7750_PCR_A6PCW_15 0x1000 /* 15 waits inserted */ +#define SH7750_PCR_A6PCW_30 0x2000 /* 30 waits inserted */ +#define SH7750_PCR_A6PCW_50 0x3000 /* 50 waits inserted */ -#define SH7750_PCR_A5TED 0x0E00 /* Area 5 Address-OE\/WE\ Assertion Delay, - delay time from address output to - OE\/WE\ assertion on the connected - PCMCIA interface */ +#define SH7750_PCR_A5TED 0x0E00 /* Area 5 Address-OE\/WE\ Assertion Delay, + delay time from address output to + OE\/WE\ assertion on the connected + PCMCIA interface */ #define SH7750_PCR_A5TED_S 9 -#define SH7750_PCR_A6TED 0x01C0 /* Area 6 Address-OE\/WE\ Assertion Delay */ +#define SH7750_PCR_A6TED 0x01C0 /* Area 6 Address-OE\/WE\ Assertion Delay */ #define SH7750_PCR_A6TED_S 6 -#define SH7750_PCR_TED_0WS 0 /* 0 Waits inserted */ -#define SH7750_PCR_TED_1WS 1 /* 1 Waits inserted */ -#define SH7750_PCR_TED_2WS 2 /* 2 Waits inserted */ -#define SH7750_PCR_TED_3WS 3 /* 3 Waits inserted */ -#define SH7750_PCR_TED_6WS 4 /* 6 Waits inserted */ -#define SH7750_PCR_TED_9WS 5 /* 9 Waits inserted */ -#define SH7750_PCR_TED_12WS 6 /* 12 Waits inserted */ -#define SH7750_PCR_TED_15WS 7 /* 15 Waits inserted */ +#define SH7750_PCR_TED_0WS 0 /* 0 Waits inserted */ +#define SH7750_PCR_TED_1WS 1 /* 1 Waits inserted */ +#define SH7750_PCR_TED_2WS 2 /* 2 Waits inserted */ +#define SH7750_PCR_TED_3WS 3 /* 3 Waits inserted */ +#define SH7750_PCR_TED_6WS 4 /* 6 Waits inserted */ +#define SH7750_PCR_TED_9WS 5 /* 9 Waits inserted */ +#define SH7750_PCR_TED_12WS 6 /* 12 Waits inserted */ +#define SH7750_PCR_TED_15WS 7 /* 15 Waits inserted */ -#define SH7750_PCR_A5TEH 0x0038 /* Area 5 OE\/WE\ Negation Address delay, - address hold delay time from OE\/WE\ - negation in a write on the connected - PCMCIA interface */ +#define SH7750_PCR_A5TEH 0x0038 /* Area 5 OE\/WE\ Negation Address delay, + address hold delay time from OE\/WE\ + negation in a write on the connected + PCMCIA interface */ #define SH7750_PCR_A5TEH_S 3 -#define SH7750_PCR_A6TEH 0x0007 /* Area 6 OE\/WE\ Negation Address delay */ +#define SH7750_PCR_A6TEH 0x0007 /* Area 6 OE\/WE\ Negation Address delay */ #define SH7750_PCR_A6TEH_S 0 -#define SH7750_PCR_TEH_0WS 0 /* 0 Waits inserted */ -#define SH7750_PCR_TEH_1WS 1 /* 1 Waits inserted */ -#define SH7750_PCR_TEH_2WS 2 /* 2 Waits inserted */ -#define SH7750_PCR_TEH_3WS 3 /* 3 Waits inserted */ -#define SH7750_PCR_TEH_6WS 4 /* 6 Waits inserted */ -#define SH7750_PCR_TEH_9WS 5 /* 9 Waits inserted */ -#define SH7750_PCR_TEH_12WS 6 /* 12 Waits inserted */ -#define SH7750_PCR_TEH_15WS 7 /* 15 Waits inserted */ +#define SH7750_PCR_TEH_0WS 0 /* 0 Waits inserted */ +#define SH7750_PCR_TEH_1WS 1 /* 1 Waits inserted */ +#define SH7750_PCR_TEH_2WS 2 /* 2 Waits inserted */ +#define SH7750_PCR_TEH_3WS 3 /* 3 Waits inserted */ +#define SH7750_PCR_TEH_6WS 4 /* 6 Waits inserted */ +#define SH7750_PCR_TEH_9WS 5 /* 9 Waits inserted */ +#define SH7750_PCR_TEH_12WS 6 /* 12 Waits inserted */ +#define SH7750_PCR_TEH_15WS 7 /* 15 Waits inserted */ /* Refresh Timer Control/Status Register (half) - RTSCR */ -#define SH7750_RTCSR_REGOFS 0x80001C /* offset */ +#define SH7750_RTCSR_REGOFS 0x80001C /* offset */ #define SH7750_RTCSR SH7750_P4_REG32(SH7750_RTCSR_REGOFS) #define SH7750_RTCSR_A7 SH7750_A7_REG32(SH7750_RTCSR_REGOFS) -#define SH7750_RTCSR_KEY 0xA500 /* RTCSR write key */ -#define SH7750_RTCSR_CMF 0x0080 /* Compare-Match Flag (indicates a - match between the refresh timer - counter and refresh time constant) */ -#define SH7750_RTCSR_CMIE 0x0040 /* Compare-Match Interrupt Enable */ -#define SH7750_RTCSR_CKS 0x0038 /* Refresh Counter Clock Selects */ -#define SH7750_RTCSR_CKS_DIS 0x0000 /* Clock Input Disabled */ -#define SH7750_RTCSR_CKS_CKIO_DIV4 0x0008 /* Bus Clock / 4 */ -#define SH7750_RTCSR_CKS_CKIO_DIV16 0x0010 /* Bus Clock / 16 */ -#define SH7750_RTCSR_CKS_CKIO_DIV64 0x0018 /* Bus Clock / 64 */ -#define SH7750_RTCSR_CKS_CKIO_DIV256 0x0020 /* Bus Clock / 256 */ -#define SH7750_RTCSR_CKS_CKIO_DIV1024 0x0028 /* Bus Clock / 1024 */ -#define SH7750_RTCSR_CKS_CKIO_DIV2048 0x0030 /* Bus Clock / 2048 */ -#define SH7750_RTCSR_CKS_CKIO_DIV4096 0x0038 /* Bus Clock / 4096 */ +#define SH7750_RTCSR_KEY 0xA500 /* RTCSR write key */ +#define SH7750_RTCSR_CMF 0x0080 /* Compare-Match Flag (indicates a + match between the refresh timer + counter and refresh time constant) */ +#define SH7750_RTCSR_CMIE 0x0040 /* Compare-Match Interrupt Enable */ +#define SH7750_RTCSR_CKS 0x0038 /* Refresh Counter Clock Selects */ +#define SH7750_RTCSR_CKS_DIS 0x0000 /* Clock Input Disabled */ +#define SH7750_RTCSR_CKS_CKIO_DIV4 0x0008 /* Bus Clock / 4 */ +#define SH7750_RTCSR_CKS_CKIO_DIV16 0x0010 /* Bus Clock / 16 */ +#define SH7750_RTCSR_CKS_CKIO_DIV64 0x0018 /* Bus Clock / 64 */ +#define SH7750_RTCSR_CKS_CKIO_DIV256 0x0020 /* Bus Clock / 256 */ +#define SH7750_RTCSR_CKS_CKIO_DIV1024 0x0028 /* Bus Clock / 1024 */ +#define SH7750_RTCSR_CKS_CKIO_DIV2048 0x0030 /* Bus Clock / 2048 */ +#define SH7750_RTCSR_CKS_CKIO_DIV4096 0x0038 /* Bus Clock / 4096 */ -#define SH7750_RTCSR_OVF 0x0004 /* Refresh Count Overflow Flag */ -#define SH7750_RTCSR_OVIE 0x0002 /* Refresh Count Overflow Interrupt - Enable */ -#define SH7750_RTCSR_LMTS 0x0001 /* Refresh Count Overflow Limit Select */ -#define SH7750_RTCSR_LMTS_1024 0x0000 /* Count Limit is 1024 */ -#define SH7750_RTCSR_LMTS_512 0x0001 /* Count Limit is 512 */ +#define SH7750_RTCSR_OVF 0x0004 /* Refresh Count Overflow Flag */ +#define SH7750_RTCSR_OVIE 0x0002 /* Refresh Count Overflow Interrupt + Enable */ +#define SH7750_RTCSR_LMTS 0x0001 /* Refresh Count Overflow Limit Select */ +#define SH7750_RTCSR_LMTS_1024 0x0000 /* Count Limit is 1024 */ +#define SH7750_RTCSR_LMTS_512 0x0001 /* Count Limit is 512 */ /* Refresh Timer Counter (half) - RTCNT */ -#define SH7750_RTCNT_REGOFS 0x800020 /* offset */ +#define SH7750_RTCNT_REGOFS 0x800020 /* offset */ #define SH7750_RTCNT SH7750_P4_REG32(SH7750_RTCNT_REGOFS) #define SH7750_RTCNT_A7 SH7750_A7_REG32(SH7750_RTCNT_REGOFS) -#define SH7750_RTCNT_KEY 0xA500 /* RTCNT write key */ +#define SH7750_RTCNT_KEY 0xA500 /* RTCNT write key */ /* Refresh Time Constant Register (half) - RTCOR */ -#define SH7750_RTCOR_REGOFS 0x800024 /* offset */ +#define SH7750_RTCOR_REGOFS 0x800024 /* offset */ #define SH7750_RTCOR SH7750_P4_REG32(SH7750_RTCOR_REGOFS) #define SH7750_RTCOR_A7 SH7750_A7_REG32(SH7750_RTCOR_REGOFS) -#define SH7750_RTCOR_KEY 0xA500 /* RTCOR write key */ +#define SH7750_RTCOR_KEY 0xA500 /* RTCOR write key */ /* Refresh Count Register (half) - RFCR */ -#define SH7750_RFCR_REGOFS 0x800028 /* offset */ +#define SH7750_RFCR_REGOFS 0x800028 /* offset */ #define SH7750_RFCR SH7750_P4_REG32(SH7750_RFCR_REGOFS) #define SH7750_RFCR_A7 SH7750_A7_REG32(SH7750_RFCR_REGOFS) -#define SH7750_RFCR_KEY 0xA400 /* RFCR write key */ +#define SH7750_RFCR_KEY 0xA400 /* RFCR write key */ /* Synchronous DRAM mode registers - SDMR */ -#define SH7750_SDMR2_REGOFS 0x900000 /* base offset */ -#define SH7750_SDMR2_REGNB 0x0FFC /* nb of register */ +#define SH7750_SDMR2_REGOFS 0x900000 /* base offset */ +#define SH7750_SDMR2_REGNB 0x0FFC /* nb of register */ #define SH7750_SDMR2 SH7750_P4_REG32(SH7750_SDMR2_REGOFS) #define SH7750_SDMR2_A7 SH7750_A7_REG32(SH7750_SDMR2_REGOFS) -#define SH7750_SDMR3_REGOFS 0x940000 /* offset */ -#define SH7750_SDMR3_REGNB 0x0FFC /* nb of register */ +#define SH7750_SDMR3_REGOFS 0x940000 /* offset */ +#define SH7750_SDMR3_REGNB 0x0FFC /* nb of register */ #define SH7750_SDMR3 SH7750_P4_REG32(SH7750_SDMR3_REGOFS) #define SH7750_SDMR3_A7 SH7750_A7_REG32(SH7750_SDMR3_REGOFS) @@ -1015,7 +1015,7 @@ */ /* DMA Source Address Register - SAR0, SAR1, SAR2, SAR3 */ -#define SH7750_SAR_REGOFS(n) (0xA00000 + ((n)*16)) /* offset */ +#define SH7750_SAR_REGOFS(n) (0xA00000 + ((n)*16)) /* offset */ #define SH7750_SAR(n) SH7750_P4_REG32(SH7750_SAR_REGOFS(n)) #define SH7750_SAR_A7(n) SH7750_A7_REG32(SH7750_SAR_REGOFS(n)) #define SH7750_SAR0 SH7750_SAR(0) @@ -1028,7 +1028,7 @@ #define SH7750_SAR3_A7 SH7750_SAR_A7(3) /* DMA Destination Address Register - DAR0, DAR1, DAR2, DAR3 */ -#define SH7750_DAR_REGOFS(n) (0xA00004 + ((n)*16)) /* offset */ +#define SH7750_DAR_REGOFS(n) (0xA00004 + ((n)*16)) /* offset */ #define SH7750_DAR(n) SH7750_P4_REG32(SH7750_DAR_REGOFS(n)) #define SH7750_DAR_A7(n) SH7750_A7_REG32(SH7750_DAR_REGOFS(n)) #define SH7750_DAR0 SH7750_DAR(0) @@ -1041,7 +1041,7 @@ #define SH7750_DAR3_A7 SH7750_DAR_A7(3) /* DMA Transfer Count Register - DMATCR0, DMATCR1, DMATCR2, DMATCR3 */ -#define SH7750_DMATCR_REGOFS(n) (0xA00008 + ((n)*16)) /* offset */ +#define SH7750_DMATCR_REGOFS(n) (0xA00008 + ((n)*16)) /* offset */ #define SH7750_DMATCR(n) SH7750_P4_REG32(SH7750_DMATCR_REGOFS(n)) #define SH7750_DMATCR_A7(n) SH7750_A7_REG32(SH7750_DMATCR_REGOFS(n)) #define SH7750_DMATCR0_P4 SH7750_DMATCR(0) @@ -1054,7 +1054,7 @@ #define SH7750_DMATCR3_A7 SH7750_DMATCR_A7(3) /* DMA Channel Control Register - CHCR0, CHCR1, CHCR2, CHCR3 */ -#define SH7750_CHCR_REGOFS(n) (0xA0000C + ((n)*16)) /* offset */ +#define SH7750_CHCR_REGOFS(n) (0xA0000C + ((n)*16)) /* offset */ #define SH7750_CHCR(n) SH7750_P4_REG32(SH7750_CHCR_REGOFS(n)) #define SH7750_CHCR_A7(n) SH7750_A7_REG32(SH7750_CHCR_REGOFS(n)) #define SH7750_CHCR0 SH7750_CHCR(0) @@ -1066,227 +1066,227 @@ #define SH7750_CHCR2_A7 SH7750_CHCR_A7(2) #define SH7750_CHCR3_A7 SH7750_CHCR_A7(3) -#define SH7750_CHCR_SSA 0xE0000000 /* Source Address Space Attribute */ -#define SH7750_CHCR_SSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ -#define SH7750_CHCR_SSA_DYNBSZ 0x20000000 /* Dynamic Bus Sizing I/O space */ -#define SH7750_CHCR_SSA_IO8 0x40000000 /* 8-bit I/O space */ -#define SH7750_CHCR_SSA_IO16 0x60000000 /* 16-bit I/O space */ -#define SH7750_CHCR_SSA_CMEM8 0x80000000 /* 8-bit common memory space */ -#define SH7750_CHCR_SSA_CMEM16 0xA0000000 /* 16-bit common memory space */ -#define SH7750_CHCR_SSA_AMEM8 0xC0000000 /* 8-bit attribute memory space */ -#define SH7750_CHCR_SSA_AMEM16 0xE0000000 /* 16-bit attribute memory space */ +#define SH7750_CHCR_SSA 0xE0000000 /* Source Address Space Attribute */ +#define SH7750_CHCR_SSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ +#define SH7750_CHCR_SSA_DYNBSZ 0x20000000 /* Dynamic Bus Sizing I/O space */ +#define SH7750_CHCR_SSA_IO8 0x40000000 /* 8-bit I/O space */ +#define SH7750_CHCR_SSA_IO16 0x60000000 /* 16-bit I/O space */ +#define SH7750_CHCR_SSA_CMEM8 0x80000000 /* 8-bit common memory space */ +#define SH7750_CHCR_SSA_CMEM16 0xA0000000 /* 16-bit common memory space */ +#define SH7750_CHCR_SSA_AMEM8 0xC0000000 /* 8-bit attribute memory space */ +#define SH7750_CHCR_SSA_AMEM16 0xE0000000 /* 16-bit attribute memory space */ -#define SH7750_CHCR_STC 0x10000000 /* Source Address Wait Control Select, - specifies CS5 or CS6 space wait - control for PCMCIA access */ +#define SH7750_CHCR_STC 0x10000000 /* Source Address Wait Control Select, + specifies CS5 or CS6 space wait + control for PCMCIA access */ -#define SH7750_CHCR_DSA 0x0E000000 /* Source Address Space Attribute */ -#define SH7750_CHCR_DSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ -#define SH7750_CHCR_DSA_DYNBSZ 0x02000000 /* Dynamic Bus Sizing I/O space */ -#define SH7750_CHCR_DSA_IO8 0x04000000 /* 8-bit I/O space */ -#define SH7750_CHCR_DSA_IO16 0x06000000 /* 16-bit I/O space */ -#define SH7750_CHCR_DSA_CMEM8 0x08000000 /* 8-bit common memory space */ -#define SH7750_CHCR_DSA_CMEM16 0x0A000000 /* 16-bit common memory space */ -#define SH7750_CHCR_DSA_AMEM8 0x0C000000 /* 8-bit attribute memory space */ -#define SH7750_CHCR_DSA_AMEM16 0x0E000000 /* 16-bit attribute memory space */ +#define SH7750_CHCR_DSA 0x0E000000 /* Source Address Space Attribute */ +#define SH7750_CHCR_DSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ +#define SH7750_CHCR_DSA_DYNBSZ 0x02000000 /* Dynamic Bus Sizing I/O space */ +#define SH7750_CHCR_DSA_IO8 0x04000000 /* 8-bit I/O space */ +#define SH7750_CHCR_DSA_IO16 0x06000000 /* 16-bit I/O space */ +#define SH7750_CHCR_DSA_CMEM8 0x08000000 /* 8-bit common memory space */ +#define SH7750_CHCR_DSA_CMEM16 0x0A000000 /* 16-bit common memory space */ +#define SH7750_CHCR_DSA_AMEM8 0x0C000000 /* 8-bit attribute memory space */ +#define SH7750_CHCR_DSA_AMEM16 0x0E000000 /* 16-bit attribute memory space */ -#define SH7750_CHCR_DTC 0x01000000 /* Destination Address Wait Control - Select, specifies CS5 or CS6 - space wait control for PCMCIA - access */ +#define SH7750_CHCR_DTC 0x01000000 /* Destination Address Wait Control + Select, specifies CS5 or CS6 + space wait control for PCMCIA + access */ -#define SH7750_CHCR_DS 0x00080000 /* DREQ\ Select : */ -#define SH7750_CHCR_DS_LOWLVL 0x00000000 /* Low Level Detection */ -#define SH7750_CHCR_DS_FALL 0x00080000 /* Falling Edge Detection */ +#define SH7750_CHCR_DS 0x00080000 /* DREQ\ Select : */ +#define SH7750_CHCR_DS_LOWLVL 0x00000000 /* Low Level Detection */ +#define SH7750_CHCR_DS_FALL 0x00080000 /* Falling Edge Detection */ -#define SH7750_CHCR_RL 0x00040000 /* Request Check Level: */ -#define SH7750_CHCR_RL_ACTH 0x00000000 /* DRAK is an active high out */ -#define SH7750_CHCR_RL_ACTL 0x00040000 /* DRAK is an active low out */ +#define SH7750_CHCR_RL 0x00040000 /* Request Check Level: */ +#define SH7750_CHCR_RL_ACTH 0x00000000 /* DRAK is an active high out */ +#define SH7750_CHCR_RL_ACTL 0x00040000 /* DRAK is an active low out */ -#define SH7750_CHCR_AM 0x00020000 /* Acknowledge Mode: */ -#define SH7750_CHCR_AM_RD 0x00000000 /* DACK is output in read cycle */ -#define SH7750_CHCR_AM_WR 0x00020000 /* DACK is output in write cycle */ +#define SH7750_CHCR_AM 0x00020000 /* Acknowledge Mode: */ +#define SH7750_CHCR_AM_RD 0x00000000 /* DACK is output in read cycle */ +#define SH7750_CHCR_AM_WR 0x00020000 /* DACK is output in write cycle */ -#define SH7750_CHCR_AL 0x00010000 /* Acknowledge Level: */ -#define SH7750_CHCR_AL_ACTH 0x00000000 /* DACK is an active high out */ -#define SH7750_CHCR_AL_ACTL 0x00010000 /* DACK is an active low out */ +#define SH7750_CHCR_AL 0x00010000 /* Acknowledge Level: */ +#define SH7750_CHCR_AL_ACTH 0x00000000 /* DACK is an active high out */ +#define SH7750_CHCR_AL_ACTL 0x00010000 /* DACK is an active low out */ -#define SH7750_CHCR_DM 0x0000C000 /* Destination Address Mode: */ -#define SH7750_CHCR_DM_FIX 0x00000000 /* Destination Addr Fixed */ -#define SH7750_CHCR_DM_INC 0x00004000 /* Destination Addr Incremented */ -#define SH7750_CHCR_DM_DEC 0x00008000 /* Destination Addr Decremented */ +#define SH7750_CHCR_DM 0x0000C000 /* Destination Address Mode: */ +#define SH7750_CHCR_DM_FIX 0x00000000 /* Destination Addr Fixed */ +#define SH7750_CHCR_DM_INC 0x00004000 /* Destination Addr Incremented */ +#define SH7750_CHCR_DM_DEC 0x00008000 /* Destination Addr Decremented */ -#define SH7750_CHCR_SM 0x00003000 /* Source Address Mode: */ -#define SH7750_CHCR_SM_FIX 0x00000000 /* Source Addr Fixed */ -#define SH7750_CHCR_SM_INC 0x00001000 /* Source Addr Incremented */ -#define SH7750_CHCR_SM_DEC 0x00002000 /* Source Addr Decremented */ +#define SH7750_CHCR_SM 0x00003000 /* Source Address Mode: */ +#define SH7750_CHCR_SM_FIX 0x00000000 /* Source Addr Fixed */ +#define SH7750_CHCR_SM_INC 0x00001000 /* Source Addr Incremented */ +#define SH7750_CHCR_SM_DEC 0x00002000 /* Source Addr Decremented */ -#define SH7750_CHCR_RS 0x00000F00 /* Request Source Select: */ -#define SH7750_CHCR_RS_ER_DA_EA_TO_EA 0x000 /* External Request, Dual Address - Mode (External Addr Space-> - External Addr Space) */ -#define SH7750_CHCR_RS_ER_SA_EA_TO_ED 0x200 /* External Request, Single - Address Mode (External Addr - Space -> External Device) */ -#define SH7750_CHCR_RS_ER_SA_ED_TO_EA 0x300 /* External Request, Single - Address Mode, (External - Device -> External Addr - Space) */ -#define SH7750_CHCR_RS_AR_EA_TO_EA 0x400 /* Auto-Request (External Addr - Space -> External Addr Space) */ +#define SH7750_CHCR_RS 0x00000F00 /* Request Source Select: */ +#define SH7750_CHCR_RS_ER_DA_EA_TO_EA 0x000 /* External Request, Dual Address + Mode (External Addr Space-> + External Addr Space) */ +#define SH7750_CHCR_RS_ER_SA_EA_TO_ED 0x200 /* External Request, Single + Address Mode (External Addr + Space -> External Device) */ +#define SH7750_CHCR_RS_ER_SA_ED_TO_EA 0x300 /* External Request, Single + Address Mode, (External + Device -> External Addr + Space) */ +#define SH7750_CHCR_RS_AR_EA_TO_EA 0x400 /* Auto-Request (External Addr + Space -> External Addr Space) */ -#define SH7750_CHCR_RS_AR_EA_TO_OCP 0x500 /* Auto-Request (External Addr - Space -> On-chip Peripheral - Module) */ -#define SH7750_CHCR_RS_AR_OCP_TO_EA 0x600 /* Auto-Request (On-chip - Peripheral Module -> - External Addr Space */ -#define SH7750_CHCR_RS_SCITX_EA_TO_SC 0x800 /* SCI Transmit-Data-Empty intr - transfer request (external - address space -> SCTDR1) */ -#define SH7750_CHCR_RS_SCIRX_SC_TO_EA 0x900 /* SCI Receive-Data-Full intr - transfer request (SCRDR1 -> - External Addr Space) */ -#define SH7750_CHCR_RS_SCIFTX_EA_TO_SC 0xA00 /* SCIF Transmit-Data-Empty intr - transfer request (external - address space -> SCFTDR1) */ -#define SH7750_CHCR_RS_SCIFRX_SC_TO_EA 0xB00 /* SCIF Receive-Data-Full intr - transfer request (SCFRDR2 -> - External Addr Space) */ -#define SH7750_CHCR_RS_TMU2_EA_TO_EA 0xC00 /* TMU Channel 2 (input capture - interrupt), (external address - space -> external address - space) */ -#define SH7750_CHCR_RS_TMU2_EA_TO_OCP 0xD00 /* TMU Channel 2 (input capture - interrupt), (external address - space -> on-chip peripheral - module) */ -#define SH7750_CHCR_RS_TMU2_OCP_TO_EA 0xE00 /* TMU Channel 2 (input capture - interrupt), (on-chip - peripheral module -> external - address space) */ +#define SH7750_CHCR_RS_AR_EA_TO_OCP 0x500 /* Auto-Request (External Addr + Space -> On-chip Peripheral + Module) */ +#define SH7750_CHCR_RS_AR_OCP_TO_EA 0x600 /* Auto-Request (On-chip + Peripheral Module -> + External Addr Space */ +#define SH7750_CHCR_RS_SCITX_EA_TO_SC 0x800 /* SCI Transmit-Data-Empty intr + transfer request (external + address space -> SCTDR1) */ +#define SH7750_CHCR_RS_SCIRX_SC_TO_EA 0x900 /* SCI Receive-Data-Full intr + transfer request (SCRDR1 -> + External Addr Space) */ +#define SH7750_CHCR_RS_SCIFTX_EA_TO_SC 0xA00 /* SCIF Transmit-Data-Empty intr + transfer request (external + address space -> SCFTDR1) */ +#define SH7750_CHCR_RS_SCIFRX_SC_TO_EA 0xB00 /* SCIF Receive-Data-Full intr + transfer request (SCFRDR2 -> + External Addr Space) */ +#define SH7750_CHCR_RS_TMU2_EA_TO_EA 0xC00 /* TMU Channel 2 (input capture + interrupt), (external address + space -> external address + space) */ +#define SH7750_CHCR_RS_TMU2_EA_TO_OCP 0xD00 /* TMU Channel 2 (input capture + interrupt), (external address + space -> on-chip peripheral + module) */ +#define SH7750_CHCR_RS_TMU2_OCP_TO_EA 0xE00 /* TMU Channel 2 (input capture + interrupt), (on-chip + peripheral module -> external + address space) */ -#define SH7750_CHCR_TM 0x00000080 /* Transmit mode: */ -#define SH7750_CHCR_TM_CSTEAL 0x00000000 /* Cycle Steal Mode */ -#define SH7750_CHCR_TM_BURST 0x00000080 /* Burst Mode */ +#define SH7750_CHCR_TM 0x00000080 /* Transmit mode: */ +#define SH7750_CHCR_TM_CSTEAL 0x00000000 /* Cycle Steal Mode */ +#define SH7750_CHCR_TM_BURST 0x00000080 /* Burst Mode */ -#define SH7750_CHCR_TS 0x00000070 /* Transmit Size: */ -#define SH7750_CHCR_TS_QUAD 0x00000000 /* Quadword Size (64 bits) */ -#define SH7750_CHCR_TS_BYTE 0x00000010 /* Byte Size (8 bit) */ -#define SH7750_CHCR_TS_WORD 0x00000020 /* Word Size (16 bit) */ -#define SH7750_CHCR_TS_LONG 0x00000030 /* Longword Size (32 bit) */ -#define SH7750_CHCR_TS_BLOCK 0x00000040 /* 32-byte block transfer */ +#define SH7750_CHCR_TS 0x00000070 /* Transmit Size: */ +#define SH7750_CHCR_TS_QUAD 0x00000000 /* Quadword Size (64 bits) */ +#define SH7750_CHCR_TS_BYTE 0x00000010 /* Byte Size (8 bit) */ +#define SH7750_CHCR_TS_WORD 0x00000020 /* Word Size (16 bit) */ +#define SH7750_CHCR_TS_LONG 0x00000030 /* Longword Size (32 bit) */ +#define SH7750_CHCR_TS_BLOCK 0x00000040 /* 32-byte block transfer */ -#define SH7750_CHCR_IE 0x00000004 /* Interrupt Enable */ -#define SH7750_CHCR_TE 0x00000002 /* Transfer End */ -#define SH7750_CHCR_DE 0x00000001 /* DMAC Enable */ +#define SH7750_CHCR_IE 0x00000004 /* Interrupt Enable */ +#define SH7750_CHCR_TE 0x00000002 /* Transfer End */ +#define SH7750_CHCR_DE 0x00000001 /* DMAC Enable */ /* DMA Operation Register - DMAOR */ -#define SH7750_DMAOR_REGOFS 0xA00040 /* offset */ +#define SH7750_DMAOR_REGOFS 0xA00040 /* offset */ #define SH7750_DMAOR SH7750_P4_REG32(SH7750_DMAOR_REGOFS) #define SH7750_DMAOR_A7 SH7750_A7_REG32(SH7750_DMAOR_REGOFS) -#define SH7750_DMAOR_DDT 0x00008000 /* On-Demand Data Transfer Mode */ +#define SH7750_DMAOR_DDT 0x00008000 /* On-Demand Data Transfer Mode */ -#define SH7750_DMAOR_PR 0x00000300 /* Priority Mode: */ -#define SH7750_DMAOR_PR_0123 0x00000000 /* CH0 > CH1 > CH2 > CH3 */ -#define SH7750_DMAOR_PR_0231 0x00000100 /* CH0 > CH2 > CH3 > CH1 */ -#define SH7750_DMAOR_PR_2013 0x00000200 /* CH2 > CH0 > CH1 > CH3 */ -#define SH7750_DMAOR_PR_RR 0x00000300 /* Round-robin mode */ +#define SH7750_DMAOR_PR 0x00000300 /* Priority Mode: */ +#define SH7750_DMAOR_PR_0123 0x00000000 /* CH0 > CH1 > CH2 > CH3 */ +#define SH7750_DMAOR_PR_0231 0x00000100 /* CH0 > CH2 > CH3 > CH1 */ +#define SH7750_DMAOR_PR_2013 0x00000200 /* CH2 > CH0 > CH1 > CH3 */ +#define SH7750_DMAOR_PR_RR 0x00000300 /* Round-robin mode */ -#define SH7750_DMAOR_COD 0x00000010 /* Check Overrun for DREQ\ */ -#define SH7750_DMAOR_AE 0x00000004 /* Address Error flag */ -#define SH7750_DMAOR_NMIF 0x00000002 /* NMI Flag */ -#define SH7750_DMAOR_DME 0x00000001 /* DMAC Master Enable */ +#define SH7750_DMAOR_COD 0x00000010 /* Check Overrun for DREQ\ */ +#define SH7750_DMAOR_AE 0x00000004 /* Address Error flag */ +#define SH7750_DMAOR_NMIF 0x00000002 /* NMI Flag */ +#define SH7750_DMAOR_DME 0x00000001 /* DMAC Master Enable */ /* * I/O Ports */ /* Port Control Register A - PCTRA */ -#define SH7750_PCTRA_REGOFS 0x80002C /* offset */ +#define SH7750_PCTRA_REGOFS 0x80002C /* offset */ #define SH7750_PCTRA SH7750_P4_REG32(SH7750_PCTRA_REGOFS) #define SH7750_PCTRA_A7 SH7750_A7_REG32(SH7750_PCTRA_REGOFS) -#define SH7750_PCTRA_PBPUP(n) 0 /* Bit n is pulled up */ -#define SH7750_PCTRA_PBNPUP(n) (1 << ((n)*2+1)) /* Bit n is not pulled up */ -#define SH7750_PCTRA_PBINP(n) 0 /* Bit n is an input */ -#define SH7750_PCTRA_PBOUT(n) (1 << ((n)*2)) /* Bit n is an output */ +#define SH7750_PCTRA_PBPUP(n) 0 /* Bit n is pulled up */ +#define SH7750_PCTRA_PBNPUP(n) (1 << ((n)*2+1)) /* Bit n is not pulled up */ +#define SH7750_PCTRA_PBINP(n) 0 /* Bit n is an input */ +#define SH7750_PCTRA_PBOUT(n) (1 << ((n)*2)) /* Bit n is an output */ /* Port Data Register A - PDTRA(half) */ -#define SH7750_PDTRA_REGOFS 0x800030 /* offset */ +#define SH7750_PDTRA_REGOFS 0x800030 /* offset */ #define SH7750_PDTRA SH7750_P4_REG32(SH7750_PDTRA_REGOFS) #define SH7750_PDTRA_A7 SH7750_A7_REG32(SH7750_PDTRA_REGOFS) #define SH7750_PDTRA_BIT(n) (1 << (n)) /* Port Control Register B - PCTRB */ -#define SH7750_PCTRB_REGOFS 0x800040 /* offset */ +#define SH7750_PCTRB_REGOFS 0x800040 /* offset */ #define SH7750_PCTRB SH7750_P4_REG32(SH7750_PCTRB_REGOFS) #define SH7750_PCTRB_A7 SH7750_A7_REG32(SH7750_PCTRB_REGOFS) -#define SH7750_PCTRB_PBPUP(n) 0 /* Bit n is pulled up */ -#define SH7750_PCTRB_PBNPUP(n) (1 << ((n-16)*2+1)) /* Bit n is not pulled up */ -#define SH7750_PCTRB_PBINP(n) 0 /* Bit n is an input */ -#define SH7750_PCTRB_PBOUT(n) (1 << ((n-16)*2)) /* Bit n is an output */ +#define SH7750_PCTRB_PBPUP(n) 0 /* Bit n is pulled up */ +#define SH7750_PCTRB_PBNPUP(n) (1 << ((n-16)*2+1)) /* Bit n is not pulled up */ +#define SH7750_PCTRB_PBINP(n) 0 /* Bit n is an input */ +#define SH7750_PCTRB_PBOUT(n) (1 << ((n-16)*2)) /* Bit n is an output */ /* Port Data Register B - PDTRB(half) */ -#define SH7750_PDTRB_REGOFS 0x800044 /* offset */ +#define SH7750_PDTRB_REGOFS 0x800044 /* offset */ #define SH7750_PDTRB SH7750_P4_REG32(SH7750_PDTRB_REGOFS) #define SH7750_PDTRB_A7 SH7750_A7_REG32(SH7750_PDTRB_REGOFS) #define SH7750_PDTRB_BIT(n) (1 << ((n)-16)) /* GPIO Interrupt Control Register - GPIOIC(half) */ -#define SH7750_GPIOIC_REGOFS 0x800048 /* offset */ +#define SH7750_GPIOIC_REGOFS 0x800048 /* offset */ #define SH7750_GPIOIC SH7750_P4_REG32(SH7750_GPIOIC_REGOFS) #define SH7750_GPIOIC_A7 SH7750_A7_REG32(SH7750_GPIOIC_REGOFS) -#define SH7750_GPIOIC_PTIREN(n) (1 << (n)) /* Port n is used as a GPIO int */ +#define SH7750_GPIOIC_PTIREN(n) (1 << (n)) /* Port n is used as a GPIO int */ /* * Interrupt Controller - INTC */ /* Interrupt Control Register - ICR (half) */ -#define SH7750_ICR_REGOFS 0xD00000 /* offset */ +#define SH7750_ICR_REGOFS 0xD00000 /* offset */ #define SH7750_ICR SH7750_P4_REG32(SH7750_ICR_REGOFS) #define SH7750_ICR_A7 SH7750_A7_REG32(SH7750_ICR_REGOFS) -#define SH7750_ICR_NMIL 0x8000 /* NMI Input Level */ -#define SH7750_ICR_MAI 0x4000 /* NMI Interrupt Mask */ +#define SH7750_ICR_NMIL 0x8000 /* NMI Input Level */ +#define SH7750_ICR_MAI 0x4000 /* NMI Interrupt Mask */ -#define SH7750_ICR_NMIB 0x0200 /* NMI Block Mode: */ -#define SH7750_ICR_NMIB_BLK 0x0000 /* NMI requests held pending while - SR.BL bit is set to 1 */ -#define SH7750_ICR_NMIB_NBLK 0x0200 /* NMI requests detected when SR.BL bit - set to 1 */ +#define SH7750_ICR_NMIB 0x0200 /* NMI Block Mode: */ +#define SH7750_ICR_NMIB_BLK 0x0000 /* NMI requests held pending while + SR.BL bit is set to 1 */ +#define SH7750_ICR_NMIB_NBLK 0x0200 /* NMI requests detected when SR.BL bit + set to 1 */ -#define SH7750_ICR_NMIE 0x0100 /* NMI Edge Select: */ -#define SH7750_ICR_NMIE_FALL 0x0000 /* Interrupt request detected on falling - edge of NMI input */ -#define SH7750_ICR_NMIE_RISE 0x0100 /* Interrupt request detected on rising - edge of NMI input */ +#define SH7750_ICR_NMIE 0x0100 /* NMI Edge Select: */ +#define SH7750_ICR_NMIE_FALL 0x0000 /* Interrupt request detected on falling + edge of NMI input */ +#define SH7750_ICR_NMIE_RISE 0x0100 /* Interrupt request detected on rising + edge of NMI input */ -#define SH7750_ICR_IRLM 0x0080 /* IRL Pin Mode: */ -#define SH7750_ICR_IRLM_ENC 0x0000 /* IRL\ pins used as a level-encoded - interrupt requests */ -#define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four independent - interrupt requests */ +#define SH7750_ICR_IRLM 0x0080 /* IRL Pin Mode: */ +#define SH7750_ICR_IRLM_ENC 0x0000 /* IRL\ pins used as a level-encoded + interrupt requests */ +#define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four independent + interrupt requests */ /* * User Break Controller registers */ -#define SH7750_BARA 0x200000 /* Break address regiser A */ -#define SH7750_BAMRA 0x200004 /* Break address mask regiser A */ -#define SH7750_BBRA 0x200008 /* Break bus cycle regiser A */ -#define SH7750_BARB 0x20000c /* Break address regiser B */ -#define SH7750_BAMRB 0x200010 /* Break address mask regiser B */ -#define SH7750_BBRB 0x200014 /* Break bus cycle regiser B */ -#define SH7750_BASRB 0x000018 /* Break ASID regiser B */ -#define SH7750_BDRB 0x200018 /* Break data regiser B */ -#define SH7750_BDMRB 0x20001c /* Break data mask regiser B */ -#define SH7750_BRCR 0x200020 /* Break control register */ +#define SH7750_BARA 0x200000 /* Break address regiser A */ +#define SH7750_BAMRA 0x200004 /* Break address mask regiser A */ +#define SH7750_BBRA 0x200008 /* Break bus cycle regiser A */ +#define SH7750_BARB 0x20000c /* Break address regiser B */ +#define SH7750_BAMRB 0x200010 /* Break address mask regiser B */ +#define SH7750_BBRB 0x200014 /* Break bus cycle regiser B */ +#define SH7750_BASRB 0x000018 /* Break ASID regiser B */ +#define SH7750_BDRB 0x200018 /* Break data regiser B */ +#define SH7750_BDMRB 0x20001c /* Break data mask regiser B */ +#define SH7750_BRCR 0x200020 /* Break control register */ -#define SH7750_BRCR_UDBE 0x0001 /* User break debug enable bit */ +#define SH7750_BRCR_UDBE 0x0001 /* User break debug enable bit */ /* * Missing in RTEMS, added for QEMU diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h index becb596979..3d5ba598d0 100644 --- a/include/hw/sh4/sh.h +++ b/include/hw/sh4/sh.h @@ -44,25 +44,25 @@ typedef struct { uint16_t portbmask_trigger; /* Return 0 if no action was taken */ int (*port_change_cb) (uint16_t porta, uint16_t portb, - uint16_t * periph_pdtra, - uint16_t * periph_portdira, - uint16_t * periph_pdtrb, - uint16_t * periph_portdirb); + uint16_t * periph_pdtra, + uint16_t * periph_portdira, + uint16_t * periph_pdtrb, + uint16_t * periph_portdirb); } sh7750_io_device; int sh7750_register_io_device(struct SH7750State *s, - sh7750_io_device * device); + sh7750_io_device * device); /* sh_serial.c */ #define SH_SERIAL_FEAT_SCIF (1 << 0) void sh_serial_init(MemoryRegion *sysmem, hwaddr base, int feat, uint32_t freq, Chardev *chr, - qemu_irq eri_source, - qemu_irq rxi_source, - qemu_irq txi_source, - qemu_irq tei_source, - qemu_irq bri_source); + qemu_irq eri_source, + qemu_irq rxi_source, + qemu_irq txi_source, + qemu_irq tei_source, + qemu_irq bri_source); /* sh7750.c */ qemu_irq sh7750_irl(struct SH7750State *s); From 221389657aa77ced7a17936747f288193e321d3f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0867/1334] hw/sh4: Coding style: Fix multi-line comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Yoshinori Sato Message-Id: <3f192c699f4e5949ec0fcc436e5610f50afe2dbf.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 6 +- hw/intc/sh_intc.c | 9 +- hw/sh4/r2d.c | 6 +- hw/sh4/sh7750.c | 22 +- hw/sh4/sh7750_regs.h | 500 +++++++++++++++++++++---------------------- hw/sh4/shix.c | 10 +- hw/timer/sh_timer.c | 7 +- 7 files changed, 284 insertions(+), 276 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 167f4d8cb9..05ae8e84ce 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -115,8 +115,10 @@ static void sh_serial_write(void *opaque, hwaddr offs, case 0x0c: /* FTDR / TDR */ if (qemu_chr_fe_backend_connected(&s->chr)) { ch = val; - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ + /* + * XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks + */ qemu_chr_fe_write_all(&s->chr, &ch, 1); } s->dr = val; diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index a269b8fbd4..84eec7d4ba 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -450,8 +450,7 @@ int sh_intc_init(MemoryRegion *sysmem, desc->nr_mask_regs = nr_mask_regs; desc->prio_regs = prio_regs; desc->nr_prio_regs = nr_prio_regs; - /* Allocate 4 MemoryRegions per register (2 actions * 2 aliases). - **/ + /* Allocate 4 MemoryRegions per register (2 actions * 2 aliases) */ desc->iomem_aliases = g_new0(MemoryRegion, (nr_mask_regs + nr_prio_regs) * 4); @@ -498,8 +497,10 @@ int sh_intc_init(MemoryRegion *sysmem, return 0; } -/* Assert level IRL interrupt. - 0:deassert. 1:lowest priority,... 15:highest priority. */ +/* + * Assert level IRL interrupt. + * 0:deassert. 1:lowest priority,... 15:highest priority + */ void sh_intc_set_irl(void *opaque, int n, int level) { struct intc_source *s = opaque; diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 8f0d373b09..46f1fae48c 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -352,8 +352,10 @@ static void r2d_init(MachineState *machine) } if (kernel_cmdline) { - /* I see no evidence that this .kernel_cmdline buffer requires - NUL-termination, so using strncpy should be ok. */ + /* + * I see no evidence that this .kernel_cmdline buffer requires + * NUL-termination, so using strncpy should be ok. + */ strncpy(boot_params.kernel_cmdline, kernel_cmdline, sizeof(boot_params.kernel_cmdline)); } diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 2a175bfa74..2539924b00 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -82,9 +82,10 @@ static inline int has_bcr3_and_bcr4(SH7750State * s) { return s->cpu->env.features & SH_FEATURE_BCR3_AND_BCR4; } -/********************************************************************** - I/O ports -**********************************************************************/ + +/* + * I/O ports + */ int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device) { @@ -194,9 +195,9 @@ static void portb_changed(SH7750State * s, uint16_t prev) gen_port_interrupts(s); } -/********************************************************************** - Memory -**********************************************************************/ +/* + * Memory + */ static void error_access(const char *kind, hwaddr addr) { @@ -491,7 +492,8 @@ static const MemoryRegionOps sh7750_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -/* sh775x interrupt controller tables for sh_intc.c +/* + * sh775x interrupt controller tables for sh_intc.c * stolen from linux/arch/sh/kernel/cpu/sh4/setup-sh7750.c */ @@ -642,9 +644,9 @@ static struct intc_group groups_irl[] = { IRL_7, IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E), }; -/********************************************************************** - Memory mapped cache and TLB -**********************************************************************/ +/* + * Memory mapped cache and TLB + */ #define MM_REGION_MASK 0x07000000 #define MM_ICACHE_ADDR (0) diff --git a/hw/sh4/sh7750_regs.h b/hw/sh4/sh7750_regs.h index fd1050646f..bd12b0532d 100644 --- a/hw/sh4/sh7750_regs.h +++ b/hw/sh4/sh7750_regs.h @@ -43,8 +43,7 @@ * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and * in 0x1f000000 - 0x1fffffff (area 7 address) */ -#define SH7750_P4_BASE 0xff000000 /* Accessible only in - privileged mode */ +#define SH7750_P4_BASE 0xff000000 /* Accessible only in privileged mode */ #define SH7750_A7_BASE 0x1f000000 /* Accessible only using TLB */ #define SH7750_P4_REG32(ofs) (SH7750_P4_BASE + (ofs)) @@ -81,24 +80,24 @@ #define SH7750_PTEL_PR_RWPO 0x00000020 /* read-write in priv mode */ #define SH7750_PTEL_PR_ROPU 0x00000040 /* read-only in priv or user mode */ #define SH7750_PTEL_PR_RWPU 0x00000060 /* read-write in priv or user mode */ -#define SH7750_PTEL_C 0x00000008 /* Cacheability - (0 - page not cacheable) */ -#define SH7750_PTEL_D 0x00000004 /* Dirty bit (1 - write has been - performed to a page) */ -#define SH7750_PTEL_SH 0x00000002 /* Share Status bit (1 - page are - shared by processes) */ -#define SH7750_PTEL_WT 0x00000001 /* Write-through bit, specifies the - cache write mode: - 0 - Copy-back mode - 1 - Write-through mode */ +#define SH7750_PTEL_C 0x00000008 /* Cacheability */ + /* (0 - page not cacheable) */ +#define SH7750_PTEL_D 0x00000004 /* Dirty bit (1 - write has been */ + /* performed to a page) */ +#define SH7750_PTEL_SH 0x00000002 /* Share Status bit (1 - page are */ + /* shared by processes) */ +#define SH7750_PTEL_WT 0x00000001 /* Write-through bit, specifies the */ + /* cache write mode: */ + /* 0 - Copy-back mode */ + /* 1 - Write-through mode */ /* Page Table Entry Assistance register - PTEA */ #define SH7750_PTEA_REGOFS 0x000034 /* offset */ #define SH7750_PTEA SH7750_P4_REG32(SH7750_PTEA_REGOFS) #define SH7750_PTEA_A7 SH7750_A7_REG32(SH7750_PTEA_REGOFS) -#define SH7750_PTEA_TC 0x00000008 /* Timing Control bit - 0 - use area 5 wait states - 1 - use area 6 wait states */ +#define SH7750_PTEA_TC 0x00000008 /* Timing Control bit */ + /* 0 - use area 5 wait states */ + /* 1 - use area 6 wait states */ #define SH7750_PTEA_SA 0x00000007 /* Space Attribute bits: */ #define SH7750_PTEA_SA_UNDEF 0x00000000 /* 0 - undefined */ #define SH7750_PTEA_SA_IOVAR 0x00000001 /* 1 - variable-size I/O space */ @@ -150,13 +149,13 @@ #define SH7750_CCR_A7 SH7750_A7_REG32(SH7750_CCR_REGOFS) #define SH7750_CCR_IIX 0x00008000 /* IC index enable bit */ -#define SH7750_CCR_ICI 0x00000800 /* IC invalidation bit: - set it to clear IC */ +#define SH7750_CCR_ICI 0x00000800 /* IC invalidation bit: */ + /* set it to clear IC */ #define SH7750_CCR_ICE 0x00000100 /* IC enable bit */ #define SH7750_CCR_OIX 0x00000080 /* OC index enable bit */ -#define SH7750_CCR_ORA 0x00000020 /* OC RAM enable bit - if you set OCE = 0, - you should set ORA = 0 */ +#define SH7750_CCR_ORA 0x00000020 /* OC RAM enable bit */ + /* if you set OCE = 0, */ + /* you should set ORA = 0 */ #define SH7750_CCR_OCI 0x00000008 /* OC invalidation bit */ #define SH7750_CCR_CB 0x00000004 /* Copy-back bit for P1 area */ #define SH7750_CCR_WT 0x00000002 /* Write-through bit for P0,U0,P3 area */ @@ -213,21 +212,22 @@ /* General exception category */ #define SH7750_EVT_USER_BREAK 0x1E0 /* User break */ #define SH7750_EVT_IADDR_ERR 0x0E0 /* Instruction address error */ -#define SH7750_EVT_TLB_READ_MISS 0x040 /* ITLB miss exception / - DTLB miss exception (read) */ -#define SH7750_EVT_TLB_READ_PROTV 0x0A0 /* ITLB protection violation / - DTLB protection violation (read) */ -#define SH7750_EVT_ILLEGAL_INSTR 0x180 /* General Illegal Instruction - exception */ -#define SH7750_EVT_SLOT_ILLEGAL_INSTR 0x1A0 /* Slot Illegal Instruction - exception */ +#define SH7750_EVT_TLB_READ_MISS 0x040 /* ITLB miss exception / */ + /* DTLB miss exception (read) */ +#define SH7750_EVT_TLB_READ_PROTV 0x0A0 /* ITLB protection violation, */ + /* DTLB protection violation */ + /* (read) */ +#define SH7750_EVT_ILLEGAL_INSTR 0x180 /* General Illegal Instruction */ + /* exception */ +#define SH7750_EVT_SLOT_ILLEGAL_INSTR 0x1A0 /* Slot Illegal Instruction */ + /* exception */ #define SH7750_EVT_FPU_DISABLE 0x800 /* General FPU disable exception */ #define SH7750_EVT_SLOT_FPU_DISABLE 0x820 /* Slot FPU disable exception */ #define SH7750_EVT_DATA_READ_ERR 0x0E0 /* Data address error (read) */ #define SH7750_EVT_DATA_WRITE_ERR 0x100 /* Data address error (write) */ #define SH7750_EVT_DTLB_WRITE_MISS 0x060 /* DTLB miss exception (write) */ -#define SH7750_EVT_DTLB_WRITE_PROTV 0x0C0 /* DTLB protection violation - exception (write) */ +#define SH7750_EVT_DTLB_WRITE_PROTV 0x0C0 /* DTLB protection violation */ + /* exception (write) */ #define SH7750_EVT_FPU_EXCEPTION 0x120 /* FPU exception */ #define SH7750_EVT_INITIAL_PGWRITE 0x080 /* Initial Page Write exception */ #define SH7750_EVT_TRAPA 0x160 /* Unconditional trap (TRAPA) */ @@ -268,14 +268,14 @@ #define SH7750_EVT_SCI_TEI 0x540 /* Transmit End */ /* Peripheral Module Interrupts - Watchdog Timer (WDT) */ -#define SH7750_EVT_WDT_ITI 0x560 /* Interval Timer Interrupt - (used when WDT operates in - interval timer mode) */ +#define SH7750_EVT_WDT_ITI 0x560 /* Interval Timer Interrupt */ + /* (used when WDT operates in */ + /* interval timer mode) */ /* Peripheral Module Interrupts - Memory Refresh Unit (REF) */ #define SH7750_EVT_REF_RCMI 0x580 /* Compare-match Interrupt */ -#define SH7750_EVT_REF_ROVI 0x5A0 /* Refresh Counter Overflow - interrupt */ +#define SH7750_EVT_REF_ROVI 0x5A0 /* Refresh Counter Overflow */ + /* interrupt */ /* Peripheral Module Interrupts - Hitachi User Debug Interface (H-UDI) */ #define SH7750_EVT_HUDI 0x600 /* UDI interrupt */ @@ -290,11 +290,10 @@ #define SH7750_EVT_DMAC_DMTE3 0x6A0 /* DMAC 3 Transfer End Interrupt */ #define SH7750_EVT_DMAC_DMAE 0x6C0 /* DMAC Address Error Interrupt */ -/* Peripheral Module Interrupts - Serial Communication Interface with FIFO */ -/* (SCIF) */ +/* Peripheral Module Interrupts Serial Communication Interface w/ FIFO (SCIF) */ #define SH7750_EVT_SCIF_ERI 0x700 /* Receive Error */ -#define SH7750_EVT_SCIF_RXI 0x720 /* Receive FIFO Data Full or - Receive Data ready interrupt */ +#define SH7750_EVT_SCIF_RXI 0x720 /* Receive FIFO Data Full or */ + /* Receive Data ready interrupt */ #define SH7750_EVT_SCIF_BRI 0x740 /* Break or overrun error */ #define SH7750_EVT_SCIF_TXI 0x760 /* Transmit FIFO Data Empty */ @@ -305,13 +304,13 @@ #define SH7750_STBCR SH7750_P4_REG32(SH7750_STBCR_REGOFS) #define SH7750_STBCR_A7 SH7750_A7_REG32(SH7750_STBCR_REGOFS) -#define SH7750_STBCR_STBY 0x80 /* Specifies a transition to standby mode: - 0 - Transition to SLEEP mode on SLEEP - 1 - Transition to STANDBY mode on SLEEP */ -#define SH7750_STBCR_PHZ 0x40 /* State of peripheral module pins in - standby mode: - 0 - normal state - 1 - high-impendance state */ +#define SH7750_STBCR_STBY 0x80 /* Specifies a transition to standby mode: */ + /* 0 Transition to SLEEP mode on SLEEP */ + /* 1 Transition to STANDBY mode on SLEEP */ +#define SH7750_STBCR_PHZ 0x40 /* State of peripheral module pins in */ + /* standby mode: */ + /* 0 normal state */ + /* 1 high-impendance state */ #define SH7750_STBCR_PPU 0x20 /* Peripheral module pins pull-up controls */ #define SH7750_STBCR_MSTP4 0x10 /* Stopping the clock supply to DMAC */ @@ -332,16 +331,16 @@ #define SH7750_STBCR2 SH7750_P4_REG32(SH7750_STBCR2_REGOFS) #define SH7750_STBCR2_A7 SH7750_A7_REG32(SH7750_STBCR2_REGOFS) -#define SH7750_STBCR2_DSLP 0x80 /* Specifies transition to deep sleep mode: - 0 - transition to sleep or standby mode - as it is specified in STBY bit - 1 - transition to deep sleep mode on - execution of SLEEP instruction */ -#define SH7750_STBCR2_MSTP6 0x02 /* Stopping the clock supply to Store Queue - in the cache controller */ +#define SH7750_STBCR2_DSLP 0x80 /* Specifies transition to deep sleep mode */ + /* 0 transition to sleep or standby mode */ + /* as it is specified in STBY bit */ + /* 1 transition to deep sleep mode on */ + /* execution of SLEEP instruction */ +#define SH7750_STBCR2_MSTP6 0x02 /* Stopping the clock supply to the */ + /* Store Queue in the cache controller */ #define SH7750_STBCR2_SQ_STP SH7750_STBCR2_MSTP6 -#define SH7750_STBCR2_MSTP5 0x01 /* Stopping the clock supply to the User - Break Controller (UBC) */ +#define SH7750_STBCR2_MSTP5 0x01 /* Stopping the clock supply to the */ + /* User Break Controller (UBC) */ #define SH7750_STBCR2_UBC_STP SH7750_STBCR2_MSTP5 /* @@ -351,9 +350,9 @@ #define SH7750_FRQCR SH7750_P4_REG32(SH7750_FRQCR_REGOFS) #define SH7750_FRQCR_A7 SH7750_A7_REG32(SH7750_FRQCR_REGOFS) -#define SH7750_FRQCR_CKOEN 0x0800 /* Clock Output Enable - 0 - CKIO pin goes to HiZ/pullup - 1 - Clock is output from CKIO */ +#define SH7750_FRQCR_CKOEN 0x0800 /* Clock Output Enable */ + /* 0 - CKIO pin goes to HiZ/pullup */ + /* 1 - Clock is output from CKIO */ #define SH7750_FRQCR_PLL1EN 0x0400 /* PLL circuit 1 enable */ #define SH7750_FRQCR_PLL2EN 0x0200 /* PLL circuit 2 enable */ @@ -373,8 +372,8 @@ #define SH7750_FRQCR_BFCDIV6 0x0020 /* 4 - * 1/6 */ #define SH7750_FRQCR_BFCDIV8 0x0028 /* 5 - * 1/8 */ -#define SH7750_FRQCR_PFC 0x0007 /* Peripheral module clock frequency - division ratio: */ +#define SH7750_FRQCR_PFC 0x0007 /* Peripheral module clock frequency */ + /* division ratio: */ #define SH7750_FRQCR_PFCDIV2 0x0000 /* 0 - * 1/2 */ #define SH7750_FRQCR_PFCDIV3 0x0001 /* 1 - * 1/3 */ #define SH7750_FRQCR_PFCDIV4 0x0002 /* 2 - * 1/4 */ @@ -389,17 +388,15 @@ #define SH7750_WTCNT_REGOFS 0xC00008 /* offset */ #define SH7750_WTCNT SH7750_P4_REG32(SH7750_WTCNT_REGOFS) #define SH7750_WTCNT_A7 SH7750_A7_REG32(SH7750_WTCNT_REGOFS) -#define SH7750_WTCNT_KEY 0x5A00 /* When WTCNT byte register written, - you have to set the upper byte to - 0x5A */ +#define SH7750_WTCNT_KEY 0x5A00 /* When WTCNT byte register written, you */ + /* have to set the upper byte to 0x5A */ /* Watchdog Timer Control/Status register - WTCSR */ #define SH7750_WTCSR_REGOFS 0xC0000C /* offset */ #define SH7750_WTCSR SH7750_P4_REG32(SH7750_WTCSR_REGOFS) #define SH7750_WTCSR_A7 SH7750_A7_REG32(SH7750_WTCSR_REGOFS) -#define SH7750_WTCSR_KEY 0xA500 /* When WTCSR byte register written, - you have to set the upper byte to - 0xA5 */ +#define SH7750_WTCSR_KEY 0xA500 /* When WTCSR byte register written, you */ + /* have to set the upper byte to 0xA5 */ #define SH7750_WTCSR_TME 0x80 /* Timer enable (1-upcount start) */ #define SH7750_WTCSR_MODE 0x40 /* Timer Mode Select: */ #define SH7750_WTCSR_MODE_WT 0x40 /* Watchdog Timer Mode */ @@ -540,10 +537,10 @@ #define SH7750_RCR2_RTCEN 0x08 /* RTC Crystal Oscillator is Operated */ #define SH7750_RCR2_ADJ 0x04 /* 30-Second Adjastment */ #define SH7750_RCR2_RESET 0x02 /* Frequency divider circuits are reset */ -#define SH7750_RCR2_START 0x01 /* 0 - sec, min, hr, day-of-week, month, - year counters are stopped - 1 - sec, min, hr, day-of-week, month, - year counters operate normally */ +#define SH7750_RCR2_START 0x01 /* 0 - sec, min, hr, day-of-week, month, */ + /* year counters are stopped */ + /* 1 - sec, min, hr, day-of-week, month, */ + /* year counters operate normally */ /* * Bus State Controller - BSC */ @@ -554,96 +551,98 @@ #define SH7750_BCR1_ENDIAN 0x80000000 /* Endianness (1 - little endian) */ #define SH7750_BCR1_MASTER 0x40000000 /* Master/Slave mode (1-master) */ #define SH7750_BCR1_A0MPX 0x20000000 /* Area 0 Memory Type (0-SRAM,1-MPX) */ -#define SH7750_BCR1_IPUP 0x02000000 /* Input Pin Pull-up Control: - 0 - pull-up resistor is on for - control input pins - 1 - pull-up resistor is off */ -#define SH7750_BCR1_OPUP 0x01000000 /* Output Pin Pull-up Control: - 0 - pull-up resistor is on for - control output pins - 1 - pull-up resistor is off */ -#define SH7750_BCR1_A1MBC 0x00200000 /* Area 1 SRAM Byte Control Mode: - 0 - Area 1 SRAM is set to - normal mode - 1 - Area 1 SRAM is set to byte - control mode */ -#define SH7750_BCR1_A4MBC 0x00100000 /* Area 4 SRAM Byte Control Mode: - 0 - Area 4 SRAM is set to - normal mode - 1 - Area 4 SRAM is set to byte - control mode */ -#define SH7750_BCR1_BREQEN 0x00080000 /* BREQ Enable: - 0 - External requests are not - accepted - 1 - External requests are - accepted */ -#define SH7750_BCR1_PSHR 0x00040000 /* Partial Sharing Bit: - 0 - Master Mode - 1 - Partial-sharing Mode */ -#define SH7750_BCR1_MEMMPX 0x00020000 /* Area 1 to 6 MPX Interface: - 0 - SRAM/burst ROM interface - 1 - MPX interface */ -#define SH7750_BCR1_HIZMEM 0x00008000 /* High Impendance Control. Specifies - the state of A[25:0], BS\, CSn\, - RD/WR\, CE2A\, CE2B\ in standby - mode and when bus is released: - 0 - signals go to High-Z mode - 1 - signals driven */ -#define SH7750_BCR1_HIZCNT 0x00004000 /* High Impendance Control. Specifies - the state of the RAS\, RAS2\, WEn\, - CASn\, DQMn, RD\, CASS\, FRAME\, - RD2\ signals in standby mode and - when bus is released: - 0 - signals go to High-Z mode - 1 - signals driven */ +#define SH7750_BCR1_IPUP 0x02000000 /* Input Pin Pull-up Control: */ + /* 0 - pull-up resistor is on for */ + /* control input pins */ + /* 1 - pull-up resistor is off */ +#define SH7750_BCR1_OPUP 0x01000000 /* Output Pin Pull-up Control: */ + /* 0 - pull-up resistor is on for */ + /* control output pins */ + /* 1 - pull-up resistor is off */ +#define SH7750_BCR1_A1MBC 0x00200000 /* Area 1 SRAM Byte Control Mode: */ + /* 0 - Area 1 SRAM is set to */ + /* normal mode */ + /* 1 - Area 1 SRAM is set to byte */ + /* control mode */ +#define SH7750_BCR1_A4MBC 0x00100000 /* Area 4 SRAM Byte Control Mode: */ + /* 0 - Area 4 SRAM is set to */ + /* normal mode */ + /* 1 - Area 4 SRAM is set to byte */ + /* control mode */ +#define SH7750_BCR1_BREQEN 0x00080000 /* BREQ Enable: */ + /* 0 - External requests are not */ + /* accepted */ + /* 1 - External requests are */ + /* accepted */ +#define SH7750_BCR1_PSHR 0x00040000 /* Partial Sharing Bit: */ + /* 0 - Master Mode */ + /* 1 - Partial-sharing Mode */ +#define SH7750_BCR1_MEMMPX 0x00020000 /* Area 1 to 6 MPX Interface: */ + /* 0 - SRAM/burst ROM interface */ + /* 1 - MPX interface */ +#define SH7750_BCR1_HIZMEM 0x00008000 /* High Impendance Control. */ + /* Specifies the state of A[25:0], */ + /* BS\, CSn\, RD/WR\, CE2A\, CE2B\ */ + /* in standby mode and when bus is */ + /* released: */ + /* 0 - signals go to High-Z mode */ + /* 1 - signals driven */ +#define SH7750_BCR1_HIZCNT 0x00004000 /* High Impendance Control. */ + /* Specifies the state of the */ + /* RAS\, RAS2\, WEn\, CASn\, DQMn, */ + /* RD\, CASS\, FRAME\, RD2\ */ + /* signals in standby mode and */ + /* when bus is released: */ + /* 0 - signals go to High-Z mode */ + /* 1 - signals driven */ #define SH7750_BCR1_A0BST 0x00003800 /* Area 0 Burst ROM Control */ #define SH7750_BCR1_A0BST_SRAM 0x0000 /* Area 0 accessed as SRAM i/f */ -#define SH7750_BCR1_A0BST_ROM4 0x0800 /* Area 0 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM8 0x1000 /* Area 0 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM16 0x1800 /* Area 0 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM32 0x2000 /* Area 0 accessed as burst ROM - interface, 32 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM4 0x0800 /* Area 0 accessed as burst ROM */ + /* interface, 4 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM8 0x1000 /* Area 0 accessed as burst ROM */ + /* interface, 8 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM16 0x1800 /* Area 0 accessed as burst ROM */ + /* interface, 16 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM32 0x2000 /* Area 0 accessed as burst ROM */ + /* interface, 32 cosequtive access */ #define SH7750_BCR1_A5BST 0x00000700 /* Area 5 Burst ROM Control */ #define SH7750_BCR1_A5BST_SRAM 0x0000 /* Area 5 accessed as SRAM i/f */ -#define SH7750_BCR1_A5BST_ROM4 0x0100 /* Area 5 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM8 0x0200 /* Area 5 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM16 0x0300 /* Area 5 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM32 0x0400 /* Area 5 accessed as burst ROM - interface, 32 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM4 0x0100 /* Area 5 accessed as burst ROM */ + /* interface, 4 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM8 0x0200 /* Area 5 accessed as burst ROM */ + /* interface, 8 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM16 0x0300 /* Area 5 accessed as burst ROM */ + /* interface, 16 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM32 0x0400 /* Area 5 accessed as burst ROM */ + /* interface, 32 cosequtive access */ #define SH7750_BCR1_A6BST 0x000000E0 /* Area 6 Burst ROM Control */ #define SH7750_BCR1_A6BST_SRAM 0x0000 /* Area 6 accessed as SRAM i/f */ -#define SH7750_BCR1_A6BST_ROM4 0x0020 /* Area 6 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM8 0x0040 /* Area 6 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM16 0x0060 /* Area 6 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM32 0x0080 /* Area 6 accessed as burst ROM - interface, 32 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM4 0x0020 /* Area 6 accessed as burst ROM */ + /* interface, 4 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM8 0x0040 /* Area 6 accessed as burst ROM */ + /* interface, 8 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM16 0x0060 /* Area 6 accessed as burst ROM */ + /* interface, 16 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM32 0x0080 /* Area 6 accessed as burst ROM */ + /* interface, 32 cosequtive access */ #define SH7750_BCR1_DRAMTP 0x001C /* Area 2 and 3 Memory Type */ -#define SH7750_BCR1_DRAMTP_2SRAM_3SRAM 0x0000 /* Area 2 and 3 are SRAM or MPX - interface. */ -#define SH7750_BCR1_DRAMTP_2SRAM_3SDRAM 0x0008 /* Area 2 - SRAM/MPX, Area 3 - - synchronous DRAM */ -#define SH7750_BCR1_DRAMTP_2SDRAM_3SDRAM 0x000C /* Area 2 and 3 are synchronous - DRAM interface */ -#define SH7750_BCR1_DRAMTP_2SRAM_3DRAM 0x0010 /* Area 2 - SRAM/MPX, Area 3 - - DRAM interface */ -#define SH7750_BCR1_DRAMTP_2DRAM_3DRAM 0x0014 /* Area 2 and 3 are DRAM - interface */ +#define SH7750_BCR1_DRAMTP_2SRAM_3SRAM 0x0000 /* Area 2 and 3 are SRAM or */ + /* MPX interface. */ +#define SH7750_BCR1_DRAMTP_2SRAM_3SDRAM 0x0008 /* Area 2 - SRAM/MPX, Area 3 */ + /* synchronous DRAM */ +#define SH7750_BCR1_DRAMTP_2SDRAM_3SDRAM 0x000C /* Area 2 and 3 are */ + /* synchronous DRAM interface */ +#define SH7750_BCR1_DRAMTP_2SRAM_3DRAM 0x0010 /* Area 2 - SRAM/MPX, Area 3 */ + /* DRAM interface */ +#define SH7750_BCR1_DRAMTP_2DRAM_3DRAM 0x0014 /* Area 2 and 3 are DRAM */ + /* interface */ -#define SH7750_BCR1_A56PCM 0x00000001 /* Area 5 and 6 Bus Type: - 0 - SRAM interface - 1 - PCMCIA interface */ +#define SH7750_BCR1_A56PCM 0x00000001 /* Area 5 and 6 Bus Type: */ + /* 0 - SRAM interface */ + /* 1 - PCMCIA interface */ /* Bus Control Register 2 (half) - BCR2 */ #define SH7750_BCR2_REGOFS 0x800004 /* offset */ @@ -668,16 +667,16 @@ #define SH7750_BCR2_SZ_8 1 /* 8 bits */ #define SH7750_BCR2_SZ_16 2 /* 16 bits */ #define SH7750_BCR2_SZ_32 3 /* 32 bits */ -#define SH7750_BCR2_PORTEN 0x0001 /* Port Function Enable : - 0 - D51-D32 are not used as a port - 1 - D51-D32 are used as a port */ +#define SH7750_BCR2_PORTEN 0x0001 /* Port Function Enable */ + /* 0 - D51-D32 are not used as a port */ + /* 1 - D51-D32 are used as a port */ /* Wait Control Register 1 - WCR1 */ #define SH7750_WCR1_REGOFS 0x800008 /* offset */ #define SH7750_WCR1 SH7750_P4_REG32(SH7750_WCR1_REGOFS) #define SH7750_WCR1_A7 SH7750_A7_REG32(SH7750_WCR1_REGOFS) -#define SH7750_WCR1_DMAIW 0x70000000 /* DACK Device Inter-Cycle Idle - specification */ +#define SH7750_WCR1_DMAIW 0x70000000 /* DACK Device Inter-Cycle Idle */ + /* specification */ #define SH7750_WCR1_DMAIW_S 28 #define SH7750_WCR1_A6IW 0x07000000 /* Area 6 Inter-Cycle Idle spec. */ #define SH7750_WCR1_A6IW_S 24 @@ -794,8 +793,8 @@ #define SH7750_MCR_RASD 0x80000000 /* RAS Down mode */ #define SH7750_MCR_MRSET 0x40000000 /* SDRAM Mode Register Set */ #define SH7750_MCR_PALL 0x00000000 /* SDRAM Precharge All cmd. Mode */ -#define SH7750_MCR_TRC 0x38000000 /* RAS Precharge Time at End of - Refresh: */ +#define SH7750_MCR_TRC 0x38000000 /* RAS Precharge Time at End of */ + /* Refresh: */ #define SH7750_MCR_TRC_0 0x00000000 /* 0 */ #define SH7750_MCR_TRC_3 0x08000000 /* 3 */ #define SH7750_MCR_TRC_6 0x10000000 /* 6 */ @@ -809,10 +808,10 @@ #define SH7750_MCR_TCAS_1 0x00000000 /* 1 */ #define SH7750_MCR_TCAS_2 0x00800000 /* 2 */ -#define SH7750_MCR_TPC 0x00380000 /* DRAM: RAS Precharge Period - SDRAM: minimum number of cycles - until the next bank active cmd - is output after precharging */ +#define SH7750_MCR_TPC 0x00380000 /* DRAM: RAS Precharge Period */ + /* SDRAM: minimum number of cycles */ + /* until the next bank active cmd */ + /* is output after precharging */ #define SH7750_MCR_TPC_S 19 #define SH7750_MCR_TPC_SDRAM_1 0x00000000 /* 1 cycle */ #define SH7750_MCR_TPC_SDRAM_2 0x00080000 /* 2 cycles */ @@ -823,9 +822,10 @@ #define SH7750_MCR_TPC_SDRAM_7 0x00300000 /* 7 cycles */ #define SH7750_MCR_TPC_SDRAM_8 0x00380000 /* 8 cycles */ -#define SH7750_MCR_RCD 0x00030000 /* DRAM: RAS-CAS Assertion Delay time - SDRAM: bank active-read/write cmd - delay time */ +#define SH7750_MCR_RCD 0x00030000 /* DRAM: RAS-CAS Assertion Delay */ + /* time */ + /* SDRAM: bank active-read/write */ + /* command delay time */ #define SH7750_MCR_RCD_DRAM_2 0x00000000 /* DRAM delay 2 clocks */ #define SH7750_MCR_RCD_DRAM_3 0x00010000 /* DRAM delay 3 clocks */ #define SH7750_MCR_RCD_DRAM_4 0x00020000 /* DRAM delay 4 clocks */ @@ -841,10 +841,10 @@ #define SH7750_MCR_TRWL_4 0x00006000 /* 4 */ #define SH7750_MCR_TRWL_5 0x00008000 /* 5 */ -#define SH7750_MCR_TRAS 0x00001C00 /* DRAM: CAS-Before-RAS Refresh RAS - asserting period - SDRAM: Command interval after - synchronous DRAM refresh */ +#define SH7750_MCR_TRAS 0x00001C00 /* DRAM: CAS-Before-RAS Refresh RAS */ + /* asserting period */ + /* SDRAM: Command interval after */ + /* synchronous DRAM refresh */ #define SH7750_MCR_TRAS_DRAM_2 0x00000000 /* 2 */ #define SH7750_MCR_TRAS_DRAM_3 0x00000400 /* 3 */ #define SH7750_MCR_TRAS_DRAM_4 0x00000800 /* 4 */ @@ -898,30 +898,30 @@ #define SH7750_PCR SH7750_P4_REG32(SH7750_PCR_REGOFS) #define SH7750_PCR_A7 SH7750_A7_REG32(SH7750_PCR_REGOFS) -#define SH7750_PCR_A5PCW 0xC000 /* Area 5 PCMCIA Wait - Number of wait - states to be added to the number of - waits specified by WCR2 in a low-speed - PCMCIA wait cycle */ +#define SH7750_PCR_A5PCW 0xC000 /* Area 5 PCMCIA Wait - Number of wait */ + /* states to be added to the number of */ + /* waits specified by WCR2 in a */ + /* low-speed PCMCIA wait cycle */ #define SH7750_PCR_A5PCW_0 0x0000 /* 0 waits inserted */ #define SH7750_PCR_A5PCW_15 0x4000 /* 15 waits inserted */ #define SH7750_PCR_A5PCW_30 0x8000 /* 30 waits inserted */ #define SH7750_PCR_A5PCW_50 0xC000 /* 50 waits inserted */ -#define SH7750_PCR_A6PCW 0x3000 /* Area 6 PCMCIA Wait - Number of wait - states to be added to the number of - waits specified by WCR2 in a low-speed - PCMCIA wait cycle */ +#define SH7750_PCR_A6PCW 0x3000 /* Area 6 PCMCIA Wait - Number of wait */ + /* states to be added to the number of */ + /* waits specified by WCR2 in a */ + /* low-speed PCMCIA wait cycle */ #define SH7750_PCR_A6PCW_0 0x0000 /* 0 waits inserted */ #define SH7750_PCR_A6PCW_15 0x1000 /* 15 waits inserted */ #define SH7750_PCR_A6PCW_30 0x2000 /* 30 waits inserted */ #define SH7750_PCR_A6PCW_50 0x3000 /* 50 waits inserted */ -#define SH7750_PCR_A5TED 0x0E00 /* Area 5 Address-OE\/WE\ Assertion Delay, - delay time from address output to - OE\/WE\ assertion on the connected - PCMCIA interface */ +#define SH7750_PCR_A5TED 0x0E00 /* Area 5 Addr-OE\/WE\ Assertion Delay */ + /* delay time from address output to */ + /* OE\/WE\ assertion on the connected */ + /* PCMCIA interface */ #define SH7750_PCR_A5TED_S 9 -#define SH7750_PCR_A6TED 0x01C0 /* Area 6 Address-OE\/WE\ Assertion Delay */ +#define SH7750_PCR_A6TED 0x01C0 /* Area 6 Addr-OE\/WE\ Assertion Delay */ #define SH7750_PCR_A6TED_S 6 #define SH7750_PCR_TED_0WS 0 /* 0 Waits inserted */ @@ -933,10 +933,10 @@ #define SH7750_PCR_TED_12WS 6 /* 12 Waits inserted */ #define SH7750_PCR_TED_15WS 7 /* 15 Waits inserted */ -#define SH7750_PCR_A5TEH 0x0038 /* Area 5 OE\/WE\ Negation Address delay, - address hold delay time from OE\/WE\ - negation in a write on the connected - PCMCIA interface */ +#define SH7750_PCR_A5TEH 0x0038 /* Area 5 OE\/WE\ Negation Addr delay, */ + /* address hold delay time from OE\/WE\ */ + /* negation in a write on the connected */ + /* PCMCIA interface */ #define SH7750_PCR_A5TEH_S 3 #define SH7750_PCR_A6TEH 0x0007 /* Area 6 OE\/WE\ Negation Address delay */ @@ -957,9 +957,9 @@ #define SH7750_RTCSR_A7 SH7750_A7_REG32(SH7750_RTCSR_REGOFS) #define SH7750_RTCSR_KEY 0xA500 /* RTCSR write key */ -#define SH7750_RTCSR_CMF 0x0080 /* Compare-Match Flag (indicates a - match between the refresh timer - counter and refresh time constant) */ +#define SH7750_RTCSR_CMF 0x0080 /* Compare-Match Flag (indicates a */ + /* match between the refresh timer */ + /* counter and refresh time constant) */ #define SH7750_RTCSR_CMIE 0x0040 /* Compare-Match Interrupt Enable */ #define SH7750_RTCSR_CKS 0x0038 /* Refresh Counter Clock Selects */ #define SH7750_RTCSR_CKS_DIS 0x0000 /* Clock Input Disabled */ @@ -972,8 +972,8 @@ #define SH7750_RTCSR_CKS_CKIO_DIV4096 0x0038 /* Bus Clock / 4096 */ #define SH7750_RTCSR_OVF 0x0004 /* Refresh Count Overflow Flag */ -#define SH7750_RTCSR_OVIE 0x0002 /* Refresh Count Overflow Interrupt - Enable */ +#define SH7750_RTCSR_OVIE 0x0002 /* Refresh Count Overflow Interrupt */ + /* Enable */ #define SH7750_RTCSR_LMTS 0x0001 /* Refresh Count Overflow Limit Select */ #define SH7750_RTCSR_LMTS_1024 0x0000 /* Count Limit is 1024 */ #define SH7750_RTCSR_LMTS_512 0x0001 /* Count Limit is 512 */ @@ -1076,9 +1076,9 @@ #define SH7750_CHCR_SSA_AMEM8 0xC0000000 /* 8-bit attribute memory space */ #define SH7750_CHCR_SSA_AMEM16 0xE0000000 /* 16-bit attribute memory space */ -#define SH7750_CHCR_STC 0x10000000 /* Source Address Wait Control Select, - specifies CS5 or CS6 space wait - control for PCMCIA access */ +#define SH7750_CHCR_STC 0x10000000 /* Source Addr Wait Control Select */ + /* specifies CS5 or CS6 space wait */ + /* control for PCMCIA access */ #define SH7750_CHCR_DSA 0x0E000000 /* Source Address Space Attribute */ #define SH7750_CHCR_DSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ @@ -1090,10 +1090,10 @@ #define SH7750_CHCR_DSA_AMEM8 0x0C000000 /* 8-bit attribute memory space */ #define SH7750_CHCR_DSA_AMEM16 0x0E000000 /* 16-bit attribute memory space */ -#define SH7750_CHCR_DTC 0x01000000 /* Destination Address Wait Control - Select, specifies CS5 or CS6 - space wait control for PCMCIA - access */ +#define SH7750_CHCR_DTC 0x01000000 /* Destination Address Wait Control */ + /* Select, specifies CS5 or CS6 */ + /* space wait control for PCMCIA */ + /* access */ #define SH7750_CHCR_DS 0x00080000 /* DREQ\ Select : */ #define SH7750_CHCR_DS_LOWLVL 0x00000000 /* Low Level Detection */ @@ -1122,49 +1122,49 @@ #define SH7750_CHCR_SM_DEC 0x00002000 /* Source Addr Decremented */ #define SH7750_CHCR_RS 0x00000F00 /* Request Source Select: */ -#define SH7750_CHCR_RS_ER_DA_EA_TO_EA 0x000 /* External Request, Dual Address - Mode (External Addr Space-> - External Addr Space) */ -#define SH7750_CHCR_RS_ER_SA_EA_TO_ED 0x200 /* External Request, Single - Address Mode (External Addr - Space -> External Device) */ -#define SH7750_CHCR_RS_ER_SA_ED_TO_EA 0x300 /* External Request, Single - Address Mode, (External - Device -> External Addr - Space) */ -#define SH7750_CHCR_RS_AR_EA_TO_EA 0x400 /* Auto-Request (External Addr - Space -> External Addr Space) */ +#define SH7750_CHCR_RS_ER_DA_EA_TO_EA 0x000 /* External Request, Dual Addr */ + /* Mode, External Addr Space */ + /* -> External Addr Space) */ +#define SH7750_CHCR_RS_ER_SA_EA_TO_ED 0x200 /* External Request, Single */ + /* Address Mode (Ext. Addr */ + /* Space -> External Device) */ +#define SH7750_CHCR_RS_ER_SA_ED_TO_EA 0x300 /* External Request, Single */ + /* Address Mode, (External */ + /* Device -> External Addr */ + /* Space) */ +#define SH7750_CHCR_RS_AR_EA_TO_EA 0x400 /* Auto-Request (External Addr */ + /* Space -> Ext. Addr Space) */ -#define SH7750_CHCR_RS_AR_EA_TO_OCP 0x500 /* Auto-Request (External Addr - Space -> On-chip Peripheral - Module) */ -#define SH7750_CHCR_RS_AR_OCP_TO_EA 0x600 /* Auto-Request (On-chip - Peripheral Module -> - External Addr Space */ -#define SH7750_CHCR_RS_SCITX_EA_TO_SC 0x800 /* SCI Transmit-Data-Empty intr - transfer request (external - address space -> SCTDR1) */ -#define SH7750_CHCR_RS_SCIRX_SC_TO_EA 0x900 /* SCI Receive-Data-Full intr - transfer request (SCRDR1 -> - External Addr Space) */ -#define SH7750_CHCR_RS_SCIFTX_EA_TO_SC 0xA00 /* SCIF Transmit-Data-Empty intr - transfer request (external - address space -> SCFTDR1) */ -#define SH7750_CHCR_RS_SCIFRX_SC_TO_EA 0xB00 /* SCIF Receive-Data-Full intr - transfer request (SCFRDR2 -> - External Addr Space) */ -#define SH7750_CHCR_RS_TMU2_EA_TO_EA 0xC00 /* TMU Channel 2 (input capture - interrupt), (external address - space -> external address - space) */ -#define SH7750_CHCR_RS_TMU2_EA_TO_OCP 0xD00 /* TMU Channel 2 (input capture - interrupt), (external address - space -> on-chip peripheral - module) */ -#define SH7750_CHCR_RS_TMU2_OCP_TO_EA 0xE00 /* TMU Channel 2 (input capture - interrupt), (on-chip - peripheral module -> external - address space) */ +#define SH7750_CHCR_RS_AR_EA_TO_OCP 0x500 /* Auto-Request (External Addr */ + /* Space -> On-chip */ + /* Peripheral Module) */ +#define SH7750_CHCR_RS_AR_OCP_TO_EA 0x600 /* Auto-Request (On-chip */ + /* Peripheral Module -> */ + /* External Addr Space */ +#define SH7750_CHCR_RS_SCITX_EA_TO_SC 0x800 /* SCI Transmit-Data-Empty intr */ + /* transfer request (external */ + /* address space -> SCTDR1) */ +#define SH7750_CHCR_RS_SCIRX_SC_TO_EA 0x900 /* SCI Receive-Data-Full intr */ + /* transfer request (SCRDR1 */ + /* -> External Addr Space) */ +#define SH7750_CHCR_RS_SCIFTX_EA_TO_SC 0xA00 /* SCIF TX-Data-Empty intr */ + /* transfer request (external */ + /* address space -> SCFTDR1) */ +#define SH7750_CHCR_RS_SCIFRX_SC_TO_EA 0xB00 /* SCIF Receive-Data-Full intr */ + /* transfer request (SCFRDR2 */ + /* -> External Addr Space) */ +#define SH7750_CHCR_RS_TMU2_EA_TO_EA 0xC00 /* TMU Channel 2 (input capture */ + /* interrupt), (external */ + /* address space -> external */ + /* address space) */ +#define SH7750_CHCR_RS_TMU2_EA_TO_OCP 0xD00 /* TMU Channel 2 (input capture */ + /* interrupt), (external */ + /* address space -> on-chip */ + /* peripheral module) */ +#define SH7750_CHCR_RS_TMU2_OCP_TO_EA 0xE00 /* TMU Channel 2 (input capture */ + /* interrupt), (on-chip */ + /* peripheral module -> */ + /* external address space) */ #define SH7750_CHCR_TM 0x00000080 /* Transmit mode: */ #define SH7750_CHCR_TM_CSTEAL 0x00000000 /* Cycle Steal Mode */ @@ -1255,22 +1255,22 @@ #define SH7750_ICR_MAI 0x4000 /* NMI Interrupt Mask */ #define SH7750_ICR_NMIB 0x0200 /* NMI Block Mode: */ -#define SH7750_ICR_NMIB_BLK 0x0000 /* NMI requests held pending while - SR.BL bit is set to 1 */ -#define SH7750_ICR_NMIB_NBLK 0x0200 /* NMI requests detected when SR.BL bit - set to 1 */ +#define SH7750_ICR_NMIB_BLK 0x0000 /* NMI requests held pending while */ + /* SR.BL bit is set to 1 */ +#define SH7750_ICR_NMIB_NBLK 0x0200 /* NMI requests detected when SR.BL */ + /* bit set to 1 */ #define SH7750_ICR_NMIE 0x0100 /* NMI Edge Select: */ -#define SH7750_ICR_NMIE_FALL 0x0000 /* Interrupt request detected on falling - edge of NMI input */ -#define SH7750_ICR_NMIE_RISE 0x0100 /* Interrupt request detected on rising - edge of NMI input */ +#define SH7750_ICR_NMIE_FALL 0x0000 /* Interrupt request detected on */ + /* falling edge of NMI input */ +#define SH7750_ICR_NMIE_RISE 0x0100 /* Interrupt request detected on */ + /* rising edge of NMI input */ #define SH7750_ICR_IRLM 0x0080 /* IRL Pin Mode: */ -#define SH7750_ICR_IRLM_ENC 0x0000 /* IRL\ pins used as a level-encoded - interrupt requests */ -#define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four independent - interrupt requests */ +#define SH7750_ICR_IRLM_ENC 0x0000 /* IRL\ pins used as a level-encoded */ + /* interrupt requests */ +#define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four */ + /* independent interrupt requests */ /* * User Break Controller registers diff --git a/hw/sh4/shix.c b/hw/sh4/shix.c index b0579aa0f1..6b39de417f 100644 --- a/hw/sh4/shix.c +++ b/hw/sh4/shix.c @@ -22,11 +22,11 @@ * THE SOFTWARE. */ /* - Shix 2.0 board by Alexis Polti, described at - https://web.archive.org/web/20070917001736/perso.enst.fr/~polti/realisations/shix20 - - More information in target/sh4/README.sh4 -*/ + * Shix 2.0 board by Alexis Polti, described at + * https://web.archive.org/web/20070917001736/perso.enst.fr/~polti/realisations/shix20 + * + * More information in target/sh4/README.sh4 + */ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 58af1a1edb..4f765b339b 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -104,9 +104,10 @@ static void sh_timer_write(void *opaque, hwaddr offset, case OFFSET_TCR: ptimer_transaction_begin(s->timer); if (s->enabled) { - /* Pause the timer if it is running. This may cause some - inaccuracy dure to rounding, but avoids a whole lot of other - messyness. */ + /* + * Pause the timer if it is running. This may cause some inaccuracy + * dure to rounding, but avoids a whole lot of other messyness + */ ptimer_stop(s->timer); } freq = s->freq; From f94bff1337ff525e2ff458b8e4cd57f9561acde3 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0868/1334] hw/sh4: Coding style: White space fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Yoshinori Sato Message-Id: <91698c54fa493a4cfe93546211206439787d4b78.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 23 ++++++++++------------- hw/intc/sh_intc.c | 39 +++++++++++++++++++++++---------------- hw/pci-host/sh_pci.c | 10 ++++------ hw/sh4/r2d.c | 39 ++++++++++++++++++++------------------- hw/sh4/sh7750.c | 26 +++++++++++++------------- hw/sh4/sh7750_regnames.c | 5 +++-- hw/sh4/sh7750_regs.h | 18 +++++++++--------- hw/sh4/shix.c | 2 +- hw/timer/sh_timer.c | 22 ++++++++++++++++------ include/hw/sh4/sh.h | 10 +++++----- 10 files changed, 104 insertions(+), 90 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 05ae8e84ce..3fdb9f9a99 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -75,7 +75,7 @@ typedef struct { qemu_irq bri; } sh_serial_state; -static void sh_serial_clear_fifo(sh_serial_state * s) +static void sh_serial_clear_fifo(sh_serial_state *s) { memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); s->rx_cnt = 0; @@ -93,7 +93,7 @@ static void sh_serial_write(void *opaque, hwaddr offs, printf("sh_serial: write offs=0x%02x val=0x%02x\n", offs, val); #endif - switch(offs) { + switch (offs) { case 0x00: /* SMR */ s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); return; @@ -131,7 +131,7 @@ static void sh_serial_write(void *opaque, hwaddr offs, #endif } if (s->feat & SH_SERIAL_FEAT_SCIF) { - switch(offs) { + switch (offs) { case 0x10: /* FSR */ if (!(val & (1 << 6))) s->flags &= ~SH_SERIAL_FLAG_TEND; @@ -178,9 +178,8 @@ static void sh_serial_write(void *opaque, hwaddr offs, case 0x24: /* LSR */ return; } - } - else { - switch(offs) { + } else { + switch (offs) { #if 0 case 0x0c: ret = s->dr; @@ -207,7 +206,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, uint32_t ret = ~0; #if 0 - switch(offs) { + switch (offs) { case 0x00: ret = s->smr; break; @@ -223,7 +222,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, } #endif if (s->feat & SH_SERIAL_FEAT_SCIF) { - switch(offs) { + switch (offs) { case 0x00: /* SMR */ ret = s->smr; break; @@ -270,9 +269,8 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, ret = 0; break; } - } - else { - switch(offs) { + } else { + switch (offs) { #if 0 case 0x0c: ret = s->dr; @@ -397,8 +395,7 @@ void sh_serial_init(MemoryRegion *sysmem, if (feat & SH_SERIAL_FEAT_SCIF) { s->fcr = 0; - } - else { + } else { s->dr = 0xff; } diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 84eec7d4ba..67005081e3 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -71,18 +71,18 @@ void sh_intc_toggle_source(struct intc_source *source, enable_changed == -1 ? "disabled " : "", source->pending ? "pending" : ""); #endif - } + } } -static void sh_intc_set_irq (void *opaque, int n, int level) +static void sh_intc_set_irq(void *opaque, int n, int level) { - struct intc_desc *desc = opaque; - struct intc_source *source = &(desc->sources[n]); + struct intc_desc *desc = opaque; + struct intc_source *source = &(desc->sources[n]); - if (level && !source->asserted) - sh_intc_toggle_source(source, 0, 1); - else if (!level && source->asserted) - sh_intc_toggle_source(source, 0, -1); + if (level && !source->asserted) + sh_intc_toggle_source(source, 0, 1); + else if (!level && source->asserted) + sh_intc_toggle_source(source, 0, -1); } int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) @@ -236,7 +236,7 @@ static uint64_t sh_intc_read(void *opaque, hwaddr offset, printf("sh_intc_read 0x%lx\n", (unsigned long) offset); #endif - sh_intc_locate(desc, (unsigned long)offset, &valuep, + sh_intc_locate(desc, (unsigned long)offset, &valuep, &enum_ids, &first, &width, &mode); return *valuep; } @@ -257,14 +257,20 @@ static void sh_intc_write(void *opaque, hwaddr offset, printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value); #endif - sh_intc_locate(desc, (unsigned long)offset, &valuep, + sh_intc_locate(desc, (unsigned long)offset, &valuep, &enum_ids, &first, &width, &mode); switch (mode) { - case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break; - case INTC_MODE_DUAL_SET: value |= *valuep; break; - case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break; - default: abort(); + case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: + break; + case INTC_MODE_DUAL_SET: + value |= *valuep; + break; + case INTC_MODE_DUAL_CLR: + value = *valuep & ~value; + break; + default: + abort(); } for (k = 0; k <= first; k++) { @@ -465,7 +471,7 @@ int sh_intc_init(MemoryRegion *sysmem, } desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources); - + memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc, "interrupt-controller", 0x100000000ULL); @@ -507,7 +513,8 @@ void sh_intc_set_irl(void *opaque, int n, int level) int i, irl = level ^ 15; for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) { if (i == irl) - sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1); + sh_intc_toggle_source(s, s->enable_count ? 0 : 1, + s->asserted ? 0 : 1); else if (s->asserted) sh_intc_toggle_source(s, 0, -1); diff --git a/hw/pci-host/sh_pci.c b/hw/pci-host/sh_pci.c index 08c1562e22..719d6ca2a6 100644 --- a/hw/pci-host/sh_pci.c +++ b/hw/pci-host/sh_pci.c @@ -49,13 +49,12 @@ struct SHPCIState { uint32_t iobr; }; -static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val, - unsigned size) +static void sh_pci_reg_write(void *p, hwaddr addr, uint64_t val, unsigned size) { SHPCIState *pcic = p; PCIHostState *phb = PCI_HOST_BRIDGE(pcic); - switch(addr) { + switch (addr) { case 0 ... 0xfc: stl_le_p(pcic->dev->config + addr, val); break; @@ -75,13 +74,12 @@ static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val, } } -static uint64_t sh_pci_reg_read (void *p, hwaddr addr, - unsigned size) +static uint64_t sh_pci_reg_read(void *p, hwaddr addr, unsigned size) { SHPCIState *pcic = p; PCIHostState *phb = PCI_HOST_BRIDGE(pcic); - switch(addr) { + switch (addr) { case 0 ... 0xfc: return ldl_le_p(pcic->dev->config + addr); case 0x1c0: diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 46f1fae48c..216d6e24a1 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -96,19 +96,19 @@ enum r2d_fpga_irq { }; static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = { - [CF_IDE] = { 1, 1<<9 }, - [CF_CD] = { 2, 1<<8 }, - [PCI_INTA] = { 9, 1<<14 }, - [PCI_INTB] = { 10, 1<<13 }, - [PCI_INTC] = { 3, 1<<12 }, - [PCI_INTD] = { 0, 1<<11 }, - [SM501] = { 4, 1<<10 }, - [KEY] = { 5, 1<<6 }, - [RTC_A] = { 6, 1<<5 }, - [RTC_T] = { 7, 1<<4 }, - [SDCARD] = { 8, 1<<7 }, - [EXT] = { 11, 1<<0 }, - [TP] = { 12, 1<<15 }, + [CF_IDE] = { 1, 1 << 9 }, + [CF_CD] = { 2, 1 << 8 }, + [PCI_INTA] = { 9, 1 << 14 }, + [PCI_INTB] = { 10, 1 << 13 }, + [PCI_INTC] = { 3, 1 << 12 }, + [PCI_INTD] = { 0, 1 << 11 }, + [SM501] = { 4, 1 << 10 }, + [KEY] = { 5, 1 << 6 }, + [RTC_A] = { 6, 1 << 5 }, + [RTC_T] = { 7, 1 << 4 }, + [SDCARD] = { 8, 1 << 7 }, + [EXT] = { 11, 1 << 0 }, + [TP] = { 12, 1 << 15 }, }; static void update_irl(r2d_fpga_t *fpga) @@ -306,7 +306,7 @@ static void r2d_init(MachineState *machine) /* NIC: rtl8139 on-board, and 2 slots. */ for (i = 0; i < nb_nics; i++) pci_nic_init_nofail(&nd_table[i], pci_bus, - "rtl8139", i==0 ? "2" : NULL); + "rtl8139", i == 0 ? "2" : NULL); /* USB keyboard */ usb_create_simple(usb_bus_find(-1), "usb-kbd"); @@ -321,8 +321,8 @@ static void r2d_init(MachineState *machine) SDRAM_BASE + LINUX_LOAD_OFFSET, INITRD_LOAD_OFFSET - LINUX_LOAD_OFFSET); if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); - exit(1); + fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); + exit(1); } /* initialization which should be done by firmware */ @@ -330,7 +330,8 @@ static void r2d_init(MachineState *machine) MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 SDRAM */ address_space_stw(&address_space_memory, SH7750_BCR2, 3 << (3 * 2), MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 32bit */ - reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; /* Start from P2 area */ + /* Start from P2 area */ + reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; } if (initrd_filename) { @@ -341,8 +342,8 @@ static void r2d_init(MachineState *machine) SDRAM_SIZE - INITRD_LOAD_OFFSET); if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initrd '%s'\n", initrd_filename); - exit(1); + fprintf(stderr, "qemu: could not load initrd '%s'\n", initrd_filename); + exit(1); } /* initialization which should be done by firmware */ diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 2539924b00..1e61f9f1c8 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -78,7 +78,7 @@ typedef struct SH7750State { struct intc_desc intc; } SH7750State; -static inline int has_bcr3_and_bcr4(SH7750State * s) +static inline int has_bcr3_and_bcr4(SH7750State *s) { return s->cpu->env.features & SH_FEATURE_BCR3_AND_BCR4; } @@ -87,7 +87,7 @@ static inline int has_bcr3_and_bcr4(SH7750State * s) * I/O ports */ -int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device) +int sh7750_register_io_device(SH7750State *s, sh7750_io_device *device) { int i; @@ -102,7 +102,7 @@ int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device) static uint16_t portdir(uint32_t v) { -#define EVENPORTMASK(n) ((v & (1<<((n)<<1))) >> (n)) +#define EVENPORTMASK(n) ((v & (1 << ((n) << 1))) >> (n)) return EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) | EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) | @@ -114,7 +114,7 @@ static uint16_t portdir(uint32_t v) static uint16_t portpullup(uint32_t v) { -#define ODDPORTMASK(n) ((v & (1<<(((n)<<1)+1))) >> (n)) +#define ODDPORTMASK(n) ((v & (1 << (((n) << 1) + 1))) >> (n)) return ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) | ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) | @@ -123,26 +123,26 @@ static uint16_t portpullup(uint32_t v) ODDPORTMASK(1) | ODDPORTMASK(0); } -static uint16_t porta_lines(SH7750State * s) +static uint16_t porta_lines(SH7750State *s) { return (s->portdira & s->pdtra) | /* CPU */ (s->periph_portdira & s->periph_pdtra) | /* Peripherals */ (~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */ } -static uint16_t portb_lines(SH7750State * s) +static uint16_t portb_lines(SH7750State *s) { return (s->portdirb & s->pdtrb) | /* CPU */ (s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */ (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */ } -static void gen_port_interrupts(SH7750State * s) +static void gen_port_interrupts(SH7750State *s) { /* XXXXX interrupts not generated */ } -static void porta_changed(SH7750State * s, uint16_t prev) +static void porta_changed(SH7750State *s, uint16_t prev) { uint16_t currenta, changes; int i, r = 0; @@ -171,7 +171,7 @@ static void porta_changed(SH7750State * s, uint16_t prev) gen_port_interrupts(s); } -static void portb_changed(SH7750State * s, uint16_t prev) +static void portb_changed(SH7750State *s, uint16_t prev) { uint16_t currentb, changes; int i, r = 0; @@ -228,7 +228,7 @@ static uint32_t sh7750_mem_readw(void *opaque, hwaddr addr) case SH7750_BCR2_A7: return s->bcr2; case SH7750_BCR3_A7: - if(!has_bcr3_and_bcr4(s)) + if (!has_bcr3_and_bcr4(s)) error_access("word read", addr); return s->bcr3; case SH7750_FRQCR_A7: @@ -263,7 +263,7 @@ static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr) case SH7750_BCR1_A7: return s->bcr1; case SH7750_BCR4_A7: - if(!has_bcr3_and_bcr4(s)) + if (!has_bcr3_and_bcr4(s)) error_access("long read", addr); return s->bcr4; case SH7750_WCR1_A7: @@ -332,7 +332,7 @@ static void sh7750_mem_writew(void *opaque, hwaddr addr, s->bcr2 = mem_value; return; case SH7750_BCR3_A7: - if(!has_bcr3_and_bcr4(s)) + if (!has_bcr3_and_bcr4(s)) error_access("word write", addr); s->bcr3 = mem_value; return; @@ -384,7 +384,7 @@ static void sh7750_mem_writel(void *opaque, hwaddr addr, s->bcr1 = mem_value; return; case SH7750_BCR4_A7: - if(!has_bcr3_and_bcr4(s)) + if (!has_bcr3_and_bcr4(s)) error_access("long write", addr); s->bcr4 = mem_value; return; diff --git a/hw/sh4/sh7750_regnames.c b/hw/sh4/sh7750_regnames.c index b1f112df3e..37b3acd620 100644 --- a/hw/sh4/sh7750_regnames.c +++ b/hw/sh4/sh7750_regnames.c @@ -81,14 +81,15 @@ static regname_t regnames[] = { REGNAME(SH7750_BCR3_A7) REGNAME(SH7750_BCR4_A7) REGNAME(SH7750_SDMR2_A7) - REGNAME(SH7750_SDMR3_A7) {(uint32_t) - 1, NULL} + REGNAME(SH7750_SDMR3_A7) + { (uint32_t)-1, NULL } }; const char *regname(uint32_t addr) { unsigned int i; - for (i = 0; regnames[i].regaddr != (uint32_t) - 1; i++) { + for (i = 0; regnames[i].regaddr != (uint32_t)-1; i++) { if (regnames[i].regaddr == addr) return regnames[i].regname; } diff --git a/hw/sh4/sh7750_regs.h b/hw/sh4/sh7750_regs.h index bd12b0532d..beb571d5e9 100644 --- a/hw/sh4/sh7750_regs.h +++ b/hw/sh4/sh7750_regs.h @@ -1015,7 +1015,7 @@ */ /* DMA Source Address Register - SAR0, SAR1, SAR2, SAR3 */ -#define SH7750_SAR_REGOFS(n) (0xA00000 + ((n)*16)) /* offset */ +#define SH7750_SAR_REGOFS(n) (0xA00000 + ((n) * 16)) /* offset */ #define SH7750_SAR(n) SH7750_P4_REG32(SH7750_SAR_REGOFS(n)) #define SH7750_SAR_A7(n) SH7750_A7_REG32(SH7750_SAR_REGOFS(n)) #define SH7750_SAR0 SH7750_SAR(0) @@ -1028,7 +1028,7 @@ #define SH7750_SAR3_A7 SH7750_SAR_A7(3) /* DMA Destination Address Register - DAR0, DAR1, DAR2, DAR3 */ -#define SH7750_DAR_REGOFS(n) (0xA00004 + ((n)*16)) /* offset */ +#define SH7750_DAR_REGOFS(n) (0xA00004 + ((n) * 16)) /* offset */ #define SH7750_DAR(n) SH7750_P4_REG32(SH7750_DAR_REGOFS(n)) #define SH7750_DAR_A7(n) SH7750_A7_REG32(SH7750_DAR_REGOFS(n)) #define SH7750_DAR0 SH7750_DAR(0) @@ -1041,7 +1041,7 @@ #define SH7750_DAR3_A7 SH7750_DAR_A7(3) /* DMA Transfer Count Register - DMATCR0, DMATCR1, DMATCR2, DMATCR3 */ -#define SH7750_DMATCR_REGOFS(n) (0xA00008 + ((n)*16)) /* offset */ +#define SH7750_DMATCR_REGOFS(n) (0xA00008 + ((n) * 16)) /* offset */ #define SH7750_DMATCR(n) SH7750_P4_REG32(SH7750_DMATCR_REGOFS(n)) #define SH7750_DMATCR_A7(n) SH7750_A7_REG32(SH7750_DMATCR_REGOFS(n)) #define SH7750_DMATCR0_P4 SH7750_DMATCR(0) @@ -1054,7 +1054,7 @@ #define SH7750_DMATCR3_A7 SH7750_DMATCR_A7(3) /* DMA Channel Control Register - CHCR0, CHCR1, CHCR2, CHCR3 */ -#define SH7750_CHCR_REGOFS(n) (0xA0000C + ((n)*16)) /* offset */ +#define SH7750_CHCR_REGOFS(n) (0xA0000C + ((n) * 16)) /* offset */ #define SH7750_CHCR(n) SH7750_P4_REG32(SH7750_CHCR_REGOFS(n)) #define SH7750_CHCR_A7(n) SH7750_A7_REG32(SH7750_CHCR_REGOFS(n)) #define SH7750_CHCR0 SH7750_CHCR(0) @@ -1208,9 +1208,9 @@ #define SH7750_PCTRA_A7 SH7750_A7_REG32(SH7750_PCTRA_REGOFS) #define SH7750_PCTRA_PBPUP(n) 0 /* Bit n is pulled up */ -#define SH7750_PCTRA_PBNPUP(n) (1 << ((n)*2+1)) /* Bit n is not pulled up */ +#define SH7750_PCTRA_PBNPUP(n) (1 << ((n) * 2 + 1)) /* Bit n is not pulled up */ #define SH7750_PCTRA_PBINP(n) 0 /* Bit n is an input */ -#define SH7750_PCTRA_PBOUT(n) (1 << ((n)*2)) /* Bit n is an output */ +#define SH7750_PCTRA_PBOUT(n) (1 << ((n) * 2)) /* Bit n is an output */ /* Port Data Register A - PDTRA(half) */ #define SH7750_PDTRA_REGOFS 0x800030 /* offset */ @@ -1225,16 +1225,16 @@ #define SH7750_PCTRB_A7 SH7750_A7_REG32(SH7750_PCTRB_REGOFS) #define SH7750_PCTRB_PBPUP(n) 0 /* Bit n is pulled up */ -#define SH7750_PCTRB_PBNPUP(n) (1 << ((n-16)*2+1)) /* Bit n is not pulled up */ +#define SH7750_PCTRB_PBNPUP(n) (1 << ((n - 16) * 2 + 1)) /* Bit n is not pulled up */ #define SH7750_PCTRB_PBINP(n) 0 /* Bit n is an input */ -#define SH7750_PCTRB_PBOUT(n) (1 << ((n-16)*2)) /* Bit n is an output */ +#define SH7750_PCTRB_PBOUT(n) (1 << ((n - 16) * 2)) /* Bit n is an output */ /* Port Data Register B - PDTRB(half) */ #define SH7750_PDTRB_REGOFS 0x800044 /* offset */ #define SH7750_PDTRB SH7750_P4_REG32(SH7750_PDTRB_REGOFS) #define SH7750_PDTRB_A7 SH7750_A7_REG32(SH7750_PDTRB_REGOFS) -#define SH7750_PDTRB_BIT(n) (1 << ((n)-16)) +#define SH7750_PDTRB_BIT(n) (1 << ((n) - 16)) /* GPIO Interrupt Control Register - GPIOIC(half) */ #define SH7750_GPIOIC_REGOFS 0x800048 /* offset */ diff --git a/hw/sh4/shix.c b/hw/sh4/shix.c index 6b39de417f..aa812512f0 100644 --- a/hw/sh4/shix.c +++ b/hw/sh4/shix.c @@ -48,7 +48,7 @@ static void shix_init(MachineState *machine) MemoryRegion *rom = g_new(MemoryRegion, 1); MemoryRegion *sdram = g_new(MemoryRegion, 2); const char *bios_name = machine->firmware ?: BIOS_FILENAME; - + cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); /* Allocate memory space */ diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 4f765b339b..01afcbd2b0 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -55,7 +55,7 @@ static void sh_timer_update(sh_timer_state *s) int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE); if (new_level != s->old_level) - qemu_set_irq (s->irq, new_level); + qemu_set_irq(s->irq, new_level); s->old_level = s->int_level; s->int_level = new_level; @@ -113,11 +113,21 @@ static void sh_timer_write(void *opaque, hwaddr offset, freq = s->freq; /* ??? Need to recalculate expiry time after changing divisor. */ switch (value & TIMER_TCR_TPSC) { - case 0: freq >>= 2; break; - case 1: freq >>= 4; break; - case 2: freq >>= 6; break; - case 3: freq >>= 8; break; - case 4: freq >>= 10; break; + case 0: + freq >>= 2; + break; + case 1: + freq >>= 4; + break; + case 2: + freq >>= 6; + break; + case 3: + freq >>= 8; + break; + case 4: + freq >>= 10; + break; case 6: case 7: if (s->feat & TIMER_FEAT_EXTCLK) { diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h index 3d5ba598d0..366cedcda0 100644 --- a/include/hw/sh4/sh.h +++ b/include/hw/sh4/sh.h @@ -44,14 +44,14 @@ typedef struct { uint16_t portbmask_trigger; /* Return 0 if no action was taken */ int (*port_change_cb) (uint16_t porta, uint16_t portb, - uint16_t * periph_pdtra, - uint16_t * periph_portdira, - uint16_t * periph_pdtrb, - uint16_t * periph_portdirb); + uint16_t *periph_pdtra, + uint16_t *periph_portdira, + uint16_t *periph_pdtrb, + uint16_t *periph_portdirb); } sh7750_io_device; int sh7750_register_io_device(struct SH7750State *s, - sh7750_io_device * device); + sh7750_io_device *device); /* sh_serial.c */ #define SH_SERIAL_FEAT_SCIF (1 << 0) From ac3c9e74c1ee1071e5be692a611c5ee261b9b581 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0869/1334] hw/sh4: Coding style: Add missing braces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Yoshinori Sato Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 48 ++++++++++++++-------- hw/intc/sh_intc.c | 87 ++++++++++++++++++++++------------------ hw/sh4/r2d.c | 15 ++++--- hw/sh4/sh7750.c | 24 +++++++---- hw/sh4/sh7750_regnames.c | 3 +- hw/timer/sh_timer.c | 22 +++++----- 6 files changed, 118 insertions(+), 81 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 3fdb9f9a99..1b1e6a6a04 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -103,8 +103,9 @@ static void sh_serial_write(void *opaque, hwaddr offs, case 0x08: /* SCR */ /* TODO : For SH7751, SCIF mask should be 0xfb. */ s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); - if (!(val & (1 << 5))) + if (!(val & (1 << 5))) { s->flags |= SH_SERIAL_FLAG_TEND; + } if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) { qemu_set_irq(s->txi, val & (1 << 7)); } @@ -133,16 +134,21 @@ static void sh_serial_write(void *opaque, hwaddr offs, if (s->feat & SH_SERIAL_FEAT_SCIF) { switch (offs) { case 0x10: /* FSR */ - if (!(val & (1 << 6))) + if (!(val & (1 << 6))) { s->flags &= ~SH_SERIAL_FLAG_TEND; - if (!(val & (1 << 5))) + } + if (!(val & (1 << 5))) { s->flags &= ~SH_SERIAL_FLAG_TDE; - if (!(val & (1 << 4))) + } + if (!(val & (1 << 4))) { s->flags &= ~SH_SERIAL_FLAG_BRK; - if (!(val & (1 << 1))) + } + if (!(val & (1 << 1))) { s->flags &= ~SH_SERIAL_FLAG_RDF; - if (!(val & (1 << 0))) + } + if (!(val & (1 << 0))) { s->flags &= ~SH_SERIAL_FLAG_DR; + } if (!(val & (1 << 1)) || !(val & (1 << 0))) { if (s->rxi) { @@ -231,29 +237,37 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, break; case 0x10: /* FSR */ ret = 0; - if (s->flags & SH_SERIAL_FLAG_TEND) + if (s->flags & SH_SERIAL_FLAG_TEND) { ret |= (1 << 6); - if (s->flags & SH_SERIAL_FLAG_TDE) + } + if (s->flags & SH_SERIAL_FLAG_TDE) { ret |= (1 << 5); - if (s->flags & SH_SERIAL_FLAG_BRK) + } + if (s->flags & SH_SERIAL_FLAG_BRK) { ret |= (1 << 4); - if (s->flags & SH_SERIAL_FLAG_RDF) + } + if (s->flags & SH_SERIAL_FLAG_RDF) { ret |= (1 << 1); - if (s->flags & SH_SERIAL_FLAG_DR) + } + if (s->flags & SH_SERIAL_FLAG_DR) { ret |= (1 << 0); + } - if (s->scr & (1 << 5)) + if (s->scr & (1 << 5)) { s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; + } break; case 0x14: if (s->rx_cnt > 0) { ret = s->rx_fifo[s->rx_tail++]; s->rx_cnt--; - if (s->rx_tail == SH_RX_FIFO_LENGTH) + if (s->rx_tail == SH_RX_FIFO_LENGTH) { s->rx_tail = 0; - if (s->rx_cnt < s->rtrg) + } + if (s->rx_cnt < s->rtrg) { s->flags &= ~SH_SERIAL_FLAG_RDF; + } } break; case 0x18: @@ -308,8 +322,9 @@ static int sh_serial_can_receive(sh_serial_state *s) static void sh_serial_receive_break(sh_serial_state *s) { - if (s->feat & SH_SERIAL_FEAT_SCIF) + if (s->feat & SH_SERIAL_FEAT_SCIF) { s->sr |= (1 << 4); + } } static int sh_serial_can_receive1(void *opaque) @@ -361,8 +376,9 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) static void sh_serial_event(void *opaque, QEMUChrEvent event) { sh_serial_state *s = opaque; - if (event == CHR_EVENT_BREAK) + if (event == CHR_EVENT_BREAK) { sh_serial_receive_break(s); + } } static const MemoryRegionOps sh_serial_ops = { diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 67005081e3..eddad7c195 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -26,23 +26,23 @@ void sh_intc_toggle_source(struct intc_source *source, int pending_changed = 0; int old_pending; - if ((source->enable_count == source->enable_max) && (enable_adj == -1)) + if ((source->enable_count == source->enable_max) && (enable_adj == -1)) { enable_changed = -1; - + } source->enable_count += enable_adj; - if (source->enable_count == source->enable_max) + if (source->enable_count == source->enable_max) { enable_changed = 1; - + } source->asserted += assert_adj; old_pending = source->pending; source->pending = source->asserted && (source->enable_count == source->enable_max); - if (old_pending != source->pending) + if (old_pending != source->pending) { pending_changed = 1; - + } if (pending_changed) { if (source->pending) { source->parent->pending++; @@ -79,10 +79,11 @@ static void sh_intc_set_irq(void *opaque, int n, int level) struct intc_desc *desc = opaque; struct intc_source *source = &(desc->sources[n]); - if (level && !source->asserted) - sh_intc_toggle_source(source, 0, 1); - else if (!level && source->asserted) - sh_intc_toggle_source(source, 0, -1); + if (level && !source->asserted) { + sh_intc_toggle_source(source, 0, 1); + } else if (!level && source->asserted) { + sh_intc_toggle_source(source, 0, -1); + } } int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) @@ -126,16 +127,18 @@ static unsigned int sh_intc_mode(unsigned long address, return INTC_MODE_NONE; if (set_reg && clr_reg) { - if (address == INTC_A7(set_reg)) + if (address == INTC_A7(set_reg)) { return INTC_MODE_DUAL_SET; - else + } else { return INTC_MODE_DUAL_CLR; + } } - if (set_reg) + if (set_reg) { return INTC_MODE_ENABLE_REG; - else + } else { return INTC_MODE_MASK_REG; + } } static void sh_intc_locate(struct intc_desc *desc, @@ -155,9 +158,9 @@ static void sh_intc_locate(struct intc_desc *desc, struct intc_mask_reg *mr = desc->mask_regs + i; mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); - if (mode == INTC_MODE_NONE) + if (mode == INTC_MODE_NONE) { continue; - + } *modep = mode; *datap = &mr->value; *enums = mr->enum_ids; @@ -172,9 +175,9 @@ static void sh_intc_locate(struct intc_desc *desc, struct intc_prio_reg *pr = desc->prio_regs + i; mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); - if (mode == INTC_MODE_NONE) + if (mode == INTC_MODE_NONE) { continue; - + } *modep = mode | INTC_MODE_IS_PRIO; *datap = &pr->value; *enums = pr->enum_ids; @@ -192,9 +195,9 @@ static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, { struct intc_source *source = desc->sources + id; - if (!id) + if (!id) { return; - + } if (!source->next_enum_id && (!source->enable_max || !source->vect)) { #ifdef DEBUG_INTC_SOURCES printf("sh_intc: reserved interrupt source %d modified\n", id); @@ -202,9 +205,9 @@ static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, return; } - if (source->vect) + if (source->vect) { sh_intc_toggle_source(source, enable ? 1 : -1, 0); - + } #ifdef DEBUG_INTC else { printf("setting interrupt group %d to %d\n", id, !!enable); @@ -276,8 +279,9 @@ static void sh_intc_write(void *opaque, hwaddr offset, for (k = 0; k <= first; k++) { mask = ((1 << width) - 1) << ((first - k) * width); - if ((*valuep & mask) == (value & mask)) + if ((*valuep & mask) == (value & mask)) { continue; + } #if 0 printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", k, first, enum_ids[k], (unsigned int)mask); @@ -300,9 +304,9 @@ static const MemoryRegionOps sh_intc_ops = { struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) { - if (id) + if (id) { return desc->sources + id; - + } return NULL; } @@ -351,12 +355,13 @@ static void sh_intc_register_source(struct intc_desc *desc, struct intc_mask_reg *mr = desc->mask_regs + i; for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) { - if (mr->enum_ids[k] != source) + if (mr->enum_ids[k] != source) { continue; - + } s = sh_intc_source(desc, mr->enum_ids[k]); - if (s) + if (s) { s->enable_max++; + } } } } @@ -366,12 +371,13 @@ static void sh_intc_register_source(struct intc_desc *desc, struct intc_prio_reg *pr = desc->prio_regs + i; for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) { - if (pr->enum_ids[k] != source) + if (pr->enum_ids[k] != source) { continue; - + } s = sh_intc_source(desc, pr->enum_ids[k]); - if (s) + if (s) { s->enable_max++; + } } } } @@ -381,12 +387,13 @@ static void sh_intc_register_source(struct intc_desc *desc, struct intc_group *gr = groups + i; for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) { - if (gr->enum_ids[k] != source) + if (gr->enum_ids[k] != source) { continue; - + } s = sh_intc_source(desc, gr->enum_ids[k]); - if (s) + if (s) { s->enable_max++; + } } } } @@ -425,9 +432,9 @@ void sh_intc_register_sources(struct intc_desc *desc, s->next_enum_id = gr->enum_ids[0]; for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) { - if (!gr->enum_ids[k]) + if (!gr->enum_ids[k]) { continue; - + } s = sh_intc_source(desc, gr->enum_ids[k - 1]); s->next_enum_id = gr->enum_ids[k]; } @@ -512,11 +519,11 @@ void sh_intc_set_irl(void *opaque, int n, int level) struct intc_source *s = opaque; int i, irl = level ^ 15; for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) { - if (i == irl) + if (i == irl) { sh_intc_toggle_source(s, s->enable_count ? 0 : 1, s->asserted ? 0 : 1); - else - if (s->asserted) - sh_intc_toggle_source(s, 0, -1); + } else if (s->asserted) { + sh_intc_toggle_source(s, 0, -1); + } } } diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 216d6e24a1..57ccae7249 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -114,20 +114,23 @@ static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = { static void update_irl(r2d_fpga_t *fpga) { int i, irl = 15; - for (i = 0; i < NR_IRQS; i++) - if (fpga->irlmon & fpga->irlmsk & irqtab[i].msk) - if (irqtab[i].irl < irl) - irl = irqtab[i].irl; + for (i = 0; i < NR_IRQS; i++) { + if ((fpga->irlmon & fpga->irlmsk & irqtab[i].msk) && + irqtab[i].irl < irl) { + irl = irqtab[i].irl; + } + } qemu_set_irq(fpga->irl, irl ^ 15); } static void r2d_fpga_irq_set(void *opaque, int n, int level) { r2d_fpga_t *fpga = opaque; - if (level) + if (level) { fpga->irlmon |= irqtab[n].msk; - else + } else { fpga->irlmon &= ~irqtab[n].msk; + } update_irl(fpga); } diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 1e61f9f1c8..ca7e261aba 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -153,8 +153,9 @@ static void porta_changed(SH7750State *s, uint16_t prev) fprintf(stderr, "pdtra=0x%04x, pctra=0x%08x\n", s->pdtra, s->pctra); #endif currenta = porta_lines(s); - if (currenta == prev) + if (currenta == prev) { return; + } changes = currenta ^ prev; for (i = 0; i < NB_DEVICES; i++) { @@ -167,8 +168,9 @@ static void porta_changed(SH7750State *s, uint16_t prev) } } - if (r) + if (r) { gen_port_interrupts(s); + } } static void portb_changed(SH7750State *s, uint16_t prev) @@ -177,8 +179,9 @@ static void portb_changed(SH7750State *s, uint16_t prev) int i, r = 0; currentb = portb_lines(s); - if (currentb == prev) + if (currentb == prev) { return; + } changes = currentb ^ prev; for (i = 0; i < NB_DEVICES; i++) { @@ -191,8 +194,9 @@ static void portb_changed(SH7750State *s, uint16_t prev) } } - if (r) + if (r) { gen_port_interrupts(s); + } } /* @@ -228,8 +232,9 @@ static uint32_t sh7750_mem_readw(void *opaque, hwaddr addr) case SH7750_BCR2_A7: return s->bcr2; case SH7750_BCR3_A7: - if (!has_bcr3_and_bcr4(s)) + if (!has_bcr3_and_bcr4(s)) { error_access("word read", addr); + } return s->bcr3; case SH7750_FRQCR_A7: return 0; @@ -263,8 +268,9 @@ static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr) case SH7750_BCR1_A7: return s->bcr1; case SH7750_BCR4_A7: - if (!has_bcr3_and_bcr4(s)) + if (!has_bcr3_and_bcr4(s)) { error_access("long read", addr); + } return s->bcr4; case SH7750_WCR1_A7: case SH7750_WCR2_A7: @@ -332,8 +338,9 @@ static void sh7750_mem_writew(void *opaque, hwaddr addr, s->bcr2 = mem_value; return; case SH7750_BCR3_A7: - if (!has_bcr3_and_bcr4(s)) + if (!has_bcr3_and_bcr4(s)) { error_access("word write", addr); + } s->bcr3 = mem_value; return; case SH7750_PCR_A7: @@ -384,8 +391,9 @@ static void sh7750_mem_writel(void *opaque, hwaddr addr, s->bcr1 = mem_value; return; case SH7750_BCR4_A7: - if (!has_bcr3_and_bcr4(s)) + if (!has_bcr3_and_bcr4(s)) { error_access("long write", addr); + } s->bcr4 = mem_value; return; case SH7750_WCR1_A7: diff --git a/hw/sh4/sh7750_regnames.c b/hw/sh4/sh7750_regnames.c index 37b3acd620..e531d46a8e 100644 --- a/hw/sh4/sh7750_regnames.c +++ b/hw/sh4/sh7750_regnames.c @@ -90,8 +90,9 @@ const char *regname(uint32_t addr) unsigned int i; for (i = 0; regnames[i].regaddr != (uint32_t)-1; i++) { - if (regnames[i].regaddr == addr) + if (regnames[i].regaddr == addr) { return regnames[i].regname; + } } return ""; diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 01afcbd2b0..68c109ecfd 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -54,9 +54,9 @@ static void sh_timer_update(sh_timer_state *s) { int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE); - if (new_level != s->old_level) + if (new_level != s->old_level) { qemu_set_irq(s->irq, new_level); - + } s->old_level = s->int_level; s->int_level = new_level; } @@ -73,8 +73,9 @@ static uint32_t sh_timer_read(void *opaque, hwaddr offset) case OFFSET_TCR: return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0); case OFFSET_TCPR: - if (s->feat & TIMER_FEAT_CAPT) + if (s->feat & TIMER_FEAT_CAPT) { return s->tcpr; + } /* fall through */ default: hw_error("sh_timer_read: Bad offset %x\n", (int)offset); @@ -279,17 +280,18 @@ static uint64_t tmu012_read(void *opaque, hwaddr offset, return sh_timer_read(s->timer[2], offset - 0x20); } - if (offset >= 0x14) + if (offset >= 0x14) { return sh_timer_read(s->timer[1], offset - 0x14); - - if (offset >= 0x08) + } + if (offset >= 0x08) { return sh_timer_read(s->timer[0], offset - 0x08); - - if (offset == 4) + } + if (offset == 4) { return s->tstr; - - if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) + } + if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) { return s->tocr; + } hw_error("tmu012_write: Bad offset %x\n", (int)offset); return 0; From 373b96b9c6d77a54a52239b422a44958d9dcc67e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0870/1334] hw/sh4: Coding style: Remove unnecessary casts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Yoshinori Sato Message-Id: <6cb1bcf24572ad8465c20b64fec81157f34bcbe9.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/sh_timer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 68c109ecfd..02eb865908 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -233,7 +233,7 @@ static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq) { sh_timer_state *s; - s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state)); + s = g_malloc0(sizeof(*s)); s->freq = freq; s->feat = feat; s->tcor = 0xffffffff; @@ -358,7 +358,7 @@ void tmu012_init(MemoryRegion *sysmem, hwaddr base, tmu012_state *s; int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0; - s = (tmu012_state *)g_malloc0(sizeof(tmu012_state)); + s = g_malloc0(sizeof(*s)); s->feat = feat; s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq); s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq); From 3b885dabd09f449143adbfc0595896b47648673d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0871/1334] hw/sh4: Fix typos in a comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/sh_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 02eb865908..cc7c1897a8 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -107,7 +107,7 @@ static void sh_timer_write(void *opaque, hwaddr offset, if (s->enabled) { /* * Pause the timer if it is running. This may cause some inaccuracy - * dure to rounding, but avoids a whole lot of other messyness + * due to rounding, but avoids a whole lot of other messiness */ ptimer_stop(s->timer); } From ad52cfc13750662fd003565e8035bb8ffbd4f0ef Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0872/1334] hw/sh4: Change debug printfs to traces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Message-Id: [PMD: Fixed format strings for 32-bit hosts] Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 13 ++----- hw/char/trace-events | 4 +++ hw/intc/sh_intc.c | 79 +++++++++++-------------------------------- hw/intc/trace-events | 8 +++++ hw/sh4/sh7750.c | 8 ++--- hw/sh4/trace-events | 3 ++ hw/sh4/trace.h | 1 + hw/timer/sh_timer.c | 22 +++--------- hw/timer/trace-events | 5 +++ meson.build | 1 + 10 files changed, 52 insertions(+), 92 deletions(-) create mode 100644 hw/sh4/trace-events create mode 100644 hw/sh4/trace.h diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 1b1e6a6a04..053f45e1a6 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -31,8 +31,7 @@ #include "chardev/char-fe.h" #include "qapi/error.h" #include "qemu/timer.h" - -//#define DEBUG_SERIAL +#include "trace.h" #define SH_SERIAL_FLAG_TEND (1 << 0) #define SH_SERIAL_FLAG_TDE (1 << 1) @@ -89,10 +88,7 @@ static void sh_serial_write(void *opaque, hwaddr offs, sh_serial_state *s = opaque; unsigned char ch; -#ifdef DEBUG_SERIAL - printf("sh_serial: write offs=0x%02x val=0x%02x\n", - offs, val); -#endif + trace_sh_serial_write(size, offs, val); switch (offs) { case 0x00: /* SMR */ s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); @@ -301,10 +297,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, break; } } -#ifdef DEBUG_SERIAL - printf("sh_serial: read offs=0x%02x val=0x%x\n", - offs, ret); -#endif + trace_sh_serial_read(size, offs, ret); if (ret & ~((1 << 16) - 1)) { fprintf(stderr, "sh_serial: unsupported read from 0x%02" diff --git a/hw/char/trace-events b/hw/char/trace-events index b774832af4..4a92e7674a 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -101,3 +101,7 @@ exynos_uart_rx_timeout(uint32_t channel, uint32_t stat, uint32_t intsp) "UART%d: # cadence_uart.c cadence_uart_baudrate(unsigned baudrate) "baudrate %u" + +# sh_serial.c +sh_serial_read(unsigned size, uint64_t offs, uint64_t val) " size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 +sh_serial_write(unsigned size, uint64_t offs, uint64_t val) "size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index eddad7c195..673606b24b 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -9,13 +9,12 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "hw/sh4/sh_intc.h" #include "hw/irq.h" #include "hw/sh4/sh.h" - -//#define DEBUG_INTC -//#define DEBUG_INTC_SOURCES +#include "trace.h" #define INTC_A7(x) ((x) & 0x1fffffff) @@ -57,20 +56,14 @@ void sh_intc_toggle_source(struct intc_source *source, } } - if (enable_changed || assert_adj || pending_changed) { -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n", - source->parent->pending, - source->asserted, - source->enable_count, - source->enable_max, - source->vect, - source->asserted ? "asserted " : - assert_adj ? "deasserted" : "", - enable_changed == 1 ? "enabled " : - enable_changed == -1 ? "disabled " : "", - source->pending ? "pending" : ""); -#endif + if (enable_changed || assert_adj || pending_changed) { + trace_sh_intc_sources(source->parent->pending, source->asserted, + source->enable_count, source->enable_max, + source->vect, source->asserted ? "asserted " : + assert_adj ? "deasserted" : "", + enable_changed == 1 ? "enabled " : + enable_changed == -1 ? "disabled " : "", + source->pending ? "pending" : ""); } } @@ -101,10 +94,7 @@ int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) struct intc_source *source = desc->sources + i; if (source->pending) { -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: (%d) returning interrupt source 0x%x\n", - desc->pending, source->vect); -#endif + trace_sh_intc_pending(desc->pending, source->vect); return source->vect; } } @@ -199,30 +189,22 @@ static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, return; } if (!source->next_enum_id && (!source->enable_max || !source->vect)) { -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: reserved interrupt source %d modified\n", id); -#endif + qemu_log_mask(LOG_UNIMP, + "sh_intc: reserved interrupt source %d modified\n", id); return; } if (source->vect) { sh_intc_toggle_source(source, enable ? 1 : -1, 0); } -#ifdef DEBUG_INTC - else { - printf("setting interrupt group %d to %d\n", id, !!enable); - } -#endif if ((is_group || !source->vect) && source->next_enum_id) { sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1); } -#ifdef DEBUG_INTC if (!source->vect) { - printf("setting interrupt group %d to %d - done\n", id, !!enable); + trace_sh_intc_set(id, !!enable); } -#endif } static uint64_t sh_intc_read(void *opaque, hwaddr offset, @@ -235,12 +217,9 @@ static uint64_t sh_intc_read(void *opaque, hwaddr offset, unsigned int mode = 0; unsigned long *valuep; -#ifdef DEBUG_INTC - printf("sh_intc_read 0x%lx\n", (unsigned long) offset); -#endif - sh_intc_locate(desc, (unsigned long)offset, &valuep, &enum_ids, &first, &width, &mode); + trace_sh_intc_read(size, (uint64_t)offset, *valuep); return *valuep; } @@ -256,13 +235,9 @@ static void sh_intc_write(void *opaque, hwaddr offset, unsigned long *valuep; unsigned long mask; -#ifdef DEBUG_INTC - printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value); -#endif - + trace_sh_intc_write(size, (uint64_t)offset, value); sh_intc_locate(desc, (unsigned long)offset, &valuep, &enum_ids, &first, &width, &mode); - switch (mode) { case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break; @@ -282,18 +257,10 @@ static void sh_intc_write(void *opaque, hwaddr offset, if ((*valuep & mask) == (value & mask)) { continue; } -#if 0 - printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", - k, first, enum_ids[k], (unsigned int)mask); -#endif sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0); } *valuep = value; - -#ifdef DEBUG_INTC - printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value); -#endif } static const MemoryRegionOps sh_intc_ops = { @@ -416,11 +383,8 @@ void sh_intc_register_sources(struct intc_desc *desc, s = sh_intc_source(desc, vect->enum_id); if (s) { s->vect = vect->vect; - -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n", - vect->enum_id, s->vect, s->enable_count, s->enable_max); -#endif + trace_sh_intc_register("source", vect->enum_id, s->vect, + s->enable_count, s->enable_max); } } @@ -438,11 +402,8 @@ void sh_intc_register_sources(struct intc_desc *desc, s = sh_intc_source(desc, gr->enum_ids[k - 1]); s->next_enum_id = gr->enum_ids[k]; } - -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: registered group %d (%d/%d)\n", - gr->enum_id, s->enable_count, s->enable_max); -#endif + trace_sh_intc_register("group", gr->enum_id, 0xffff, + s->enable_count, s->enable_max); } } } diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 6a17d38998..9aba7e3a7a 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -238,3 +238,11 @@ goldfish_pic_write(void *dev, int idx, unsigned int addr, unsigned int size, uin goldfish_pic_reset(void *dev, int idx) "pic: %p goldfish-irq.%d" goldfish_pic_realize(void *dev, int idx) "pic: %p goldfish-irq.%d" goldfish_pic_instance_init(void *dev) "pic: %p goldfish-irq" + +# sh_intc.c +sh_intc_sources(int p, int a, int c, int m, unsigned short v, const char *s1, const char *s2, const char *s3) "(%d/%d/%d/%d) interrupt source 0x%x %s%s%s" +sh_intc_pending(int p, unsigned short v) "(%d) returning interrupt source 0x%x" +sh_intc_register(const char *s, int id, unsigned short v, int c, int m) "%s %u -> 0x%04x (%d/%d)" +sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " -> 0x%lx" +sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx" +sh_intc_set(int id, int enable) "setting interrupt group %d to %d" diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index ca7e261aba..6c702d627c 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -32,6 +32,7 @@ #include "hw/sh4/sh_intc.h" #include "hw/timer/tmu012.h" #include "exec/exec-all.h" +#include "trace.h" #define NB_DEVICES 4 @@ -147,15 +148,11 @@ static void porta_changed(SH7750State *s, uint16_t prev) uint16_t currenta, changes; int i, r = 0; -#if 0 - fprintf(stderr, "porta changed from 0x%04x to 0x%04x\n", - prev, porta_lines(s)); - fprintf(stderr, "pdtra=0x%04x, pctra=0x%08x\n", s->pdtra, s->pctra); -#endif currenta = porta_lines(s); if (currenta == prev) { return; } + trace_sh7750_porta(prev, currenta, s->pdtra, s->pctra); changes = currenta ^ prev; for (i = 0; i < NB_DEVICES; i++) { @@ -182,6 +179,7 @@ static void portb_changed(SH7750State *s, uint16_t prev) if (currentb == prev) { return; } + trace_sh7750_portb(prev, currentb, s->pdtrb, s->pctrb); changes = currentb ^ prev; for (i = 0; i < NB_DEVICES; i++) { diff --git a/hw/sh4/trace-events b/hw/sh4/trace-events new file mode 100644 index 0000000000..4b61cd56c8 --- /dev/null +++ b/hw/sh4/trace-events @@ -0,0 +1,3 @@ +# sh7750.c +sh7750_porta(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "porta changed from 0x%04x to 0x%04x\npdtra=0x%04x, pctra=0x%08x" +sh7750_portb(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "portb changed from 0x%04x to 0x%04x\npdtrb=0x%04x, pctrb=0x%08x" diff --git a/hw/sh4/trace.h b/hw/sh4/trace.h new file mode 100644 index 0000000000..e2c13323b7 --- /dev/null +++ b/hw/sh4/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_sh4.h" diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index cc7c1897a8..e1b6145df8 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -15,8 +15,7 @@ #include "hw/sh4/sh.h" #include "hw/timer/tmu012.h" #include "hw/ptimer.h" - -//#define DEBUG_TIMER +#include "trace.h" #define TIMER_TCR_TPSC (7 << 0) #define TIMER_TCR_CKEG (3 << 3) @@ -203,10 +202,7 @@ static void sh_timer_start_stop(void *opaque, int enable) { sh_timer_state *s = (sh_timer_state *)opaque; -#ifdef DEBUG_TIMER - printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled); -#endif - + trace_sh_timer_start_stop(enable, s->enabled); ptimer_transaction_begin(s->timer); if (s->enabled && !enable) { ptimer_stop(s->timer); @@ -216,10 +212,6 @@ static void sh_timer_start_stop(void *opaque, int enable) } ptimer_transaction_commit(s->timer); s->enabled = !!enable; - -#ifdef DEBUG_TIMER - printf("sh_timer_start_stop done %d\n", s->enabled); -#endif } static void sh_timer_tick(void *opaque) @@ -269,10 +261,7 @@ static uint64_t tmu012_read(void *opaque, hwaddr offset, { tmu012_state *s = (tmu012_state *)opaque; -#ifdef DEBUG_TIMER - printf("tmu012_read 0x%lx\n", (unsigned long) offset); -#endif - + trace_sh_timer_read(offset); if (offset >= 0x20) { if (!(s->feat & TMU012_FEAT_3CHAN)) { hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); @@ -302,10 +291,7 @@ static void tmu012_write(void *opaque, hwaddr offset, { tmu012_state *s = (tmu012_state *)opaque; -#ifdef DEBUG_TIMER - printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value); -#endif - + trace_sh_timer_write(offset, value); if (offset >= 0x20) { if (!(s->feat & TMU012_FEAT_3CHAN)) { hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); diff --git a/hw/timer/trace-events b/hw/timer/trace-events index d0edcd2a80..3eccef8385 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -94,3 +94,8 @@ sifive_pwm_set_alarm(uint64_t alarm, uint64_t now) "Setting alarm to: 0x%" PRIx6 sifive_pwm_interrupt(int num) "Interrupt %d" sifive_pwm_read(uint64_t offset) "Read at address: 0x%" PRIx64 sifive_pwm_write(uint64_t data, uint64_t offset) "Write 0x%" PRIx64 " at address: 0x%" PRIx64 + +# sh_timer.c +sh_timer_start_stop(int enable, int current) "%d (%d)" +sh_timer_read(uint64_t offset) "tmu012_read 0x%" PRIx64 +sh_timer_write(uint64_t offset, uint64_t value) "tmu012_write 0x%" PRIx64 " 0x%08" PRIx64 diff --git a/meson.build b/meson.build index 2c5b53cbe2..b092728397 100644 --- a/meson.build +++ b/meson.build @@ -2459,6 +2459,7 @@ if have_system 'hw/s390x', 'hw/scsi', 'hw/sd', + 'hw/sh4', 'hw/sparc', 'hw/sparc64', 'hw/ssi', From 6e5dd76f213afc3fdf07ddebe3fed3980228f71b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0873/1334] hw/sh4/r2d: Use error_report instead of fprintf to stderr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <7f320ab72f3d4d43cd62925230a9f83583413f67.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/sh4/r2d.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 57ccae7249..72759413f3 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "cpu.h" #include "hw/sysbus.h" #include "hw/sh4/sh.h" @@ -324,7 +325,7 @@ static void r2d_init(MachineState *machine) SDRAM_BASE + LINUX_LOAD_OFFSET, INITRD_LOAD_OFFSET - LINUX_LOAD_OFFSET); if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); + error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); } @@ -345,7 +346,7 @@ static void r2d_init(MachineState *machine) SDRAM_SIZE - INITRD_LOAD_OFFSET); if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initrd '%s'\n", initrd_filename); + error_report("qemu: could not load initrd '%s'", initrd_filename); exit(1); } From 3cf7ce4337aebf8f9148ee53033710b4c4b00f01 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0874/1334] hw/char/sh_serial: Do not abort on invalid access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace fprintf with qemu_log_mask LOG_GUEST_ERROR as the intention is to handle valid accesses in these functions so if we get to these errors then it's an invalid access. Do not abort as that would allow the guest to crash QEMU and the practice in other devices is to not do that just log and ignore the invalid access. While at it also simplify the complex bit ops to check if a return value was set which can be done much simpler and clearer. Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <6b46045141d6d9cc32e17c223896fa1116384796.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 053f45e1a6..2d6ea0042e 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -31,6 +31,7 @@ #include "chardev/char-fe.h" #include "qapi/error.h" #include "qemu/timer.h" +#include "qemu/log.h" #include "trace.h" #define SH_SERIAL_FLAG_TEND (1 << 0) @@ -195,17 +196,16 @@ static void sh_serial_write(void *opaque, hwaddr offs, return; } } - - fprintf(stderr, "sh_serial: unsupported write to 0x%02" - HWADDR_PRIx "\n", offs); - abort(); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported write to 0x%02" HWADDR_PRIx "\n", + __func__, offs); } static uint64_t sh_serial_read(void *opaque, hwaddr offs, unsigned size) { sh_serial_state *s = opaque; - uint32_t ret = ~0; + uint32_t ret = UINT32_MAX; #if 0 switch (offs) { @@ -299,10 +299,11 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, } trace_sh_serial_read(size, offs, ret); - if (ret & ~((1 << 16) - 1)) { - fprintf(stderr, "sh_serial: unsupported read from 0x%02" - HWADDR_PRIx "\n", offs); - abort(); + if (ret > UINT16_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported read from 0x%02" HWADDR_PRIx "\n", + __func__, offs); + ret = 0; } return ret; From 2f6df13748a7de19ab150a52af846f70303746e5 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0875/1334] hw/char/sh_serial: Rename type sh_serial_state to SHSerialState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coding style says types should be camel case. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <0f185653528c99eeeb2b4e4afb8b818d93298c20.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 2d6ea0042e..bc5e0c4404 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -73,9 +73,9 @@ typedef struct { qemu_irq txi; qemu_irq tei; qemu_irq bri; -} sh_serial_state; +} SHSerialState; -static void sh_serial_clear_fifo(sh_serial_state *s) +static void sh_serial_clear_fifo(SHSerialState *s) { memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); s->rx_cnt = 0; @@ -86,7 +86,7 @@ static void sh_serial_clear_fifo(sh_serial_state *s) static void sh_serial_write(void *opaque, hwaddr offs, uint64_t val, unsigned size) { - sh_serial_state *s = opaque; + SHSerialState *s = opaque; unsigned char ch; trace_sh_serial_write(size, offs, val); @@ -204,7 +204,7 @@ static void sh_serial_write(void *opaque, hwaddr offs, static uint64_t sh_serial_read(void *opaque, hwaddr offs, unsigned size) { - sh_serial_state *s = opaque; + SHSerialState *s = opaque; uint32_t ret = UINT32_MAX; #if 0 @@ -309,12 +309,12 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, return ret; } -static int sh_serial_can_receive(sh_serial_state *s) +static int sh_serial_can_receive(SHSerialState *s) { return s->scr & (1 << 4); } -static void sh_serial_receive_break(sh_serial_state *s) +static void sh_serial_receive_break(SHSerialState *s) { if (s->feat & SH_SERIAL_FEAT_SCIF) { s->sr |= (1 << 4); @@ -323,13 +323,13 @@ static void sh_serial_receive_break(sh_serial_state *s) static int sh_serial_can_receive1(void *opaque) { - sh_serial_state *s = opaque; + SHSerialState *s = opaque; return sh_serial_can_receive(s); } static void sh_serial_timeout_int(void *opaque) { - sh_serial_state *s = opaque; + SHSerialState *s = opaque; s->flags |= SH_SERIAL_FLAG_RDF; if (s->scr & (1 << 6) && s->rxi) { @@ -339,7 +339,7 @@ static void sh_serial_timeout_int(void *opaque) static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) { - sh_serial_state *s = opaque; + SHSerialState *s = opaque; if (s->feat & SH_SERIAL_FEAT_SCIF) { int i; @@ -369,7 +369,7 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) static void sh_serial_event(void *opaque, QEMUChrEvent event) { - sh_serial_state *s = opaque; + SHSerialState *s = opaque; if (event == CHR_EVENT_BREAK) { sh_serial_receive_break(s); } @@ -390,9 +390,7 @@ void sh_serial_init(MemoryRegion *sysmem, qemu_irq tei_source, qemu_irq bri_source) { - sh_serial_state *s; - - s = g_malloc0(sizeof(sh_serial_state)); + SHSerialState *s = g_malloc0(sizeof(*s)); s->feat = feat; s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; From 5b344b02e1813c6823e76ea981a56e7b432985a4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0876/1334] hw/char/sh_serial: Embed QEMUTimer in state struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of allocating timer with timer_new store it directly in the state struct. This makes it simpler to free it together with the device. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index bc5e0c4404..5ee93dc732 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -65,7 +65,7 @@ typedef struct { int rtrg; CharBackend chr; - QEMUTimer *fifo_timeout_timer; + QEMUTimer fifo_timeout_timer; uint64_t etu; /* Elementary Time Unit (ns) */ qemu_irq eri; @@ -353,11 +353,11 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) if (s->rx_cnt >= s->rtrg) { s->flags |= SH_SERIAL_FLAG_RDF; if (s->scr & (1 << 6) && s->rxi) { - timer_del(s->fifo_timeout_timer); + timer_del(&s->fifo_timeout_timer); qemu_set_irq(s->rxi, 1); } } else { - timer_mod(s->fifo_timeout_timer, + timer_mod(&s->fifo_timeout_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu); } } @@ -427,8 +427,8 @@ void sh_serial_init(MemoryRegion *sysmem, sh_serial_event, NULL, s, NULL, true); } - s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - sh_serial_timeout_int, s); + timer_init_ns(&s->fifo_timeout_timer, QEMU_CLOCK_VIRTUAL, + sh_serial_timeout_int, s); s->etu = NANOSECONDS_PER_SECOND / 9600; s->eri = eri_source; s->rxi = rxi_source; From 017f77bbf75ef6c9b69188a150020013e6d5d8ad Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0877/1334] hw/char/sh_serial: Split off sh_serial_reset() from sh_serial_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 5ee93dc732..80a548d19d 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -381,6 +381,25 @@ static const MemoryRegionOps sh_serial_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void sh_serial_reset(SHSerialState *s) +{ + s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; + s->rtrg = 1; + + s->smr = 0; + s->brr = 0xff; + s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */ + s->sptr = 0; + + if (s->feat & SH_SERIAL_FEAT_SCIF) { + s->fcr = 0; + } else { + s->dr = 0xff; + } + + sh_serial_clear_fifo(s); +} + void sh_serial_init(MemoryRegion *sysmem, hwaddr base, int feat, uint32_t freq, Chardev *chr, @@ -393,21 +412,7 @@ void sh_serial_init(MemoryRegion *sysmem, SHSerialState *s = g_malloc0(sizeof(*s)); s->feat = feat; - s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; - s->rtrg = 1; - - s->smr = 0; - s->brr = 0xff; - s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */ - s->sptr = 0; - - if (feat & SH_SERIAL_FEAT_SCIF) { - s->fcr = 0; - } else { - s->dr = 0xff; - } - - sh_serial_clear_fifo(s); + sh_serial_reset(s); memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s, "serial", 0x100000000ULL); From beeb520925d54f5c69c66656a96dc68de0eec9a4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0878/1334] hw/char/sh_serial: QOM-ify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <92902ba34fdf2c8c62232365fbb6531b1036d557.1635541329.git.balaton@eik.bme.hu> [PMD: Use g_strdup() to initialize DeviceState::id] Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 94 +++++++++++++++++++++++++++------------------ hw/sh4/sh7750.c | 56 +++++++++++++++++++-------- include/hw/sh4/sh.h | 9 +---- 3 files changed, 99 insertions(+), 60 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 80a548d19d..808d4ebae7 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -26,7 +26,11 @@ */ #include "qemu/osdep.h" +#include "hw/sysbus.h" #include "hw/irq.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "hw/sh4/sh.h" #include "chardev/char-fe.h" #include "qapi/error.h" @@ -42,10 +46,10 @@ #define SH_RX_FIFO_LENGTH (16) -typedef struct { - MemoryRegion iomem; - MemoryRegion iomem_p4; - MemoryRegion iomem_a7; +OBJECT_DECLARE_SIMPLE_TYPE(SHSerialState, SH_SERIAL) + +struct SHSerialState { + SysBusDevice parent; uint8_t smr; uint8_t brr; uint8_t scr; @@ -59,8 +63,7 @@ typedef struct { uint8_t rx_tail; uint8_t rx_head; - int freq; - int feat; + uint8_t feat; int flags; int rtrg; @@ -73,7 +76,11 @@ typedef struct { qemu_irq txi; qemu_irq tei; qemu_irq bri; -} SHSerialState; +}; + +typedef struct {} SHSerialStateClass; + +OBJECT_DEFINE_TYPE(SHSerialState, sh_serial, SH_SERIAL, SYS_BUS_DEVICE) static void sh_serial_clear_fifo(SHSerialState *s) { @@ -381,8 +388,10 @@ static const MemoryRegionOps sh_serial_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void sh_serial_reset(SHSerialState *s) +static void sh_serial_reset(DeviceState *dev) { + SHSerialState *s = SH_SERIAL(dev); + s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; s->rtrg = 1; @@ -400,33 +409,21 @@ static void sh_serial_reset(SHSerialState *s) sh_serial_clear_fifo(s); } -void sh_serial_init(MemoryRegion *sysmem, - hwaddr base, int feat, - uint32_t freq, Chardev *chr, - qemu_irq eri_source, - qemu_irq rxi_source, - qemu_irq txi_source, - qemu_irq tei_source, - qemu_irq bri_source) +static void sh_serial_realize(DeviceState *d, Error **errp) { - SHSerialState *s = g_malloc0(sizeof(*s)); + SHSerialState *s = SH_SERIAL(d); + MemoryRegion *iomem = g_malloc(sizeof(*iomem)); - s->feat = feat; - sh_serial_reset(s); + assert(d->id); + memory_region_init_io(iomem, OBJECT(d), &sh_serial_ops, s, d->id, 0x28); + sysbus_init_mmio(SYS_BUS_DEVICE(d), iomem); + qdev_init_gpio_out_named(d, &s->eri, "eri", 1); + qdev_init_gpio_out_named(d, &s->rxi, "rxi", 1); + qdev_init_gpio_out_named(d, &s->txi, "txi", 1); + qdev_init_gpio_out_named(d, &s->tei, "tei", 1); + qdev_init_gpio_out_named(d, &s->bri, "bri", 1); - memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s, - "serial", 0x100000000ULL); - - memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem, - 0, 0x28); - memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); - - memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem, - 0, 0x28); - memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); - - if (chr) { - qemu_chr_fe_init(&s->chr, chr, &error_abort); + if (qemu_chr_fe_backend_connected(&s->chr)) { qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1, sh_serial_receive1, sh_serial_event, NULL, s, NULL, true); @@ -435,9 +432,32 @@ void sh_serial_init(MemoryRegion *sysmem, timer_init_ns(&s->fifo_timeout_timer, QEMU_CLOCK_VIRTUAL, sh_serial_timeout_int, s); s->etu = NANOSECONDS_PER_SECOND / 9600; - s->eri = eri_source; - s->rxi = rxi_source; - s->txi = txi_source; - s->tei = tei_source; - s->bri = bri_source; +} + +static void sh_serial_finalize(Object *obj) +{ + SHSerialState *s = SH_SERIAL(obj); + + timer_del(&s->fifo_timeout_timer); +} + +static void sh_serial_init(Object *obj) +{ +} + +static Property sh_serial_properties[] = { + DEFINE_PROP_CHR("chardev", SHSerialState, chr), + DEFINE_PROP_UINT8("features", SHSerialState, feat, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void sh_serial_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, sh_serial_properties); + dc->realize = sh_serial_realize; + dc->reset = sh_serial_reset; + /* Reason: part of SuperH CPU/SoC, needs to be wired up */ + dc->user_creatable = false; } diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 6c702d627c..f7d21f6170 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -24,9 +24,13 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/sysbus.h" #include "hw/irq.h" #include "hw/sh4/sh.h" #include "sysemu/sysemu.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "sh7750_regs.h" #include "sh7750_regnames.h" #include "hw/sh4/sh_intc.h" @@ -762,6 +766,9 @@ static const MemoryRegionOps sh7750_mmct_ops = { SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) { SH7750State *s; + DeviceState *dev; + SysBusDevice *sb; + MemoryRegion *mr, *alias; s = g_malloc0(sizeof(SH7750State)); s->cpu = cpu; @@ -807,21 +814,40 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) cpu->env.intc_handle = &s->intc; - sh_serial_init(sysmem, 0x1fe00000, - 0, s->periph_freq, serial_hd(0), - s->intc.irqs[SCI1_ERI], - s->intc.irqs[SCI1_RXI], - s->intc.irqs[SCI1_TXI], - s->intc.irqs[SCI1_TEI], - NULL); - sh_serial_init(sysmem, 0x1fe80000, - SH_SERIAL_FEAT_SCIF, - s->periph_freq, serial_hd(1), - s->intc.irqs[SCIF_ERI], - s->intc.irqs[SCIF_RXI], - s->intc.irqs[SCIF_TXI], - NULL, - s->intc.irqs[SCIF_BRI]); + /* SCI */ + dev = qdev_new(TYPE_SH_SERIAL); + dev->id = g_strdup("sci"); + qdev_prop_set_chr(dev, "chardev", serial_hd(0)); + sb = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sb, &error_fatal); + sysbus_mmio_map(sb, 0, 0xffe00000); + alias = g_malloc(sizeof(*alias)); + mr = sysbus_mmio_get_region(sb, 0); + memory_region_init_alias(alias, OBJECT(dev), "sci-a7", mr, + 0, memory_region_size(mr)); + memory_region_add_subregion(sysmem, A7ADDR(0xffe00000), alias); + qdev_connect_gpio_out_named(dev, "eri", 0, s->intc.irqs[SCI1_ERI]); + qdev_connect_gpio_out_named(dev, "rxi", 0, s->intc.irqs[SCI1_RXI]); + qdev_connect_gpio_out_named(dev, "txi", 0, s->intc.irqs[SCI1_TXI]); + qdev_connect_gpio_out_named(dev, "tei", 0, s->intc.irqs[SCI1_TEI]); + + /* SCIF */ + dev = qdev_new(TYPE_SH_SERIAL); + dev->id = g_strdup("scif"); + qdev_prop_set_chr(dev, "chardev", serial_hd(1)); + qdev_prop_set_uint8(dev, "features", SH_SERIAL_FEAT_SCIF); + sb = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sb, &error_fatal); + sysbus_mmio_map(sb, 0, 0xffe80000); + alias = g_malloc(sizeof(*alias)); + mr = sysbus_mmio_get_region(sb, 0); + memory_region_init_alias(alias, OBJECT(dev), "scif-a7", mr, + 0, memory_region_size(mr)); + memory_region_add_subregion(sysmem, A7ADDR(0xffe80000), alias); + qdev_connect_gpio_out_named(dev, "eri", 0, s->intc.irqs[SCIF_ERI]); + qdev_connect_gpio_out_named(dev, "rxi", 0, s->intc.irqs[SCIF_RXI]); + qdev_connect_gpio_out_named(dev, "txi", 0, s->intc.irqs[SCIF_TXI]); + qdev_connect_gpio_out_named(dev, "bri", 0, s->intc.irqs[SCIF_BRI]); tmu012_init(sysmem, 0x1fd80000, TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK, diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h index 366cedcda0..ec716cdd45 100644 --- a/include/hw/sh4/sh.h +++ b/include/hw/sh4/sh.h @@ -54,15 +54,8 @@ int sh7750_register_io_device(struct SH7750State *s, sh7750_io_device *device); /* sh_serial.c */ +#define TYPE_SH_SERIAL "sh-serial" #define SH_SERIAL_FEAT_SCIF (1 << 0) -void sh_serial_init(MemoryRegion *sysmem, - hwaddr base, int feat, - uint32_t freq, Chardev *chr, - qemu_irq eri_source, - qemu_irq rxi_source, - qemu_irq txi_source, - qemu_irq tei_source, - qemu_irq bri_source); /* sh7750.c */ qemu_irq sh7750_irl(struct SH7750State *s); From 44ae04f0328d0726c1b1666600d0aa8da604a341 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0879/1334] hw/char/sh_serial: Add device id to trace output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Normally there are at least two sh_serial instances. Add device id to trace messages to make it clear which instance they belong to otherwise its not possible to tell which serial device is accessed. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/char/sh_serial.c | 6 ++++-- hw/char/trace-events | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 808d4ebae7..355886ee3a 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -94,9 +94,10 @@ static void sh_serial_write(void *opaque, hwaddr offs, uint64_t val, unsigned size) { SHSerialState *s = opaque; + DeviceState *d = DEVICE(s); unsigned char ch; - trace_sh_serial_write(size, offs, val); + trace_sh_serial_write(d->id, size, offs, val); switch (offs) { case 0x00: /* SMR */ s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); @@ -212,6 +213,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, unsigned size) { SHSerialState *s = opaque; + DeviceState *d = DEVICE(s); uint32_t ret = UINT32_MAX; #if 0 @@ -304,7 +306,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, break; } } - trace_sh_serial_read(size, offs, ret); + trace_sh_serial_read(d->id, size, offs, ret); if (ret > UINT16_MAX) { qemu_log_mask(LOG_GUEST_ERROR, diff --git a/hw/char/trace-events b/hw/char/trace-events index 4a92e7674a..2ecb36232e 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -103,5 +103,5 @@ exynos_uart_rx_timeout(uint32_t channel, uint32_t stat, uint32_t intsp) "UART%d: cadence_uart_baudrate(unsigned baudrate) "baudrate %u" # sh_serial.c -sh_serial_read(unsigned size, uint64_t offs, uint64_t val) " size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 -sh_serial_write(unsigned size, uint64_t offs, uint64_t val) "size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 +sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 +sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 From cfaf2806e87b0eb962866e041a0fd75a524394d8 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0880/1334] hw/intc/sh_intc: Use existing macro instead of local one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The INTC_A7 local macro does the same as the A7ADDR from include/sh/sh.h so use the latter and drop the local macro definition. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <53f033477c73b7c9b021d36033c590416d6199c7.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 673606b24b..a98953d665 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -16,8 +16,6 @@ #include "hw/sh4/sh.h" #include "trace.h" -#define INTC_A7(x) ((x) & 0x1fffffff) - void sh_intc_toggle_source(struct intc_source *source, int enable_adj, int assert_adj) { @@ -112,12 +110,12 @@ int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) static unsigned int sh_intc_mode(unsigned long address, unsigned long set_reg, unsigned long clr_reg) { - if ((address != INTC_A7(set_reg)) && - (address != INTC_A7(clr_reg))) + if ((address != A7ADDR(set_reg)) && + (address != A7ADDR(clr_reg))) return INTC_MODE_NONE; if (set_reg && clr_reg) { - if (address == INTC_A7(set_reg)) { + if (address == A7ADDR(set_reg)) { return INTC_MODE_DUAL_SET; } else { return INTC_MODE_DUAL_CLR; @@ -297,11 +295,11 @@ static unsigned int sh_intc_register(MemoryRegion *sysmem, #define SH_INTC_IOMEM_FORMAT "interrupt-controller-%s-%s-%s" snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "p4"); - memory_region_init_alias(iomem_p4, NULL, name, iomem, INTC_A7(address), 4); + memory_region_init_alias(iomem_p4, NULL, name, iomem, A7ADDR(address), 4); memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4); snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "a7"); - memory_region_init_alias(iomem_a7, NULL, name, iomem, INTC_A7(address), 4); + memory_region_init_alias(iomem_a7, NULL, name, iomem, A7ADDR(address), 4); memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7); #undef SH_INTC_IOMEM_FORMAT From dc6f1734b704e5563e4c512393e2629799093681 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0881/1334] hw/intc/sh_intc: Turn some defines into an enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turn the INTC_MODE defines into an enum and clean up the function returning these to make it clearer by removing nested ifs and superfluous parenthesis. The one remaining #define is a flag which is moved further apart by changing its value from 8 to 0x80 to leave some spare bits as this is or-ed with the enum value at some places. Signed-off-by: BALATON Zoltan Message-Id: <4adf4e1ac9d2e728e5a536c69e310d77f0c4455a.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index a98953d665..f0ef83124e 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -100,33 +100,26 @@ int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) abort(); } -#define INTC_MODE_NONE 0 -#define INTC_MODE_DUAL_SET 1 -#define INTC_MODE_DUAL_CLR 2 -#define INTC_MODE_ENABLE_REG 3 -#define INTC_MODE_MASK_REG 4 -#define INTC_MODE_IS_PRIO 8 +typedef enum { + INTC_MODE_NONE, + INTC_MODE_DUAL_SET, + INTC_MODE_DUAL_CLR, + INTC_MODE_ENABLE_REG, + INTC_MODE_MASK_REG, +} SHIntCMode; +#define INTC_MODE_IS_PRIO 0x80 -static unsigned int sh_intc_mode(unsigned long address, - unsigned long set_reg, unsigned long clr_reg) +static SHIntCMode sh_intc_mode(unsigned long address, unsigned long set_reg, + unsigned long clr_reg) { - if ((address != A7ADDR(set_reg)) && - (address != A7ADDR(clr_reg))) + if (address != A7ADDR(set_reg) && address != A7ADDR(clr_reg)) { return INTC_MODE_NONE; - + } if (set_reg && clr_reg) { - if (address == A7ADDR(set_reg)) { - return INTC_MODE_DUAL_SET; - } else { - return INTC_MODE_DUAL_CLR; - } - } - - if (set_reg) { - return INTC_MODE_ENABLE_REG; - } else { - return INTC_MODE_MASK_REG; + return address == A7ADDR(set_reg) ? + INTC_MODE_DUAL_SET : INTC_MODE_DUAL_CLR; } + return set_reg ? INTC_MODE_ENABLE_REG : INTC_MODE_MASK_REG; } static void sh_intc_locate(struct intc_desc *desc, @@ -137,7 +130,8 @@ static void sh_intc_locate(struct intc_desc *desc, unsigned int *width, unsigned int *modep) { - unsigned int i, mode; + SHIntCMode mode; + unsigned int i; /* this is slow but works for now */ From 81d18cd48a87205bed159586fd3ca524a36bd49a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0882/1334] hw/intc/sh_intc: Rename iomem region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the iomem region to "intc" from "interrupt-controller" which makes the info mtree output less wide as it is already too wide because of all the aliases. Also drop the format macro which was only used twice in close proximity so we can just use the literal string instead without a macro definition. Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index f0ef83124e..175d12b371 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -287,15 +287,13 @@ static unsigned int sh_intc_register(MemoryRegion *sysmem, iomem_p4 = desc->iomem_aliases + index; iomem_a7 = iomem_p4 + 1; -#define SH_INTC_IOMEM_FORMAT "interrupt-controller-%s-%s-%s" - snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "p4"); + snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "p4"); memory_region_init_alias(iomem_p4, NULL, name, iomem, A7ADDR(address), 4); memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4); - snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "a7"); + snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "a7"); memory_region_init_alias(iomem_a7, NULL, name, iomem, A7ADDR(address), 4); memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7); -#undef SH_INTC_IOMEM_FORMAT /* used to increment aliases index */ return 2; @@ -431,9 +429,8 @@ int sh_intc_init(MemoryRegion *sysmem, } desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources); - - memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc, - "interrupt-controller", 0x100000000ULL); + memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc, "intc", + 0x100000000ULL); #define INT_REG_PARAMS(reg_struct, type, action, j) \ reg_struct->action##_reg, #type, #action, j From 92d1d3ada11dfb338bb96b6405d6938901182488 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0883/1334] hw/intc/sh_intc: Drop another useless macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The INT_REG_PARAMS macro was only used a few times within one function on adjacent lines and is actually more complex than writing out the parameters so simplify it by expanding the macro at call sites and dropping the #define. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 175d12b371..b908be0ff5 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -432,16 +432,12 @@ int sh_intc_init(MemoryRegion *sysmem, memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc, "intc", 0x100000000ULL); -#define INT_REG_PARAMS(reg_struct, type, action, j) \ - reg_struct->action##_reg, #type, #action, j if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { struct intc_mask_reg *mr = desc->mask_regs + i; - j += sh_intc_register(sysmem, desc, - INT_REG_PARAMS(mr, mask, set, j)); - j += sh_intc_register(sysmem, desc, - INT_REG_PARAMS(mr, mask, clr, j)); + j += sh_intc_register(sysmem, desc, mr->set_reg, "mask", "set", j); + j += sh_intc_register(sysmem, desc, mr->clr_reg, "mask", "clr", j); } } @@ -449,13 +445,10 @@ int sh_intc_init(MemoryRegion *sysmem, for (i = 0; i < desc->nr_prio_regs; i++) { struct intc_prio_reg *pr = desc->prio_regs + i; - j += sh_intc_register(sysmem, desc, - INT_REG_PARAMS(pr, prio, set, j)); - j += sh_intc_register(sysmem, desc, - INT_REG_PARAMS(pr, prio, clr, j)); + j += sh_intc_register(sysmem, desc, pr->set_reg, "prio", "set", j); + j += sh_intc_register(sysmem, desc, pr->clr_reg, "prio", "clr", j); } } -#undef INT_REG_PARAMS return 0; } From 51cb902baca1ccfba270fa5a1f230d85301a68e6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0884/1334] hw/intc/sh_intc: Move sh_intc_register() closer to its only user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sh_intc_register() function is only used at one place. Move them together so it's easier to see what's going on. Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <49f2742bc67cba7164385fafad204ab1e1bd3a0b.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 60 +++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index b908be0ff5..1a2824f8e0 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -269,36 +269,6 @@ struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) return NULL; } -static unsigned int sh_intc_register(MemoryRegion *sysmem, - struct intc_desc *desc, - const unsigned long address, - const char *type, - const char *action, - const unsigned int index) -{ - char name[60]; - MemoryRegion *iomem, *iomem_p4, *iomem_a7; - - if (!address) { - return 0; - } - - iomem = &desc->iomem; - iomem_p4 = desc->iomem_aliases + index; - iomem_a7 = iomem_p4 + 1; - - snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "p4"); - memory_region_init_alias(iomem_p4, NULL, name, iomem, A7ADDR(address), 4); - memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4); - - snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "a7"); - memory_region_init_alias(iomem_a7, NULL, name, iomem, A7ADDR(address), 4); - memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7); - - /* used to increment aliases index */ - return 2; -} - static void sh_intc_register_source(struct intc_desc *desc, intc_enum source, struct intc_group *groups, @@ -398,6 +368,36 @@ void sh_intc_register_sources(struct intc_desc *desc, } } +static unsigned int sh_intc_register(MemoryRegion *sysmem, + struct intc_desc *desc, + const unsigned long address, + const char *type, + const char *action, + const unsigned int index) +{ + char name[60]; + MemoryRegion *iomem, *iomem_p4, *iomem_a7; + + if (!address) { + return 0; + } + + iomem = &desc->iomem; + iomem_p4 = desc->iomem_aliases + index; + iomem_a7 = iomem_p4 + 1; + + snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "p4"); + memory_region_init_alias(iomem_p4, NULL, name, iomem, A7ADDR(address), 4); + memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4); + + snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "a7"); + memory_region_init_alias(iomem_a7, NULL, name, iomem, A7ADDR(address), 4); + memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7); + + /* used to increment aliases index */ + return 2; +} + int sh_intc_init(MemoryRegion *sysmem, struct intc_desc *desc, int nr_sources, From 46ea1f8236ffdf80c52dad79ee7d2dc18ed5eda1 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0885/1334] hw/intc/sh_intc: Remove excessive parenthesis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop unneded parenthesis and split up one complex expression to write it with less brackets so it's easier to follow. Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 1a2824f8e0..416581dc07 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -23,7 +23,7 @@ void sh_intc_toggle_source(struct intc_source *source, int pending_changed = 0; int old_pending; - if ((source->enable_count == source->enable_max) && (enable_adj == -1)) { + if (source->enable_count == source->enable_max && enable_adj == -1) { enable_changed = -1; } source->enable_count += enable_adj; @@ -68,7 +68,7 @@ void sh_intc_toggle_source(struct intc_source *source, static void sh_intc_set_irq(void *opaque, int n, int level) { struct intc_desc *desc = opaque; - struct intc_source *source = &(desc->sources[n]); + struct intc_source *source = &desc->sources[n]; if (level && !source->asserted) { sh_intc_toggle_source(source, 0, 1); @@ -163,7 +163,7 @@ static void sh_intc_locate(struct intc_desc *desc, *modep = mode | INTC_MODE_IS_PRIO; *datap = &pr->value; *enums = pr->enum_ids; - *first = (pr->reg_width / pr->field_width) - 1; + *first = pr->reg_width / pr->field_width - 1; *width = pr->field_width; return; } @@ -244,7 +244,8 @@ static void sh_intc_write(void *opaque, hwaddr offset, } for (k = 0; k <= first; k++) { - mask = ((1 << width) - 1) << ((first - k) * width); + mask = (1 << width) - 1; + mask <<= (first - k) * width; if ((*valuep & mask) == (value & mask)) { continue; From 12201fe38a592695eea647c9600d08a4622c1431 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0886/1334] hw/intc/sh_intc: Use array index instead of pointer arithmetics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address of element i is one word thus clearer than array + i. Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 416581dc07..e6e01428f9 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -89,7 +89,7 @@ int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) } for (i = 0; i < desc->nr_sources; i++) { - struct intc_source *source = desc->sources + i; + struct intc_source *source = &desc->sources[i]; if (source->pending) { trace_sh_intc_pending(desc->pending, source->vect); @@ -137,7 +137,7 @@ static void sh_intc_locate(struct intc_desc *desc, if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; + struct intc_mask_reg *mr = &desc->mask_regs[i]; mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); if (mode == INTC_MODE_NONE) { @@ -154,7 +154,7 @@ static void sh_intc_locate(struct intc_desc *desc, if (desc->prio_regs) { for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; + struct intc_prio_reg *pr = &desc->prio_regs[i]; mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); if (mode == INTC_MODE_NONE) { @@ -175,7 +175,7 @@ static void sh_intc_locate(struct intc_desc *desc, static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, int enable, int is_group) { - struct intc_source *source = desc->sources + id; + struct intc_source *source = &desc->sources[id]; if (!id) { return; @@ -265,7 +265,7 @@ static const MemoryRegionOps sh_intc_ops = { struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) { if (id) { - return desc->sources + id; + return &desc->sources[id]; } return NULL; } @@ -280,7 +280,7 @@ static void sh_intc_register_source(struct intc_desc *desc, if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; + struct intc_mask_reg *mr = &desc->mask_regs[i]; for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) { if (mr->enum_ids[k] != source) { @@ -296,7 +296,7 @@ static void sh_intc_register_source(struct intc_desc *desc, if (desc->prio_regs) { for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; + struct intc_prio_reg *pr = &desc->prio_regs[i]; for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) { if (pr->enum_ids[k] != source) { @@ -312,7 +312,7 @@ static void sh_intc_register_source(struct intc_desc *desc, if (groups) { for (i = 0; i < nr_groups; i++) { - struct intc_group *gr = groups + i; + struct intc_group *gr = &groups[i]; for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) { if (gr->enum_ids[k] != source) { @@ -338,7 +338,7 @@ void sh_intc_register_sources(struct intc_desc *desc, struct intc_source *s; for (i = 0; i < nr_vectors; i++) { - struct intc_vect *vect = vectors + i; + struct intc_vect *vect = &vectors[i]; sh_intc_register_source(desc, vect->enum_id, groups, nr_groups); s = sh_intc_source(desc, vect->enum_id); @@ -351,7 +351,7 @@ void sh_intc_register_sources(struct intc_desc *desc, if (groups) { for (i = 0; i < nr_groups; i++) { - struct intc_group *gr = groups + i; + struct intc_group *gr = &groups[i]; s = sh_intc_source(desc, gr->enum_id); s->next_enum_id = gr->enum_ids[0]; @@ -384,7 +384,7 @@ static unsigned int sh_intc_register(MemoryRegion *sysmem, } iomem = &desc->iomem; - iomem_p4 = desc->iomem_aliases + index; + iomem_p4 = &desc->iomem_aliases[index]; iomem_a7 = iomem_p4 + 1; snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "p4"); @@ -424,7 +424,7 @@ int sh_intc_init(MemoryRegion *sysmem, desc->sources = g_malloc0(i); for (i = 0; i < desc->nr_sources; i++) { - struct intc_source *source = desc->sources + i; + struct intc_source *source = &desc->sources[i]; source->parent = desc; } @@ -435,7 +435,7 @@ int sh_intc_init(MemoryRegion *sysmem, if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; + struct intc_mask_reg *mr = &desc->mask_regs[i]; j += sh_intc_register(sysmem, desc, mr->set_reg, "mask", "set", j); j += sh_intc_register(sysmem, desc, mr->clr_reg, "mask", "clr", j); @@ -444,7 +444,7 @@ int sh_intc_init(MemoryRegion *sysmem, if (desc->prio_regs) { for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; + struct intc_prio_reg *pr = &desc->prio_regs[i]; j += sh_intc_register(sysmem, desc, pr->set_reg, "prio", "set", j); j += sh_intc_register(sysmem, desc, pr->clr_reg, "prio", "clr", j); From 9b12fb10b731d8d9cbb6cc0bf200d1c48de7f20d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0887/1334] hw/intc/sh_intc: Inline and drop sh_intc_source() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is very simple and provides no advantage. Call sites become simpler without it so just write it in line and drop the separate function. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 54 ++++++++++++++++------------------------ hw/sh4/sh7750.c | 4 +-- include/hw/sh4/sh_intc.h | 2 +- 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index e6e01428f9..9995213cb0 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -262,33 +262,22 @@ static const MemoryRegionOps sh_intc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) -{ - if (id) { - return &desc->sources[id]; - } - return NULL; -} - static void sh_intc_register_source(struct intc_desc *desc, intc_enum source, struct intc_group *groups, int nr_groups) { unsigned int i, k; - struct intc_source *s; + intc_enum id; if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { struct intc_mask_reg *mr = &desc->mask_regs[i]; for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) { - if (mr->enum_ids[k] != source) { - continue; - } - s = sh_intc_source(desc, mr->enum_ids[k]); - if (s) { - s->enable_max++; + id = mr->enum_ids[k]; + if (id && id == source) { + desc->sources[id].enable_max++; } } } @@ -299,12 +288,9 @@ static void sh_intc_register_source(struct intc_desc *desc, struct intc_prio_reg *pr = &desc->prio_regs[i]; for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) { - if (pr->enum_ids[k] != source) { - continue; - } - s = sh_intc_source(desc, pr->enum_ids[k]); - if (s) { - s->enable_max++; + id = pr->enum_ids[k]; + if (id && id == source) { + desc->sources[id].enable_max++; } } } @@ -315,12 +301,9 @@ static void sh_intc_register_source(struct intc_desc *desc, struct intc_group *gr = &groups[i]; for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) { - if (gr->enum_ids[k] != source) { - continue; - } - s = sh_intc_source(desc, gr->enum_ids[k]); - if (s) { - s->enable_max++; + id = gr->enum_ids[k]; + if (id && id == source) { + desc->sources[id].enable_max++; } } } @@ -335,14 +318,16 @@ void sh_intc_register_sources(struct intc_desc *desc, int nr_groups) { unsigned int i, k; + intc_enum id; struct intc_source *s; for (i = 0; i < nr_vectors; i++) { struct intc_vect *vect = &vectors[i]; sh_intc_register_source(desc, vect->enum_id, groups, nr_groups); - s = sh_intc_source(desc, vect->enum_id); - if (s) { + id = vect->enum_id; + if (id) { + s = &desc->sources[id]; s->vect = vect->vect; trace_sh_intc_register("source", vect->enum_id, s->vect, s->enable_count, s->enable_max); @@ -353,14 +338,16 @@ void sh_intc_register_sources(struct intc_desc *desc, for (i = 0; i < nr_groups; i++) { struct intc_group *gr = &groups[i]; - s = sh_intc_source(desc, gr->enum_id); + id = gr->enum_id; + s = &desc->sources[id]; s->next_enum_id = gr->enum_ids[0]; for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) { if (!gr->enum_ids[k]) { continue; } - s = sh_intc_source(desc, gr->enum_ids[k - 1]); + id = gr->enum_ids[k - 1]; + s = &desc->sources[id]; s->next_enum_id = gr->enum_ids[k]; } trace_sh_intc_register("group", gr->enum_id, 0xffff, @@ -462,7 +449,10 @@ void sh_intc_set_irl(void *opaque, int n, int level) { struct intc_source *s = opaque; int i, irl = level ^ 15; - for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) { + intc_enum id = s->next_enum_id; + + for (i = 0; id; id = s->next_enum_id, i++) { + s = &s->parent->sources[id]; if (i == irl) { sh_intc_toggle_source(s, s->enable_count ? 0 : 1, s->asserted ? 0 : 1); diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index f7d21f6170..43dfb6497b 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -899,6 +899,6 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) qemu_irq sh7750_irl(SH7750State *s) { - sh_intc_toggle_source(sh_intc_source(&s->intc, IRL), 1, 0); /* enable */ - return qemu_allocate_irq(sh_intc_set_irl, sh_intc_source(&s->intc, IRL), 0); + sh_intc_toggle_source(&s->intc.sources[IRL], 1, 0); /* enable */ + return qemu_allocate_irq(sh_intc_set_irl, &s->intc.sources[IRL], 0); } diff --git a/include/hw/sh4/sh_intc.h b/include/hw/sh4/sh_intc.h index 65f3425057..f62d5c5e13 100644 --- a/include/hw/sh4/sh_intc.h +++ b/include/hw/sh4/sh_intc.h @@ -58,7 +58,7 @@ struct intc_desc { }; int sh_intc_get_pending_vector(struct intc_desc *desc, int imask); -struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id); + void sh_intc_toggle_source(struct intc_source *source, int enable_adj, int assert_adj); From 85208f7a9756948ed16ef09d7982584528904430 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0888/1334] hw/intc/sh_intc: Replace abort() with g_assert_not_reached() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the places that call abort should not happen which is better marked by g_assert_not_reached. Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <039e6a784532f2af27f8adeafdb8e0391722f567.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 9995213cb0..a0db742d75 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -96,8 +96,7 @@ int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) return source->vect; } } - - abort(); + g_assert_not_reached(); } typedef enum { @@ -168,8 +167,7 @@ static void sh_intc_locate(struct intc_desc *desc, return; } } - - abort(); + g_assert_not_reached(); } static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, @@ -240,7 +238,7 @@ static void sh_intc_write(void *opaque, hwaddr offset, value = *valuep & ~value; break; default: - abort(); + g_assert_not_reached(); } for (k = 0; k <= first; k++) { From 418a221c2b8a97838980e61cdfef356ec6976e4b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0889/1334] hw/intc/sh_intc: Avoid using continue in loops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of if !expr continue else do something it is more straight forward to say if expr then do something, especially if the action is just a few lines. Remove such uses of continue to make the code easier to follow. Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Message-Id: <0efaa5e7a1a3ee11f82b3bb1942c287576c67f8b.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index a0db742d75..1a363d4962 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -139,15 +139,14 @@ static void sh_intc_locate(struct intc_desc *desc, struct intc_mask_reg *mr = &desc->mask_regs[i]; mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); - if (mode == INTC_MODE_NONE) { - continue; + if (mode != INTC_MODE_NONE) { + *modep = mode; + *datap = &mr->value; + *enums = mr->enum_ids; + *first = mr->reg_width - 1; + *width = 1; + return; } - *modep = mode; - *datap = &mr->value; - *enums = mr->enum_ids; - *first = mr->reg_width - 1; - *width = 1; - return; } } @@ -156,15 +155,14 @@ static void sh_intc_locate(struct intc_desc *desc, struct intc_prio_reg *pr = &desc->prio_regs[i]; mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); - if (mode == INTC_MODE_NONE) { - continue; + if (mode != INTC_MODE_NONE) { + *modep = mode | INTC_MODE_IS_PRIO; + *datap = &pr->value; + *enums = pr->enum_ids; + *first = pr->reg_width / pr->field_width - 1; + *width = pr->field_width; + return; } - *modep = mode | INTC_MODE_IS_PRIO; - *datap = &pr->value; - *enums = pr->enum_ids; - *first = pr->reg_width / pr->field_width - 1; - *width = pr->field_width; - return; } } g_assert_not_reached(); @@ -245,10 +243,9 @@ static void sh_intc_write(void *opaque, hwaddr offset, mask = (1 << width) - 1; mask <<= (first - k) * width; - if ((*valuep & mask) == (value & mask)) { - continue; + if ((*valuep & mask) != (value & mask)) { + sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0); } - sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0); } *valuep = value; @@ -341,12 +338,11 @@ void sh_intc_register_sources(struct intc_desc *desc, s->next_enum_id = gr->enum_ids[0]; for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) { - if (!gr->enum_ids[k]) { - continue; + if (gr->enum_ids[k]) { + id = gr->enum_ids[k - 1]; + s = &desc->sources[id]; + s->next_enum_id = gr->enum_ids[k]; } - id = gr->enum_ids[k - 1]; - s = &desc->sources[id]; - s->next_enum_id = gr->enum_ids[k]; } trace_sh_intc_register("group", gr->enum_id, 0xffff, s->enable_count, s->enable_max); From 36cf5ee8852a3aac56be160ad87cc49974278c46 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0890/1334] hw/intc/sh_intc: Simplify allocating sources array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use g_new0 instead of g_malloc0 and avoid some unneeded temporary variable assignments. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <72efc4f2c4ff8b96848d03dca08e4541ee4076f6.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 1a363d4962..3356b42202 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -399,21 +399,14 @@ int sh_intc_init(MemoryRegion *sysmem, /* Allocate 4 MemoryRegions per register (2 actions * 2 aliases) */ desc->iomem_aliases = g_new0(MemoryRegion, (nr_mask_regs + nr_prio_regs) * 4); - - j = 0; - i = sizeof(struct intc_source) * nr_sources; - desc->sources = g_malloc0(i); - - for (i = 0; i < desc->nr_sources; i++) { - struct intc_source *source = &desc->sources[i]; - - source->parent = desc; + desc->sources = g_new0(struct intc_source, nr_sources); + for (i = 0; i < nr_sources; i++) { + desc->sources[i].parent = desc; } - desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources); memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc, "intc", 0x100000000ULL); - + j = 0; if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { struct intc_mask_reg *mr = &desc->mask_regs[i]; From 32331787896104022deb024906f1cca9606cfdc6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0891/1334] hw/intc/sh_intc: Remove unneeded local variable initialisers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sh_intc_locate function will either init these or not return so no need to initialise them. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <15e04aa665c68ab5df47bbf505346d413be2fc1c.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/sh_intc.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/hw/intc/sh_intc.c b/hw/intc/sh_intc.c index 3356b42202..c9b0b0c1ec 100644 --- a/hw/intc/sh_intc.c +++ b/hw/intc/sh_intc.c @@ -195,14 +195,13 @@ static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, } } -static uint64_t sh_intc_read(void *opaque, hwaddr offset, - unsigned size) +static uint64_t sh_intc_read(void *opaque, hwaddr offset, unsigned size) { struct intc_desc *desc = opaque; - intc_enum *enum_ids = NULL; - unsigned int first = 0; - unsigned int width = 0; - unsigned int mode = 0; + intc_enum *enum_ids; + unsigned int first; + unsigned int width; + unsigned int mode; unsigned long *valuep; sh_intc_locate(desc, (unsigned long)offset, &valuep, @@ -215,12 +214,12 @@ static void sh_intc_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { struct intc_desc *desc = opaque; - intc_enum *enum_ids = NULL; - unsigned int first = 0; - unsigned int width = 0; - unsigned int mode = 0; - unsigned int k; + intc_enum *enum_ids; + unsigned int first; + unsigned int width; + unsigned int mode; unsigned long *valuep; + unsigned int k; unsigned long mask; trace_sh_intc_write(size, (uint64_t)offset, value); From 5d9b737e5144baff1c3a4205ff57d974a347c204 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0892/1334] hw/timer/sh_timer: Rename sh_timer_state to SHTimerState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to coding style types should be camel case, also remove unneded casts from void *. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/sh_timer.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index e1b6145df8..2038adfb0a 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -45,11 +45,11 @@ typedef struct { int feat; int enabled; qemu_irq irq; -} sh_timer_state; +} SHTimerState; /* Check all active timers, and schedule the next timer interrupt. */ -static void sh_timer_update(sh_timer_state *s) +static void sh_timer_update(SHTimerState *s) { int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE); @@ -62,7 +62,7 @@ static void sh_timer_update(sh_timer_state *s) static uint32_t sh_timer_read(void *opaque, hwaddr offset) { - sh_timer_state *s = (sh_timer_state *)opaque; + SHTimerState *s = opaque; switch (offset >> 2) { case OFFSET_TCOR: @@ -85,7 +85,7 @@ static uint32_t sh_timer_read(void *opaque, hwaddr offset) static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value) { - sh_timer_state *s = (sh_timer_state *)opaque; + SHTimerState *s = opaque; int freq; switch (offset >> 2) { @@ -200,7 +200,7 @@ static void sh_timer_write(void *opaque, hwaddr offset, static void sh_timer_start_stop(void *opaque, int enable) { - sh_timer_state *s = (sh_timer_state *)opaque; + SHTimerState *s = opaque; trace_sh_timer_start_stop(enable, s->enabled); ptimer_transaction_begin(s->timer); @@ -216,14 +216,14 @@ static void sh_timer_start_stop(void *opaque, int enable) static void sh_timer_tick(void *opaque) { - sh_timer_state *s = (sh_timer_state *)opaque; + SHTimerState *s = opaque; s->int_level = s->enabled; sh_timer_update(s); } static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq) { - sh_timer_state *s; + SHTimerState *s; s = g_malloc0(sizeof(*s)); s->freq = freq; @@ -259,7 +259,7 @@ typedef struct { static uint64_t tmu012_read(void *opaque, hwaddr offset, unsigned size) { - tmu012_state *s = (tmu012_state *)opaque; + tmu012_state *s = opaque; trace_sh_timer_read(offset); if (offset >= 0x20) { @@ -289,7 +289,7 @@ static uint64_t tmu012_read(void *opaque, hwaddr offset, static void tmu012_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - tmu012_state *s = (tmu012_state *)opaque; + tmu012_state *s = opaque; trace_sh_timer_write(offset, value); if (offset >= 0x20) { From f64ccec414e72754921c25de6395d7e3b10a0c9e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0893/1334] hw/timer/sh_timer: Do not wrap lines that are not too long MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's more readable to keep things on one line if it fits the length limit. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <97bc2a38991f33fd0c8cc2e4d0a3a29b20c47d1f.1635541329.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/sh_timer.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 2038adfb0a..250ad41b48 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -82,8 +82,7 @@ static uint32_t sh_timer_read(void *opaque, hwaddr offset) } } -static void sh_timer_write(void *opaque, hwaddr offset, - uint32_t value) +static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value) { SHTimerState *s = opaque; int freq; @@ -256,8 +255,7 @@ typedef struct { int feat; } tmu012_state; -static uint64_t tmu012_read(void *opaque, hwaddr offset, - unsigned size) +static uint64_t tmu012_read(void *opaque, hwaddr offset, unsigned size) { tmu012_state *s = opaque; @@ -336,8 +334,7 @@ static const MemoryRegionOps tmu012_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -void tmu012_init(MemoryRegion *sysmem, hwaddr base, - int feat, uint32_t freq, +void tmu012_init(MemoryRegion *sysmem, hwaddr base, int feat, uint32_t freq, qemu_irq ch0_irq, qemu_irq ch1_irq, qemu_irq ch2_irq0, qemu_irq ch2_irq1) { From 65307c7792a50bffe036423ec21107f4fb9c74e3 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 30 Oct 2021 01:27:40 +0200 Subject: [PATCH 0894/1334] hw/timer/sh_timer: Fix timer memory region size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timer unit only has registers that fit in a region 0x30 bytes long. No need to have the timer region larger than that. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/sh_timer.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 250ad41b48..587fa9414a 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -350,15 +350,14 @@ void tmu012_init(MemoryRegion *sysmem, hwaddr base, int feat, uint32_t freq, ch2_irq0); /* ch2_irq1 not supported */ } - memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, - "timer", 0x100000000ULL); + memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, "timer", 0x30); memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4", - &s->iomem, 0, 0x1000); + &s->iomem, 0, memory_region_size(&s->iomem)); memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7", - &s->iomem, 0, 0x1000); + &s->iomem, 0, memory_region_size(&s->iomem)); memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); /* ??? Save/restore. */ } From 46e44759fc24a05e338cd37a735b4aad5422e717 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Oct 2021 23:02:09 +0200 Subject: [PATCH 0895/1334] hw/timer/sh_timer: Remove use of hw_error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hw_error function calls abort and is not meant to be used by devices. Use qemu_log_mask instead to log and ignore invalid accesses. Also fix format strings to allow dropping type casts of hwaddr and use __func__ instead of hard coding function name in the message which were wrong in two cases. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/sh_timer.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 587fa9414a..c72c327bfa 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "exec/memory.h" -#include "hw/hw.h" +#include "qemu/log.h" #include "hw/irq.h" #include "hw/sh4/sh.h" #include "hw/timer/tmu012.h" @@ -75,11 +75,10 @@ static uint32_t sh_timer_read(void *opaque, hwaddr offset) if (s->feat & TIMER_FEAT_CAPT) { return s->tcpr; } - /* fall through */ - default: - hw_error("sh_timer_read: Bad offset %x\n", (int)offset); - return 0; } + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; } static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value) @@ -134,7 +133,8 @@ static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value) } /* fallthrough */ default: - hw_error("sh_timer_write: Reserved TPSC value\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Reserved TPSC value\n", __func__); } switch ((value & TIMER_TCR_CKEG) >> 3) { case 0: @@ -147,7 +147,8 @@ static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value) } /* fallthrough */ default: - hw_error("sh_timer_write: Reserved CKEG value\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Reserved CKEG value\n", __func__); } switch ((value & TIMER_TCR_ICPE) >> 6) { case 0: @@ -159,7 +160,8 @@ static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value) } /* fallthrough */ default: - hw_error("sh_timer_write: Reserved ICPE value\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Reserved ICPE value\n", __func__); } if ((value & TIMER_TCR_UNF) == 0) { s->int_level = 0; @@ -168,13 +170,15 @@ static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value) value &= ~TIMER_TCR_UNF; if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) { - hw_error("sh_timer_write: Reserved ICPF value\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Reserved ICPF value\n", __func__); } value &= ~TIMER_TCR_ICPF; /* capture not supported */ if (value & TIMER_TCR_RESERVED) { - hw_error("sh_timer_write: Reserved TCR bits set\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Reserved TCR bits set\n", __func__); } s->tcr = value; ptimer_set_limit(s->timer, s->tcor, 0); @@ -192,7 +196,8 @@ static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value) } /* fallthrough */ default: - hw_error("sh_timer_write: Bad offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); } sh_timer_update(s); } @@ -262,7 +267,9 @@ static uint64_t tmu012_read(void *opaque, hwaddr offset, unsigned size) trace_sh_timer_read(offset); if (offset >= 0x20) { if (!(s->feat & TMU012_FEAT_3CHAN)) { - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad channel offset 0x%" HWADDR_PRIx "\n", + __func__, offset); } return sh_timer_read(s->timer[2], offset - 0x20); } @@ -280,7 +287,8 @@ static uint64_t tmu012_read(void *opaque, hwaddr offset, unsigned size) return s->tocr; } - hw_error("tmu012_write: Bad offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); return 0; } @@ -292,7 +300,9 @@ static void tmu012_write(void *opaque, hwaddr offset, trace_sh_timer_write(offset, value); if (offset >= 0x20) { if (!(s->feat & TMU012_FEAT_3CHAN)) { - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad channel offset 0x%" HWADDR_PRIx "\n", + __func__, offset); } sh_timer_write(s->timer[2], offset - 0x20, value); return; @@ -315,7 +325,7 @@ static void tmu012_write(void *opaque, hwaddr offset, sh_timer_start_stop(s->timer[2], value & (1 << 2)); } else { if (value & (1 << 2)) { - hw_error("tmu012_write: Bad channel\n"); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad channel\n", __func__); } } From 0fdbb7d2c1ecb761b985b176b9bb159d483d9514 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Sep 2021 19:25:22 -0700 Subject: [PATCH 0896/1334] accel/tcg: Split out adjust_signal_pc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out a function to adjust the raw signal pc into a value that could be passed to cpu_restore_state. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- v2: Adjust pc in place; return MMUAccessType. --- accel/tcg/user-exec.c | 41 +++++++++++++++++++++++++---------------- include/exec/exec-all.h | 10 ++++++++++ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index e6bb29b42d..c02d509ec6 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -57,18 +57,11 @@ static void QEMU_NORETURN cpu_exit_tb_from_sighandler(CPUState *cpu, cpu_loop_exit_noexc(cpu); } -/* 'pc' is the host PC at which the exception was raised. 'address' is - the effective address of the memory exception. 'is_write' is 1 if a - write caused the exception and otherwise 0'. 'old_set' is the - signal set which should be restored */ -static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, - int is_write, sigset_t *old_set) +/* + * Adjust the pc to pass to cpu_restore_state; return the memop type. + */ +MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write) { - CPUState *cpu = current_cpu; - CPUClass *cc; - unsigned long address = (unsigned long)info->si_addr; - MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD; - switch (helper_retaddr) { default: /* @@ -77,7 +70,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, * pointer into the generated code that will unwind to the * correct guest pc. */ - pc = helper_retaddr; + *pc = helper_retaddr; break; case 0: @@ -97,7 +90,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, * Therefore, adjust to compensate for what will be done later * by cpu_restore_state_from_tb. */ - pc += GETPC_ADJ; + *pc += GETPC_ADJ; break; case 1: @@ -113,12 +106,28 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, * * Like tb_gen_code, release the memory lock before cpu_loop_exit. */ - pc = 0; - access_type = MMU_INST_FETCH; mmap_unlock(); - break; + *pc = 0; + return MMU_INST_FETCH; } + return is_write ? MMU_DATA_STORE : MMU_DATA_LOAD; +} + +/* + * 'pc' is the host PC at which the exception was raised. + * 'address' is the effective address of the memory exception. + * 'is_write' is 1 if a write caused the exception and otherwise 0. + * 'old_set' is the signal set which should be restored. + */ +static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, + int is_write, sigset_t *old_set) +{ + CPUState *cpu = current_cpu; + CPUClass *cc; + unsigned long address = (unsigned long)info->si_addr; + MMUAccessType access_type = adjust_signal_pc(&pc, is_write); + /* For synchronous signals we expect to be coming from the vCPU * thread (so current_cpu should be valid) and either from running * code or during translation which can fault as we cross pages. diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 9d5987ba04..e54f8e5d65 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -663,6 +663,16 @@ static inline tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, return addr; } +/** + * adjust_signal_pc: + * @pc: raw pc from the host signal ucontext_t. + * @is_write: host memory operation was write, or read-modify-write. + * + * Alter @pc as required for unwinding. Return the type of the + * guest memory access -- host reads may be for guest execution. + */ +MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); + /** * cpu_signal_handler * @signum: host signal number From f920ffdd8ed6d0abb34fdf2bbb85926cfb40aef2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 13 Sep 2021 13:01:07 -0700 Subject: [PATCH 0897/1334] accel/tcg: Move clear_helper_retaddr to cpu loop Currently there are only two places that require we reset this value before exiting to the main loop, but that will change. Reviewed-by: Warner Losh Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 3 ++- accel/tcg/user-exec.c | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c9764c1325..bba4672632 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -462,6 +462,7 @@ void cpu_exec_step_atomic(CPUState *cpu) * memory. */ #ifndef CONFIG_SOFTMMU + clear_helper_retaddr(); tcg_debug_assert(!have_mmap_lock()); #endif if (qemu_mutex_iothread_locked()) { @@ -471,7 +472,6 @@ void cpu_exec_step_atomic(CPUState *cpu) qemu_plugin_disable_mem_helpers(cpu); } - /* * As we start the exclusive region before codegen we must still * be in the region if we longjump out of either the codegen or @@ -916,6 +916,7 @@ int cpu_exec(CPUState *cpu) #endif #ifndef CONFIG_SOFTMMU + clear_helper_retaddr(); tcg_debug_assert(!have_mmap_lock()); #endif if (qemu_mutex_iothread_locked()) { diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index c02d509ec6..3f3e793b7b 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -175,7 +175,6 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, * currently executing TB was modified and must be exited * immediately. Clear helper_retaddr for next execution. */ - clear_helper_retaddr(); cpu_exit_tb_from_sighandler(cpu, old_set); /* NORETURN */ @@ -193,7 +192,6 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, * an exception. Undo signal and retaddr state prior to longjmp. */ sigprocmask(SIG_SETMASK, old_set, NULL); - clear_helper_retaddr(); cc = CPU_GET_CLASS(cpu); cc->tcg_ops->tlb_fill(cpu, address, 0, access_type, From 5e38ba7dde963414dddc8a83848701b49d0bcb00 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Sep 2021 19:47:29 -0700 Subject: [PATCH 0898/1334] accel/tcg: Split out handle_sigsegv_accerr_write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the major portion of handle_cpu_signal which is specific to tcg, handling the page protections for the translations. Most of the rest will migrate to linux-user/ shortly. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- v2: Pass guest address to handle_sigsegv_accerr_write. --- accel/tcg/user-exec.c | 101 ++++++++++++++++++++++++---------------- include/exec/exec-all.h | 12 +++++ 2 files changed, 72 insertions(+), 41 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 3f3e793b7b..b83f8d12f4 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -114,6 +114,52 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write) return is_write ? MMU_DATA_STORE : MMU_DATA_LOAD; } +/** + * handle_sigsegv_accerr_write: + * @cpu: the cpu context + * @old_set: the sigset_t from the signal ucontext_t + * @host_pc: the host pc, adjusted for the signal + * @guest_addr: the guest address of the fault + * + * Return true if the write fault has been handled, and should be re-tried. + * + * Note that it is important that we don't call page_unprotect() unless + * this is really a "write to nonwriteable page" fault, because + * page_unprotect() assumes that if it is called for an access to + * a page that's writeable this means we had two threads racing and + * another thread got there first and already made the page writeable; + * so we will retry the access. If we were to call page_unprotect() + * for some other kind of fault that should really be passed to the + * guest, we'd end up in an infinite loop of retrying the faulting access. + */ +bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, + uintptr_t host_pc, abi_ptr guest_addr) +{ + switch (page_unprotect(guest_addr, host_pc)) { + case 0: + /* + * Fault not caused by a page marked unwritable to protect + * cached translations, must be the guest binary's problem. + */ + return false; + case 1: + /* + * Fault caused by protection of cached translation; TBs + * invalidated, so resume execution. + */ + return true; + case 2: + /* + * Fault caused by protection of cached translation, and the + * currently executing TB was modified and must be exited immediately. + */ + cpu_exit_tb_from_sighandler(cpu, old_set); + /* NORETURN */ + default: + g_assert_not_reached(); + } +} + /* * 'pc' is the host PC at which the exception was raised. * 'address' is the effective address of the memory exception. @@ -125,8 +171,9 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, { CPUState *cpu = current_cpu; CPUClass *cc; - unsigned long address = (unsigned long)info->si_addr; + unsigned long host_addr = (unsigned long)info->si_addr; MMUAccessType access_type = adjust_signal_pc(&pc, is_write); + abi_ptr guest_addr; /* For synchronous signals we expect to be coming from the vCPU * thread (so current_cpu should be valid) and either from running @@ -143,49 +190,21 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, #if defined(DEBUG_SIGNAL) printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); + pc, host_addr, is_write, *(unsigned long *)old_set); #endif - /* XXX: locking issue */ - /* Note that it is important that we don't call page_unprotect() unless - * this is really a "write to nonwriteable page" fault, because - * page_unprotect() assumes that if it is called for an access to - * a page that's writeable this means we had two threads racing and - * another thread got there first and already made the page writeable; - * so we will retry the access. If we were to call page_unprotect() - * for some other kind of fault that should really be passed to the - * guest, we'd end up in an infinite loop of retrying the faulting - * access. - */ - if (is_write && info->si_signo == SIGSEGV && info->si_code == SEGV_ACCERR && - h2g_valid(address)) { - switch (page_unprotect(h2g(address), pc)) { - case 0: - /* Fault not caused by a page marked unwritable to protect - * cached translations, must be the guest binary's problem. - */ - break; - case 1: - /* Fault caused by protection of cached translation; TBs - * invalidated, so resume execution. Retain helper_retaddr - * for a possible second fault. - */ - return 1; - case 2: - /* Fault caused by protection of cached translation, and the - * currently executing TB was modified and must be exited - * immediately. Clear helper_retaddr for next execution. - */ - cpu_exit_tb_from_sighandler(cpu, old_set); - /* NORETURN */ - - default: - g_assert_not_reached(); - } - } /* Convert forcefully to guest address space, invalid addresses are still valid segv ones */ - address = h2g_nocheck(address); + guest_addr = h2g_nocheck(host_addr); + + /* XXX: locking issue */ + if (is_write && + info->si_signo == SIGSEGV && + info->si_code == SEGV_ACCERR && + h2g_valid(host_addr) && + handle_sigsegv_accerr_write(cpu, old_set, pc, guest_addr)) { + return 1; + } /* * There is no way the target can handle this other than raising @@ -194,7 +213,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, sigprocmask(SIG_SETMASK, old_set, NULL); cc = CPU_GET_CLASS(cpu); - cc->tcg_ops->tlb_fill(cpu, address, 0, access_type, + cc->tcg_ops->tlb_fill(cpu, guest_addr, 0, access_type, MMU_USER_IDX, false, pc); g_assert_not_reached(); } diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index e54f8e5d65..5f94d799aa 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -673,6 +673,18 @@ static inline tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, */ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); +/** + * handle_sigsegv_accerr_write: + * @cpu: the cpu context + * @old_set: the sigset_t from the signal ucontext_t + * @host_pc: the host pc, adjusted for the signal + * @host_addr: the host address of the fault + * + * Return true if the write fault has been handled, and should be re-tried. + */ +bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, + uintptr_t host_pc, abi_ptr guest_addr); + /** * cpu_signal_handler * @signum: host signal number From 940b30904e928854250988c3802f334c8ee12bd4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 13 Sep 2021 13:04:11 -0700 Subject: [PATCH 0899/1334] accel/tcg: Fold cpu_exit_tb_from_sighandler into caller Remove the comment about siglongjmp. We do use sigsetjmp in the main cpu loop, but we do not save the signal mask as most exits from the cpu loop do not require them. Reviewed-by: Warner Losh Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index b83f8d12f4..b1183aa4b3 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -46,17 +46,6 @@ __thread uintptr_t helper_retaddr; //#define DEBUG_SIGNAL -/* exit the current TB from a signal handler. The host registers are - restored in a state compatible with the CPU emulator - */ -static void QEMU_NORETURN cpu_exit_tb_from_sighandler(CPUState *cpu, - sigset_t *old_set) -{ - /* XXX: use siglongjmp ? */ - sigprocmask(SIG_SETMASK, old_set, NULL); - cpu_loop_exit_noexc(cpu); -} - /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ @@ -153,7 +142,8 @@ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, * Fault caused by protection of cached translation, and the * currently executing TB was modified and must be exited immediately. */ - cpu_exit_tb_from_sighandler(cpu, old_set); + sigprocmask(SIG_SETMASK, old_set, NULL); + cpu_loop_exit_noexc(cpu); /* NORETURN */ default: g_assert_not_reached(); From ba0e73336200a04f797ae0c13922146a135cb118 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 11:08:09 -0700 Subject: [PATCH 0900/1334] configure: Merge riscv32 and riscv64 host architectures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing code for safe-syscall.inc.S will compile without change for riscv32 and riscv64. We may also drop the meson.build stanza that merges them for tcg/. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- configure | 8 ++------ linux-user/host/{riscv64 => riscv}/hostdep.h | 4 ++-- linux-user/host/{riscv64 => riscv}/safe-syscall.inc.S | 0 linux-user/host/riscv32/hostdep.h | 11 ----------- meson.build | 4 +--- 5 files changed, 5 insertions(+), 22 deletions(-) rename linux-user/host/{riscv64 => riscv}/hostdep.h (94%) rename linux-user/host/{riscv64 => riscv}/safe-syscall.inc.S (100%) delete mode 100644 linux-user/host/riscv32/hostdep.h diff --git a/configure b/configure index 039467c04b..d57ad58342 100755 --- a/configure +++ b/configure @@ -570,11 +570,7 @@ elif check_define __s390__ ; then cpu="s390" fi elif check_define __riscv ; then - if check_define _LP64 ; then - cpu="riscv64" - else - cpu="riscv32" - fi + cpu="riscv" elif check_define __arm__ ; then cpu="arm" elif check_define __aarch64__ ; then @@ -587,7 +583,7 @@ ARCH= # Normalise host CPU name and set ARCH. # Note that this case should only have supported host CPUs, not guests. case "$cpu" in - ppc|ppc64|s390x|sparc64|x32|riscv32|riscv64) + ppc|ppc64|s390x|sparc64|x32|riscv) ;; ppc64le) ARCH="ppc64" diff --git a/linux-user/host/riscv64/hostdep.h b/linux-user/host/riscv/hostdep.h similarity index 94% rename from linux-user/host/riscv64/hostdep.h rename to linux-user/host/riscv/hostdep.h index 865f0fb9ff..2ba07456ae 100644 --- a/linux-user/host/riscv64/hostdep.h +++ b/linux-user/host/riscv/hostdep.h @@ -5,8 +5,8 @@ * See the COPYING file in the top-level directory. */ -#ifndef RISCV64_HOSTDEP_H -#define RISCV64_HOSTDEP_H +#ifndef RISCV_HOSTDEP_H +#define RISCV_HOSTDEP_H /* We have a safe-syscall.inc.S */ #define HAVE_SAFE_SYSCALL diff --git a/linux-user/host/riscv64/safe-syscall.inc.S b/linux-user/host/riscv/safe-syscall.inc.S similarity index 100% rename from linux-user/host/riscv64/safe-syscall.inc.S rename to linux-user/host/riscv/safe-syscall.inc.S diff --git a/linux-user/host/riscv32/hostdep.h b/linux-user/host/riscv32/hostdep.h deleted file mode 100644 index adf9edbf2d..0000000000 --- a/linux-user/host/riscv32/hostdep.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * hostdep.h : things which are dependent on the host architecture - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef RISCV32_HOSTDEP_H -#define RISCV32_HOSTDEP_H - -#endif diff --git a/meson.build b/meson.build index 2c5b53cbe2..90e3e85f20 100644 --- a/meson.build +++ b/meson.build @@ -55,7 +55,7 @@ have_block = have_system or have_tools python = import('python').find_installation() supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux'] -supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64', +supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv', 'x86', 'x86_64', 'arm', 'aarch64', 'mips', 'mips64', 'sparc', 'sparc64'] cpu = host_machine.cpu_family() @@ -351,8 +351,6 @@ if not get_option('tcg').disabled() tcg_arch = 'i386' elif config_host['ARCH'] == 'ppc64' tcg_arch = 'ppc' - elif config_host['ARCH'] in ['riscv32', 'riscv64'] - tcg_arch = 'riscv' endif add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch, language: ['c', 'cpp', 'objc']) From e6037d04c58284285726721d0d4741e1386594e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 16 Sep 2021 14:44:17 -0700 Subject: [PATCH 0901/1334] linux-user: Reorg handling for SIGSEGV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add stub host-signal.h for all linux-user hosts. Add new code replacing cpu_signal_handler. Full migration will happen one host at a time. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Acked-by: Alistair Francis Signed-off-by: Richard Henderson --- linux-user/host/aarch64/host-signal.h | 1 + linux-user/host/arm/host-signal.h | 1 + linux-user/host/i386/host-signal.h | 1 + linux-user/host/mips/host-signal.h | 1 + linux-user/host/ppc/host-signal.h | 1 + linux-user/host/ppc64/host-signal.h | 1 + linux-user/host/riscv/host-signal.h | 1 + linux-user/host/s390/host-signal.h | 1 + linux-user/host/s390x/host-signal.h | 1 + linux-user/host/sparc/host-signal.h | 1 + linux-user/host/sparc64/host-signal.h | 1 + linux-user/host/x32/host-signal.h | 1 + linux-user/host/x86_64/host-signal.h | 1 + linux-user/signal.c | 109 ++++++++++++++++++++++---- 14 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 linux-user/host/aarch64/host-signal.h create mode 100644 linux-user/host/arm/host-signal.h create mode 100644 linux-user/host/i386/host-signal.h create mode 100644 linux-user/host/mips/host-signal.h create mode 100644 linux-user/host/ppc/host-signal.h create mode 100644 linux-user/host/ppc64/host-signal.h create mode 100644 linux-user/host/riscv/host-signal.h create mode 100644 linux-user/host/s390/host-signal.h create mode 100644 linux-user/host/s390x/host-signal.h create mode 100644 linux-user/host/sparc/host-signal.h create mode 100644 linux-user/host/sparc64/host-signal.h create mode 100644 linux-user/host/x32/host-signal.h create mode 100644 linux-user/host/x86_64/host-signal.h diff --git a/linux-user/host/aarch64/host-signal.h b/linux-user/host/aarch64/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/aarch64/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/arm/host-signal.h b/linux-user/host/arm/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/arm/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/i386/host-signal.h b/linux-user/host/i386/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/i386/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/mips/host-signal.h b/linux-user/host/mips/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/mips/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/ppc/host-signal.h b/linux-user/host/ppc/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/ppc/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/ppc64/host-signal.h b/linux-user/host/ppc64/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/ppc64/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/riscv/host-signal.h b/linux-user/host/riscv/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/riscv/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/s390/host-signal.h b/linux-user/host/s390/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/s390/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/s390x/host-signal.h b/linux-user/host/s390x/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/s390x/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/sparc/host-signal.h b/linux-user/host/sparc/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/sparc/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/sparc64/host-signal.h b/linux-user/host/sparc64/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/sparc64/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/x32/host-signal.h b/linux-user/host/x32/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/x32/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/host/x86_64/host-signal.h b/linux-user/host/x86_64/host-signal.h new file mode 100644 index 0000000000..f4b4d65031 --- /dev/null +++ b/linux-user/host/x86_64/host-signal.h @@ -0,0 +1 @@ +#define HOST_SIGNAL_PLACEHOLDER diff --git a/linux-user/signal.c b/linux-user/signal.c index 14d8fdfde1..6900acb122 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "exec/gdbstub.h" +#include "hw/core/tcg-cpu-ops.h" #include #include @@ -29,6 +30,7 @@ #include "loader.h" #include "trace.h" #include "signal-common.h" +#include "host-signal.h" static struct target_sigaction sigact_table[TARGET_NSIG]; @@ -769,41 +771,116 @@ static inline void rewind_if_in_safe_syscall(void *puc) } #endif -static void host_signal_handler(int host_signum, siginfo_t *info, - void *puc) +static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) { CPUArchState *env = thread_cpu->env_ptr; CPUState *cpu = env_cpu(env); TaskState *ts = cpu->opaque; - - int sig; target_siginfo_t tinfo; ucontext_t *uc = puc; struct emulated_sigtable *k; + int guest_sig; +#ifdef HOST_SIGNAL_PLACEHOLDER /* the CPU emulator uses some host signals to detect exceptions, we forward to it some signals */ - if ((host_signum == SIGSEGV || host_signum == SIGBUS) + if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) { - if (cpu_signal_handler(host_signum, info, puc)) + if (cpu_signal_handler(host_sig, info, puc)) { return; + } } +#else + uintptr_t pc = 0; + bool sync_sig = false; + + /* + * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special + * handling wrt signal blocking and unwinding. + */ + if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) { + MMUAccessType access_type; + uintptr_t host_addr; + abi_ptr guest_addr; + bool is_write; + + host_addr = (uintptr_t)info->si_addr; + + /* + * Convert forcefully to guest address space: addresses outside + * reserved_va are still valid to report via SEGV_MAPERR. + */ + guest_addr = h2g_nocheck(host_addr); + + pc = host_signal_pc(uc); + is_write = host_signal_write(info, uc); + access_type = adjust_signal_pc(&pc, is_write); + + if (host_sig == SIGSEGV) { + const struct TCGCPUOps *tcg_ops; + + if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) { + /* If this was a write to a TB protected page, restart. */ + if (is_write && + handle_sigsegv_accerr_write(cpu, &uc->uc_sigmask, + pc, guest_addr)) { + return; + } + + /* + * With reserved_va, the whole address space is PROT_NONE, + * which means that we may get ACCERR when we want MAPERR. + */ + if (page_get_flags(guest_addr) & PAGE_VALID) { + /* maperr = false; */ + } else { + info->si_code = SEGV_MAPERR; + } + } + + sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + + tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + tcg_ops->tlb_fill(cpu, guest_addr, 0, access_type, + MMU_USER_IDX, false, pc); + g_assert_not_reached(); + } else { + sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + } + + sync_sig = true; + } +#endif /* get target signal number */ - sig = host_to_target_signal(host_signum); - if (sig < 1 || sig > TARGET_NSIG) + guest_sig = host_to_target_signal(host_sig); + if (guest_sig < 1 || guest_sig > TARGET_NSIG) { return; - trace_user_host_signal(env, host_signum, sig); + } + trace_user_host_signal(env, host_sig, guest_sig); + + host_to_target_siginfo_noswap(&tinfo, info); + k = &ts->sigtab[guest_sig - 1]; + k->info = tinfo; + k->pending = guest_sig; + ts->signal_pending = 1; + +#ifndef HOST_SIGNAL_PLACEHOLDER + /* + * For synchronous signals, unwind the cpu state to the faulting + * insn and then exit back to the main loop so that the signal + * is delivered immediately. + */ + if (sync_sig) { + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, pc); + } +#endif rewind_if_in_safe_syscall(puc); - host_to_target_siginfo_noswap(&tinfo, info); - k = &ts->sigtab[sig - 1]; - k->info = tinfo; - k->pending = sig; - ts->signal_pending = 1; - - /* Block host signals until target signal handler entered. We + /* + * Block host signals until target signal handler entered. We * can't block SIGSEGV or SIGBUS while we're executing guest * code in case the guest code provokes one in the window between * now and it getting out to the main loop. Signals will be From 61848717d639446b58b069e480739a757d74813d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 9 Oct 2021 17:24:01 +0200 Subject: [PATCH 0902/1334] monitor: Trim some trailing space from human-readable output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I noticed -cpu help printing enough trailing spaces to make the output at least 84 characters wide. Looks ugly unless the terminal is wider. Ugly or not, trailing spaces are stupid. The culprit is this line in x86_cpu_list_entry(): qemu_printf("x86 %-20s %-58s\n", name, desc); This prints a string with minimum field left-justified right before a newline. Change it to qemu_printf("x86 %-20s %s\n", name, desc); which avoids the trailing spaces and is simpler to boot. A search for the pattern with "git-grep -E '%-[0-9]+s\\n'" found a few more instances. Change them similarly. Signed-off-by: Markus Armbruster Reviewed-by: Richard Henderson Acked-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé Acked-by: Max Filippov Message-Id: <20211009152401.2982862-1-armbru@redhat.com> Signed-off-by: Laurent Vivier --- monitor/hmp-cmds.c | 2 +- target/i386/cpu-dump.c | 4 ++-- target/i386/cpu.c | 2 +- target/ppc/cpu_init.c | 2 +- target/s390x/cpu_models.c | 4 ++-- target/xtensa/mmu_helper.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index bcaa41350e..9e45a138a5 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -1945,7 +1945,7 @@ void hmp_rocker_ports(Monitor *mon, const QDict *qdict) monitor_printf(mon, " port link duplex neg?\n"); for (port = list; port; port = port->next) { - monitor_printf(mon, "%10s %-4s %-3s %2s %-3s\n", + monitor_printf(mon, "%10s %-4s %-3s %2s %s\n", port->value->name, port->value->enabled ? port->value->link_up ? "up" : "down" : "!ena", diff --git a/target/i386/cpu-dump.c b/target/i386/cpu-dump.c index 02b635a52c..08ac957e99 100644 --- a/target/i386/cpu-dump.c +++ b/target/i386/cpu-dump.c @@ -464,13 +464,13 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags) snprintf(cc_op_name, sizeof(cc_op_name), "[%d]", env->cc_op); #ifdef TARGET_X86_64 if (env->hflags & HF_CS64_MASK) { - qemu_fprintf(f, "CCS=%016" PRIx64 " CCD=%016" PRIx64 " CCO=%-8s\n", + qemu_fprintf(f, "CCS=%016" PRIx64 " CCD=%016" PRIx64 " CCO=%s\n", env->cc_src, env->cc_dst, cc_op_name); } else #endif { - qemu_fprintf(f, "CCS=%08x CCD=%08x CCO=%-8s\n", + qemu_fprintf(f, "CCS=%08x CCD=%08x CCO=%s\n", (uint32_t)env->cc_src, (uint32_t)env->cc_dst, cc_op_name); } diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 598d451dcf..c5744ce08c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -4878,7 +4878,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = g_strdup_printf("%s", model_id); } - qemu_printf("x86 %-20s %-58s\n", name, desc); + qemu_printf("x86 %-20s %s\n", name, desc); } /* list available CPU models and flags */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 65545ba9ca..ba384a592b 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -8734,7 +8734,7 @@ void ppc_cpu_list(void) #ifdef CONFIG_KVM qemu_printf("\n"); - qemu_printf("PowerPC %-16s\n", "host"); + qemu_printf("PowerPC %s\n", "host"); #endif } diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 4e4598cc77..11e06cc51f 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -398,14 +398,14 @@ void s390_cpu_list(void) for (feat = 0; feat < S390_FEAT_MAX; feat++) { const S390FeatDef *def = s390_feat_def(feat); - qemu_printf("%-20s %-50s\n", def->name, def->desc); + qemu_printf("%-20s %s\n", def->name, def->desc); } qemu_printf("\nRecognized feature groups:\n"); for (group = 0; group < S390_FEAT_GROUP_MAX; group++) { const S390FeatGroupDef *def = s390_feat_group_def(group); - qemu_printf("%-20s %-50s\n", def->name, def->desc); + qemu_printf("%-20s %s\n", def->name, def->desc); } } diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index b01ff9399a..57e319a1af 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -1098,7 +1098,7 @@ static void dump_tlb(CPUXtensaState *env, bool dtlb) qemu_printf("\tVaddr Paddr ASID Attr RWX Cache\n" "\t---------- ---------- ---- ---- --- -------\n"); } - qemu_printf("\t0x%08x 0x%08x 0x%02x 0x%02x %c%c%c %-7s\n", + qemu_printf("\t0x%08x 0x%08x 0x%02x 0x%02x %c%c%c %s\n", entry->vaddr, entry->paddr, entry->asid, From 3a23a0c06171c97e7327dd75ae5503f2e38fe749 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Fri, 8 Oct 2021 15:50:40 +0800 Subject: [PATCH 0903/1334] hw/core/machine: Add the missing delimiter in cpu_slot_to_string() The expected output string from cpu_slot_to_string() ought to be like "socket-id: *, die-id: *, core-id: *, thread-id: *", so add the missing ", " before "die-id". This affects the readability of the error message. Fixes: 176d2cda0d ("i386/cpu: Consolidate die-id validity in smp context") Signed-off-by: Yanan Wang Reviewed-by: Laurent Vivier Message-Id: <20211008075040.18028-1-wangyanan55@huawei.com> Signed-off-by: Laurent Vivier --- hw/core/machine.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index b8d95eec32..0a23ae3106 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1157,6 +1157,9 @@ static char *cpu_slot_to_string(const CPUArchId *cpu) g_string_append_printf(s, "socket-id: %"PRId64, cpu->props.socket_id); } if (cpu->props.has_die_id) { + if (s->len) { + g_string_append_printf(s, ", "); + } g_string_append_printf(s, "die-id: %"PRId64, cpu->props.die_id); } if (cpu->props.has_core_id) { From 11f976adeeb7ff0799f972a48d02f66015f53416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Oct 2021 10:38:35 +0200 Subject: [PATCH 0904/1334] MAINTAINERS: Split HPPA TCG vs HPPA machines/hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hardware emulated models don't belong to the TCG MAINTAINERS section. Move them to the 'HP-PARISC Machines' section. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Helge Deller Message-Id: <20211004083835.3802961-1-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- MAINTAINERS | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 894dc43105..6f2b200780 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -205,10 +205,7 @@ HPPA (PA-RISC) TCG CPUs M: Richard Henderson S: Maintained F: target/hppa/ -F: hw/hppa/ F: disas/hppa.c -F: hw/net/*i82596* -F: include/hw/net/lasi_82596.h M68K TCG CPUs M: Laurent Vivier @@ -1099,6 +1096,8 @@ R: Helge Deller S: Odd Fixes F: configs/devices/hppa-softmmu/default.mak F: hw/hppa/ +F: hw/net/*i82596* +F: include/hw/net/lasi_82596.h F: pc-bios/hppa-firmware.img M68K Machines From 5d2bd73588d14b5868129ace9c7912a777f06753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Sep 2021 08:40:46 +0200 Subject: [PATCH 0905/1334] hw/input/lasips2: Fix typos in function names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Artist is another device, this one is the Lasi PS/2. Rename the functions accordingly. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Damien Hedde Message-Id: <20210920064048.2729397-2-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- hw/input/lasips2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/input/lasips2.c b/hw/input/lasips2.c index e7faf24058..68d741d342 100644 --- a/hw/input/lasips2.c +++ b/hw/input/lasips2.c @@ -96,7 +96,7 @@ typedef enum { LASIPS2_STATUS_CLKSHD = 0x80, } lasips2_status_reg_t; -static const char *artist_read_reg_name(uint64_t addr) +static const char *lasips2_read_reg_name(uint64_t addr) { switch (addr & 0xc) { case REG_PS2_ID: @@ -116,7 +116,7 @@ static const char *artist_read_reg_name(uint64_t addr) } } -static const char *artist_write_reg_name(uint64_t addr) +static const char *lasips2_write_reg_name(uint64_t addr) { switch (addr & 0x0c) { case REG_PS2_RESET: @@ -145,7 +145,7 @@ static void lasips2_reg_write(void *opaque, hwaddr addr, uint64_t val, LASIPS2Port *port = opaque; trace_lasips2_reg_write(size, port->id, addr, - artist_write_reg_name(addr), val); + lasips2_write_reg_name(addr), val); switch (addr & 0xc) { case REG_PS2_CONTROL: @@ -239,7 +239,7 @@ static uint64_t lasips2_reg_read(void *opaque, hwaddr addr, unsigned size) break; } trace_lasips2_reg_read(size, port->id, addr, - artist_read_reg_name(addr), ret); + lasips2_read_reg_name(addr), ret); return ret; } From b390afd8c50b18b3d6c7ed76e8e066dc64b8e206 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 29 Oct 2021 10:14:47 +0800 Subject: [PATCH 0906/1334] migration/rdma: Fix out of order wrid destination: ../qemu/build/qemu-system-x86_64 -enable-kvm -netdev tap,id=hn0,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown -device e1000,netdev=hn0,mac=50:52:54:00:11:22 -boot c -drive if=none,file=./Fedora-rdma-server-migration.qcow2,id=drive-virtio-disk0 -device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0 -m 2048 -smp 2 -device piix3-usb-uhci -device usb-tablet -monitor stdio -vga qxl -spice streaming-video=filter,port=5902,disable-ticketing -incoming rdma:192.168.22.23:8888 qemu-system-x86_64: -spice streaming-video=filter,port=5902,disable-ticketing: warning: short-form boolean option 'disable-ticketing' deprecated Please use disable-ticketing=on instead QEMU 6.0.50 monitor - type 'help' for more information (qemu) trace-event qemu_rdma_block_for_wrid_miss on (qemu) dest_init RDMA Device opened: kernel name rxe_eth0 uverbs device name uverbs2, infiniband_verbs class device path /sys/class/infiniband_verbs/uverbs2, infiniband class device path /sys/class/infiniband/rxe_eth0, transport: (2) Ethernet qemu_rdma_block_for_wrid_miss A Wanted wrid CONTROL SEND (2000) but got CONTROL RECV (4000) source: ../qemu/build/qemu-system-x86_64 -enable-kvm -netdev tap,id=hn0,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown -device e1000,netdev=hn0,mac=50:52:54:00:11:22 -boot c -drive if=none,file=./Fedora-rdma-server.qcow2,id=drive-virtio-disk0 -device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0 -m 2048 -smp 2 -device piix3-usb-uhci -device usb-tablet -monitor stdio -vga qxl -spice streaming-video=filter,port=5901,disable-ticketing -S qemu-system-x86_64: -spice streaming-video=filter,port=5901,disable-ticketing: warning: short-form boolean option 'disable-ticketing' deprecated Please use disable-ticketing=on instead QEMU 6.0.50 monitor - type 'help' for more information (qemu) (qemu) trace-event qemu_rdma_block_for_wrid_miss on (qemu) migrate -d rdma:192.168.22.23:8888 source_resolve_host RDMA Device opened: kernel name rxe_eth0 uverbs device name uverbs2, infiniband_verbs class device path /sys/class/infiniband_verbs/uverbs2, infiniband class device path /sys/class/infiniband/rxe_eth0, transport: (2) Ethernet (qemu) qemu_rdma_block_for_wrid_miss A Wanted wrid WRITE RDMA (1) but got CONTROL RECV (4000) NOTE: we use soft RoCE as the rdma device. [root@iaas-rpma images]# rdma link show rxe_eth0/1 link rxe_eth0/1 state ACTIVE physical_state LINK_UP netdev eth0 This migration could not be completed when out of order(OOO) CQ event occurs. The send queue and receive queue shared a same completion queue, and qemu_rdma_block_for_wrid() will drop the CQs it's not interested in. But the dropped CQs by qemu_rdma_block_for_wrid() could be later CQs it wants. So in this case, qemu_rdma_block_for_wrid() will block forever. OOO cases will occur in both source side and destination side. And a forever blocking happens on only SEND and RECV are out of order. OOO between 'WRITE RDMA' and 'RECV' doesn't matter. below the OOO sequence: source destination rdma_write_one() qemu_rdma_registration_handle() 1. S1: post_recv X D1: post_recv Y 2. wait for recv CQ event X 3. D2: post_send X ---------------+ 4. wait for send CQ send event X (D2) | 5. recv CQ event X reaches (D2) | 6. +-S2: post_send Y | 7. | wait for send CQ event Y | 8. | recv CQ event Y (S2) (drop it) | 9. +-send CQ event Y reaches (S2) | 10. send CQ event X reaches (D2) -----+ 11. wait recv CQ event Y (dropped by (8)) Although a hardware IB works fine in my a hundred of runs, the IB specification doesn't guaratee the CQ order in such case. Here we introduce a independent send completion queue to distinguish ibv_post_send completion queue from the original mixed completion queue. It helps us to poll the specific CQE we are really interested in. Signed-off-by: Li Zhijian Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/rdma.c | 138 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 101 insertions(+), 37 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index 2a3c7889b9..f5d3bbe7e9 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -358,9 +358,11 @@ typedef struct RDMAContext { struct ibv_context *verbs; struct rdma_event_channel *channel; struct ibv_qp *qp; /* queue pair */ - struct ibv_comp_channel *comp_channel; /* completion channel */ + struct ibv_comp_channel *recv_comp_channel; /* recv completion channel */ + struct ibv_comp_channel *send_comp_channel; /* send completion channel */ struct ibv_pd *pd; /* protection domain */ - struct ibv_cq *cq; /* completion queue */ + struct ibv_cq *recv_cq; /* recvieve completion queue */ + struct ibv_cq *send_cq; /* send completion queue */ /* * If a previous write failed (perhaps because of a failed @@ -1059,21 +1061,34 @@ static int qemu_rdma_alloc_pd_cq(RDMAContext *rdma) return -1; } - /* create completion channel */ - rdma->comp_channel = ibv_create_comp_channel(rdma->verbs); - if (!rdma->comp_channel) { - error_report("failed to allocate completion channel"); + /* create receive completion channel */ + rdma->recv_comp_channel = ibv_create_comp_channel(rdma->verbs); + if (!rdma->recv_comp_channel) { + error_report("failed to allocate receive completion channel"); goto err_alloc_pd_cq; } /* - * Completion queue can be filled by both read and write work requests, - * so must reflect the sum of both possible queue sizes. + * Completion queue can be filled by read work requests. */ - rdma->cq = ibv_create_cq(rdma->verbs, (RDMA_SIGNALED_SEND_MAX * 3), - NULL, rdma->comp_channel, 0); - if (!rdma->cq) { - error_report("failed to allocate completion queue"); + rdma->recv_cq = ibv_create_cq(rdma->verbs, (RDMA_SIGNALED_SEND_MAX * 3), + NULL, rdma->recv_comp_channel, 0); + if (!rdma->recv_cq) { + error_report("failed to allocate receive completion queue"); + goto err_alloc_pd_cq; + } + + /* create send completion channel */ + rdma->send_comp_channel = ibv_create_comp_channel(rdma->verbs); + if (!rdma->send_comp_channel) { + error_report("failed to allocate send completion channel"); + goto err_alloc_pd_cq; + } + + rdma->send_cq = ibv_create_cq(rdma->verbs, (RDMA_SIGNALED_SEND_MAX * 3), + NULL, rdma->send_comp_channel, 0); + if (!rdma->send_cq) { + error_report("failed to allocate send completion queue"); goto err_alloc_pd_cq; } @@ -1083,11 +1098,19 @@ err_alloc_pd_cq: if (rdma->pd) { ibv_dealloc_pd(rdma->pd); } - if (rdma->comp_channel) { - ibv_destroy_comp_channel(rdma->comp_channel); + if (rdma->recv_comp_channel) { + ibv_destroy_comp_channel(rdma->recv_comp_channel); + } + if (rdma->send_comp_channel) { + ibv_destroy_comp_channel(rdma->send_comp_channel); + } + if (rdma->recv_cq) { + ibv_destroy_cq(rdma->recv_cq); + rdma->recv_cq = NULL; } rdma->pd = NULL; - rdma->comp_channel = NULL; + rdma->recv_comp_channel = NULL; + rdma->send_comp_channel = NULL; return -1; } @@ -1104,8 +1127,8 @@ static int qemu_rdma_alloc_qp(RDMAContext *rdma) attr.cap.max_recv_wr = 3; attr.cap.max_send_sge = 1; attr.cap.max_recv_sge = 1; - attr.send_cq = rdma->cq; - attr.recv_cq = rdma->cq; + attr.send_cq = rdma->send_cq; + attr.recv_cq = rdma->recv_cq; attr.qp_type = IBV_QPT_RC; ret = rdma_create_qp(rdma->cm_id, rdma->pd, &attr); @@ -1496,14 +1519,14 @@ static void qemu_rdma_signal_unregister(RDMAContext *rdma, uint64_t index, * (of any kind) has completed. * Return the work request ID that completed. */ -static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out, - uint32_t *byte_len) +static uint64_t qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, + uint64_t *wr_id_out, uint32_t *byte_len) { int ret; struct ibv_wc wc; uint64_t wr_id; - ret = ibv_poll_cq(rdma->cq, 1, &wc); + ret = ibv_poll_cq(cq, 1, &wc); if (!ret) { *wr_id_out = RDMA_WRID_NONE; @@ -1575,7 +1598,8 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out, /* Wait for activity on the completion channel. * Returns 0 on success, none-0 on error. */ -static int qemu_rdma_wait_comp_channel(RDMAContext *rdma) +static int qemu_rdma_wait_comp_channel(RDMAContext *rdma, + struct ibv_comp_channel *comp_channel) { struct rdma_cm_event *cm_event; int ret = -1; @@ -1586,7 +1610,7 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma) */ if (rdma->migration_started_on_destination && migration_incoming_get_current()->state == MIGRATION_STATUS_ACTIVE) { - yield_until_fd_readable(rdma->comp_channel->fd); + yield_until_fd_readable(comp_channel->fd); } else { /* This is the source side, we're in a separate thread * or destination prior to migration_fd_process_incoming() @@ -1597,7 +1621,7 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma) */ while (!rdma->error_state && !rdma->received_error) { GPollFD pfds[2]; - pfds[0].fd = rdma->comp_channel->fd; + pfds[0].fd = comp_channel->fd; pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR; pfds[0].revents = 0; @@ -1655,6 +1679,17 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma) return rdma->error_state; } +static struct ibv_comp_channel *to_channel(RDMAContext *rdma, int wrid) +{ + return wrid < RDMA_WRID_RECV_CONTROL ? rdma->send_comp_channel : + rdma->recv_comp_channel; +} + +static struct ibv_cq *to_cq(RDMAContext *rdma, int wrid) +{ + return wrid < RDMA_WRID_RECV_CONTROL ? rdma->send_cq : rdma->recv_cq; +} + /* * Block until the next work request has completed. * @@ -1675,13 +1710,15 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, struct ibv_cq *cq; void *cq_ctx; uint64_t wr_id = RDMA_WRID_NONE, wr_id_in; + struct ibv_comp_channel *ch = to_channel(rdma, wrid_requested); + struct ibv_cq *poll_cq = to_cq(rdma, wrid_requested); - if (ibv_req_notify_cq(rdma->cq, 0)) { + if (ibv_req_notify_cq(poll_cq, 0)) { return -1; } /* poll cq first */ while (wr_id != wrid_requested) { - ret = qemu_rdma_poll(rdma, &wr_id_in, byte_len); + ret = qemu_rdma_poll(rdma, poll_cq, &wr_id_in, byte_len); if (ret < 0) { return ret; } @@ -1702,12 +1739,12 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, } while (1) { - ret = qemu_rdma_wait_comp_channel(rdma); + ret = qemu_rdma_wait_comp_channel(rdma, ch); if (ret) { goto err_block_for_wrid; } - ret = ibv_get_cq_event(rdma->comp_channel, &cq, &cq_ctx); + ret = ibv_get_cq_event(ch, &cq, &cq_ctx); if (ret) { perror("ibv_get_cq_event"); goto err_block_for_wrid; @@ -1721,7 +1758,7 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, } while (wr_id != wrid_requested) { - ret = qemu_rdma_poll(rdma, &wr_id_in, byte_len); + ret = qemu_rdma_poll(rdma, poll_cq, &wr_id_in, byte_len); if (ret < 0) { goto err_block_for_wrid; } @@ -2437,13 +2474,21 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma_destroy_qp(rdma->cm_id); rdma->qp = NULL; } - if (rdma->cq) { - ibv_destroy_cq(rdma->cq); - rdma->cq = NULL; + if (rdma->recv_cq) { + ibv_destroy_cq(rdma->recv_cq); + rdma->recv_cq = NULL; } - if (rdma->comp_channel) { - ibv_destroy_comp_channel(rdma->comp_channel); - rdma->comp_channel = NULL; + if (rdma->send_cq) { + ibv_destroy_cq(rdma->send_cq); + rdma->send_cq = NULL; + } + if (rdma->recv_comp_channel) { + ibv_destroy_comp_channel(rdma->recv_comp_channel); + rdma->recv_comp_channel = NULL; + } + if (rdma->send_comp_channel) { + ibv_destroy_comp_channel(rdma->send_comp_channel); + rdma->send_comp_channel = NULL; } if (rdma->pd) { ibv_dealloc_pd(rdma->pd); @@ -3115,10 +3160,14 @@ static void qio_channel_rdma_set_aio_fd_handler(QIOChannel *ioc, { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); if (io_read) { - aio_set_fd_handler(ctx, rioc->rdmain->comp_channel->fd, + aio_set_fd_handler(ctx, rioc->rdmain->recv_comp_channel->fd, + false, io_read, io_write, NULL, opaque); + aio_set_fd_handler(ctx, rioc->rdmain->send_comp_channel->fd, false, io_read, io_write, NULL, opaque); } else { - aio_set_fd_handler(ctx, rioc->rdmaout->comp_channel->fd, + aio_set_fd_handler(ctx, rioc->rdmaout->recv_comp_channel->fd, + false, io_read, io_write, NULL, opaque); + aio_set_fd_handler(ctx, rioc->rdmaout->send_comp_channel->fd, false, io_read, io_write, NULL, opaque); } } @@ -3332,7 +3381,22 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, */ while (1) { uint64_t wr_id, wr_id_in; - int ret = qemu_rdma_poll(rdma, &wr_id_in, NULL); + int ret = qemu_rdma_poll(rdma, rdma->recv_cq, &wr_id_in, NULL); + if (ret < 0) { + error_report("rdma migration: polling error! %d", ret); + goto err; + } + + wr_id = wr_id_in & RDMA_WRID_TYPE_MASK; + + if (wr_id == RDMA_WRID_NONE) { + break; + } + } + + while (1) { + uint64_t wr_id, wr_id_in; + int ret = qemu_rdma_poll(rdma, rdma->send_cq, &wr_id_in, NULL); if (ret < 0) { error_report("rdma migration: polling error! %d", ret); goto err; From 95a556e7c7ddeb8e9e370dd074f48ab77915aa82 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:04 -0400 Subject: [PATCH 0907/1334] iotests/297: Move pylint config into pylintrc Move --score=n and --notes=XXX,FIXME into pylintrc. This pulls configuration out of code, which I think is probably a good thing in general. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-2-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 4 +--- tests/qemu-iotests/pylintrc | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index 91ec34d952..bc3a0ceb2a 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -65,10 +65,8 @@ def run_linters(): print('=== pylint ===') sys.stdout.flush() - # Todo notes are fine, but fixme's or xxx's should probably just be - # fixed (in tests, at least) env = os.environ.copy() - subprocess.run(('pylint-3', '--score=n', '--notes=FIXME,XXX', *files), + subprocess.run(('pylint-3', *files), env=env, check=False) print('=== mypy ===') diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index 8cb4e1d6a6..32ab77b8bb 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -31,6 +31,22 @@ disable=invalid-name, too-many-statements, consider-using-f-string, + +[REPORTS] + +# Activate the evaluation score. +score=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +# TODO notes are fine, but FIXMEs or XXXs should probably just be +# fixed (in tests, at least). +notes=FIXME, + XXX, + + [FORMAT] # Maximum number of characters on a single line. From 8f7960fa3120806e0933795c6f34d8b5b09c622b Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:05 -0400 Subject: [PATCH 0908/1334] iotests/297: Split mypy configuration out into mypy.ini More separation of code and configuration. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-3-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 14 +------------- tests/qemu-iotests/mypy.ini | 12 ++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 tests/qemu-iotests/mypy.ini diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index bc3a0ceb2a..b8101e6024 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -73,19 +73,7 @@ def run_linters(): sys.stdout.flush() env['MYPYPATH'] = env['PYTHONPATH'] - p = subprocess.run(('mypy', - '--warn-unused-configs', - '--disallow-subclassing-any', - '--disallow-any-generics', - '--disallow-incomplete-defs', - '--disallow-untyped-decorators', - '--no-implicit-optional', - '--warn-redundant-casts', - '--warn-unused-ignores', - '--no-implicit-reexport', - '--namespace-packages', - '--scripts-are-modules', - *files), + p = subprocess.run(('mypy', *files), env=env, check=False, stdout=subprocess.PIPE, diff --git a/tests/qemu-iotests/mypy.ini b/tests/qemu-iotests/mypy.ini new file mode 100644 index 0000000000..4c0339f558 --- /dev/null +++ b/tests/qemu-iotests/mypy.ini @@ -0,0 +1,12 @@ +[mypy] +disallow_any_generics = True +disallow_incomplete_defs = True +disallow_subclassing_any = True +disallow_untyped_decorators = True +implicit_reexport = False +namespace_packages = True +no_implicit_optional = True +scripts_are_modules = True +warn_redundant_casts = True +warn_unused_configs = True +warn_unused_ignores = True From 3c1d5012e8d46cd2e4084e9f995d4444b6c09076 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:06 -0400 Subject: [PATCH 0909/1334] iotests/297: Add get_files() function Split out file discovery into its own method to begin separating out configuration/setup and test execution. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-4-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index b8101e6024..15b54594c1 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -21,6 +21,7 @@ import re import shutil import subprocess import sys +from typing import List import iotests @@ -54,10 +55,14 @@ def is_python_file(filename): return False -def run_linters(): +def get_test_files() -> List[str]: named_tests = [f'tests/{entry}' for entry in os.listdir('tests')] check_tests = set(os.listdir('.') + named_tests) - set(SKIP_FILES) - files = [filename for filename in check_tests if is_python_file(filename)] + return list(filter(is_python_file, check_tests)) + + +def run_linters(): + files = get_test_files() iotests.logger.debug('Files to be checked:') iotests.logger.debug(', '.join(sorted(files))) From 447aebda3f461de159280c4896a537395aac8e90 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:07 -0400 Subject: [PATCH 0910/1334] iotests/297: Create main() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of running "run_linters" directly, create a main() function that will be responsible for environment setup, leaving run_linters() responsible only for execution of the linters. (That environment setup will be moved over in forthcoming commits.) Signed-off-by: John Snow Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-5-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index 15b54594c1..163ebc8ebf 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -89,8 +89,12 @@ def run_linters(): print(p.stdout) -for linter in ('pylint-3', 'mypy'): - if shutil.which(linter) is None: - iotests.notrun(f'{linter} not found') +def main() -> None: + for linter in ('pylint-3', 'mypy'): + if shutil.which(linter) is None: + iotests.notrun(f'{linter} not found') -iotests.script_main(run_linters) + run_linters() + + +iotests.script_main(main) From f1be6219c550aeba1f91ac33b8236296e5b34851 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:08 -0400 Subject: [PATCH 0911/1334] iotests/297: Don't rely on distro-specific linter binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'pylint-3' is another Fedora-ism. Use "python3 -m pylint" or "python3 -m mypy" to access these scripts instead. This style of invocation will prefer the "correct" tool when run in a virtual environment. Note that we still check for "pylint-3" before the test begins -- this check is now "overly strict", but shouldn't cause anything that was already running correctly to start failing. This is addressed by a commit later in this series; 'iotests/297: update tool availability checks'. Signed-off-by: John Snow Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-6-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index 163ebc8ebf..c1bddb9ce0 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -71,14 +71,14 @@ def run_linters(): sys.stdout.flush() env = os.environ.copy() - subprocess.run(('pylint-3', *files), + subprocess.run(('python3', '-m', 'pylint', *files), env=env, check=False) print('=== mypy ===') sys.stdout.flush() env['MYPYPATH'] = env['PYTHONPATH'] - p = subprocess.run(('mypy', *files), + p = subprocess.run(('python3', '-m', 'mypy', *files), env=env, check=False, stdout=subprocess.PIPE, From 2d804f55b4f6b4f500ad99567c60631ac47fd860 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:09 -0400 Subject: [PATCH 0912/1334] iotests/297: Split run_linters apart into run_pylint and run_mypy Move environment setup into main(), and split the actual linter execution into run_pylint and run_mypy, respectively. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-7-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index c1bddb9ce0..189bcaf5f9 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -21,7 +21,7 @@ import re import shutil import subprocess import sys -from typing import List +from typing import List, Mapping, Optional import iotests @@ -61,23 +61,19 @@ def get_test_files() -> List[str]: return list(filter(is_python_file, check_tests)) -def run_linters(): - files = get_test_files() +def run_pylint( + files: List[str], + env: Optional[Mapping[str, str]] = None, +) -> None: - iotests.logger.debug('Files to be checked:') - iotests.logger.debug(', '.join(sorted(files))) - - print('=== pylint ===') - sys.stdout.flush() - - env = os.environ.copy() subprocess.run(('python3', '-m', 'pylint', *files), env=env, check=False) - print('=== mypy ===') - sys.stdout.flush() - env['MYPYPATH'] = env['PYTHONPATH'] +def run_mypy( + files: List[str], + env: Optional[Mapping[str, str]] = None, +) -> None: p = subprocess.run(('python3', '-m', 'mypy', *files), env=env, check=False, @@ -94,7 +90,21 @@ def main() -> None: if shutil.which(linter) is None: iotests.notrun(f'{linter} not found') - run_linters() + files = get_test_files() + + iotests.logger.debug('Files to be checked:') + iotests.logger.debug(', '.join(sorted(files))) + + env = os.environ.copy() + env['MYPYPATH'] = env['PYTHONPATH'] + + print('=== pylint ===') + sys.stdout.flush() + run_pylint(files, env=env) + + print('=== mypy ===') + sys.stdout.flush() + run_mypy(files, env=env) iotests.script_main(main) From a4bde736295bd0951005d7292b99086825f74f8a Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:10 -0400 Subject: [PATCH 0913/1334] iotests/297: refactor run_[mypy|pylint] as generic execution shim There's virtually nothing special here anymore; we can combine these into a single, rather generic function. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-8-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index 189bcaf5f9..d21673a292 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -61,27 +61,29 @@ def get_test_files() -> List[str]: return list(filter(is_python_file, check_tests)) -def run_pylint( - files: List[str], - env: Optional[Mapping[str, str]] = None, +def run_linter( + tool: str, + args: List[str], + env: Optional[Mapping[str, str]] = None, + suppress_output: bool = False, ) -> None: + """ + Run a python-based linting tool. - subprocess.run(('python3', '-m', 'pylint', *files), - env=env, check=False) + If suppress_output is True, capture stdout/stderr of the child + process and only print that information back to stdout if the child + process's return code was non-zero. + """ + p = subprocess.run( + ('python3', '-m', tool, *args), + env=env, + check=False, + stdout=subprocess.PIPE if suppress_output else None, + stderr=subprocess.STDOUT if suppress_output else None, + universal_newlines=True, + ) - -def run_mypy( - files: List[str], - env: Optional[Mapping[str, str]] = None, -) -> None: - p = subprocess.run(('python3', '-m', 'mypy', *files), - env=env, - check=False, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - - if p.returncode != 0: + if suppress_output and p.returncode != 0: print(p.stdout) @@ -100,11 +102,11 @@ def main() -> None: print('=== pylint ===') sys.stdout.flush() - run_pylint(files, env=env) + run_linter('pylint', files, env=env) print('=== mypy ===') sys.stdout.flush() - run_mypy(files, env=env) + run_linter('mypy', files, env=env, suppress_output=True) iotests.script_main(main) From 752f425d832d1d4e00f39e80835dd249fb44e698 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:11 -0400 Subject: [PATCH 0914/1334] iotests/297: Change run_linter() to raise an exception on failure Instead of using a process return code as the python function return value (or just not returning anything at all), allow run_linter() to raise an exception instead. The responsibility for printing output on error shifts from the function itself to the caller, who will know best how to present/format that information. (Also, "suppress_output" is now a lot more accurate of a parameter name.) Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-9-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index d21673a292..76d6a23f53 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -70,22 +70,18 @@ def run_linter( """ Run a python-based linting tool. - If suppress_output is True, capture stdout/stderr of the child - process and only print that information back to stdout if the child - process's return code was non-zero. + :param suppress_output: If True, suppress all stdout/stderr output. + :raise CalledProcessError: If the linter process exits with failure. """ - p = subprocess.run( + subprocess.run( ('python3', '-m', tool, *args), env=env, - check=False, + check=True, stdout=subprocess.PIPE if suppress_output else None, stderr=subprocess.STDOUT if suppress_output else None, universal_newlines=True, ) - if suppress_output and p.returncode != 0: - print(p.stdout) - def main() -> None: for linter in ('pylint-3', 'mypy'): @@ -102,11 +98,19 @@ def main() -> None: print('=== pylint ===') sys.stdout.flush() - run_linter('pylint', files, env=env) + try: + run_linter('pylint', files, env=env) + except subprocess.CalledProcessError: + # pylint failure will be caught by diffing the IO. + pass print('=== mypy ===') sys.stdout.flush() - run_linter('mypy', files, env=env, suppress_output=True) + try: + run_linter('mypy', files, env=env, suppress_output=True) + except subprocess.CalledProcessError as exc: + if exc.output: + print(exc.output) iotests.script_main(main) From 7a90bcc269bb6f727cfd69b50128ecf4ab92e9af Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:12 -0400 Subject: [PATCH 0915/1334] iotests/297: update tool availability checks As mentioned in 'iotests/297: Don't rely on distro-specific linter binaries', these checks are overly strict. Update them to be in-line with how we actually invoke the linters themselves. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-10-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index 76d6a23f53..b2ad8d1cbe 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -18,7 +18,6 @@ import os import re -import shutil import subprocess import sys from typing import List, Mapping, Optional @@ -84,9 +83,11 @@ def run_linter( def main() -> None: - for linter in ('pylint-3', 'mypy'): - if shutil.which(linter) is None: - iotests.notrun(f'{linter} not found') + for linter in ('pylint', 'mypy'): + try: + run_linter(linter, ['--version'], suppress_output=True) + except subprocess.CalledProcessError: + iotests.notrun(f"'{linter}' not found") files = get_test_files() From 85cfec53d0b43182a14f8b5c3aa685955a852f1c Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:13 -0400 Subject: [PATCH 0916/1334] iotests/297: split test into sub-cases Take iotest 297's main() test function and split it into two sub-cases that can be skipped individually. We can also drop custom environment setup from the pylint test as it isn't needed. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-11-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 63 ++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index b2ad8d1cbe..b7d9d6077b 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -82,36 +82,51 @@ def run_linter( ) -def main() -> None: - for linter in ('pylint', 'mypy'): - try: - run_linter(linter, ['--version'], suppress_output=True) - except subprocess.CalledProcessError: - iotests.notrun(f"'{linter}' not found") +def check_linter(linter: str) -> bool: + try: + run_linter(linter, ['--version'], suppress_output=True) + except subprocess.CalledProcessError: + iotests.case_notrun(f"'{linter}' not found") + return False + return True + +def test_pylint(files: List[str]) -> None: + print('=== pylint ===') + sys.stdout.flush() + + if not check_linter('pylint'): + return + + run_linter('pylint', files) + + +def test_mypy(files: List[str]) -> None: + print('=== mypy ===') + sys.stdout.flush() + + if not check_linter('mypy'): + return + + env = os.environ.copy() + env['MYPYPATH'] = env['PYTHONPATH'] + + run_linter('mypy', files, env=env, suppress_output=True) + + +def main() -> None: files = get_test_files() iotests.logger.debug('Files to be checked:') iotests.logger.debug(', '.join(sorted(files))) - env = os.environ.copy() - env['MYPYPATH'] = env['PYTHONPATH'] - - print('=== pylint ===') - sys.stdout.flush() - try: - run_linter('pylint', files, env=env) - except subprocess.CalledProcessError: - # pylint failure will be caught by diffing the IO. - pass - - print('=== mypy ===') - sys.stdout.flush() - try: - run_linter('mypy', files, env=env, suppress_output=True) - except subprocess.CalledProcessError as exc: - if exc.output: - print(exc.output) + for test in (test_pylint, test_mypy): + try: + test(files) + except subprocess.CalledProcessError as exc: + # Linter failure will be caught by diffing the IO. + if exc.output: + print(exc.output) iotests.script_main(main) From c293ba55c52ce07e03919e61e4a17d2cc2c944d4 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:14 -0400 Subject: [PATCH 0917/1334] iotests: split linters.py out from 297 Now, 297 is just the iotests-specific incantations and linters.py is as minimal as I can think to make it. The only remaining element in here that ought to be configuration and not code is the list of skip files, but they're still numerous enough that repeating them for mypy and pylint configurations both would be ... a hassle. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-12-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/297 | 72 +++++---------------------------- tests/qemu-iotests/linters.py | 76 +++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 61 deletions(-) create mode 100644 tests/qemu-iotests/linters.py diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297 index b7d9d6077b..ee78a62735 100755 --- a/tests/qemu-iotests/297 +++ b/tests/qemu-iotests/297 @@ -17,74 +17,24 @@ # along with this program. If not, see . import os -import re import subprocess import sys -from typing import List, Mapping, Optional +from typing import List import iotests +import linters -# TODO: Empty this list! -SKIP_FILES = ( - '030', '040', '041', '044', '045', '055', '056', '057', '065', '093', - '096', '118', '124', '132', '136', '139', '147', '148', '149', - '151', '152', '155', '163', '165', '194', '196', '202', - '203', '205', '206', '207', '208', '210', '211', '212', '213', '216', - '218', '219', '224', '228', '234', '235', '236', '237', '238', - '240', '242', '245', '246', '248', '255', '256', '257', '258', '260', - '262', '264', '266', '274', '277', '280', '281', '295', '296', '298', - '299', '302', '303', '304', '307', - 'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py' -) - - -def is_python_file(filename): - if not os.path.isfile(filename): - return False - - if filename.endswith('.py'): - return True - - with open(filename, encoding='utf-8') as f: - try: - first_line = f.readline() - return re.match('^#!.*python', first_line) is not None - except UnicodeDecodeError: # Ignore binary files - return False - - -def get_test_files() -> List[str]: - named_tests = [f'tests/{entry}' for entry in os.listdir('tests')] - check_tests = set(os.listdir('.') + named_tests) - set(SKIP_FILES) - return list(filter(is_python_file, check_tests)) - - -def run_linter( - tool: str, - args: List[str], - env: Optional[Mapping[str, str]] = None, - suppress_output: bool = False, -) -> None: - """ - Run a python-based linting tool. - - :param suppress_output: If True, suppress all stdout/stderr output. - :raise CalledProcessError: If the linter process exits with failure. - """ - subprocess.run( - ('python3', '-m', tool, *args), - env=env, - check=True, - stdout=subprocess.PIPE if suppress_output else None, - stderr=subprocess.STDOUT if suppress_output else None, - universal_newlines=True, - ) +# Looking for something? +# +# List of files to exclude from linting: linters.py +# mypy configuration: mypy.ini +# pylint configuration: pylintrc def check_linter(linter: str) -> bool: try: - run_linter(linter, ['--version'], suppress_output=True) + linters.run_linter(linter, ['--version'], suppress_output=True) except subprocess.CalledProcessError: iotests.case_notrun(f"'{linter}' not found") return False @@ -98,7 +48,7 @@ def test_pylint(files: List[str]) -> None: if not check_linter('pylint'): return - run_linter('pylint', files) + linters.run_linter('pylint', files) def test_mypy(files: List[str]) -> None: @@ -111,11 +61,11 @@ def test_mypy(files: List[str]) -> None: env = os.environ.copy() env['MYPYPATH'] = env['PYTHONPATH'] - run_linter('mypy', files, env=env, suppress_output=True) + linters.run_linter('mypy', files, env=env, suppress_output=True) def main() -> None: - files = get_test_files() + files = linters.get_test_files() iotests.logger.debug('Files to be checked:') iotests.logger.debug(', '.join(sorted(files))) diff --git a/tests/qemu-iotests/linters.py b/tests/qemu-iotests/linters.py new file mode 100644 index 0000000000..c515c7afe3 --- /dev/null +++ b/tests/qemu-iotests/linters.py @@ -0,0 +1,76 @@ +# Copyright (C) 2020 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import re +import subprocess +from typing import List, Mapping, Optional + + +# TODO: Empty this list! +SKIP_FILES = ( + '030', '040', '041', '044', '045', '055', '056', '057', '065', '093', + '096', '118', '124', '132', '136', '139', '147', '148', '149', + '151', '152', '155', '163', '165', '194', '196', '202', + '203', '205', '206', '207', '208', '210', '211', '212', '213', '216', + '218', '219', '224', '228', '234', '235', '236', '237', '238', + '240', '242', '245', '246', '248', '255', '256', '257', '258', '260', + '262', '264', '266', '274', '277', '280', '281', '295', '296', '298', + '299', '302', '303', '304', '307', + 'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py' +) + + +def is_python_file(filename): + if not os.path.isfile(filename): + return False + + if filename.endswith('.py'): + return True + + with open(filename, encoding='utf-8') as f: + try: + first_line = f.readline() + return re.match('^#!.*python', first_line) is not None + except UnicodeDecodeError: # Ignore binary files + return False + + +def get_test_files() -> List[str]: + named_tests = [f'tests/{entry}' for entry in os.listdir('tests')] + check_tests = set(os.listdir('.') + named_tests) - set(SKIP_FILES) + return list(filter(is_python_file, check_tests)) + + +def run_linter( + tool: str, + args: List[str], + env: Optional[Mapping[str, str]] = None, + suppress_output: bool = False, +) -> None: + """ + Run a python-based linting tool. + + :param suppress_output: If True, suppress all stdout/stderr output. + :raise CalledProcessError: If the linter process exits with failure. + """ + subprocess.run( + ('python3', '-m', tool, *args), + env=env, + check=True, + stdout=subprocess.PIPE if suppress_output else None, + stderr=subprocess.STDOUT if suppress_output else None, + universal_newlines=True, + ) From a4294435309d8429e8ee89908b5d7cebf6a648df Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:15 -0400 Subject: [PATCH 0918/1334] iotests/linters: Add entry point for linting via Python CI We need at least a tiny little shim here to join test file discovery with test invocation. This logic could conceivably be hosted somewhere in python/, but I felt it was strictly the least-rude thing to keep the test logic here in iotests/, even if this small function isn't itself an iotest. Note that we don't actually even need the executable bit here, we'll be relying on the ability to run this module as a script using Python CLI arguments. No chance it gets misunderstood as an actual iotest that way. (It's named, not in tests/, doesn't have the execute bit, and doesn't have an execution shebang.) Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-13-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/linters.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/qemu-iotests/linters.py b/tests/qemu-iotests/linters.py index c515c7afe3..46c28fdcda 100644 --- a/tests/qemu-iotests/linters.py +++ b/tests/qemu-iotests/linters.py @@ -16,6 +16,7 @@ import os import re import subprocess +import sys from typing import List, Mapping, Optional @@ -74,3 +75,29 @@ def run_linter( stderr=subprocess.STDOUT if suppress_output else None, universal_newlines=True, ) + + +def main() -> None: + """ + Used by the Python CI system as an entry point to run these linters. + """ + def show_usage() -> None: + print(f"Usage: {sys.argv[0]} < --mypy | --pylint >", file=sys.stderr) + sys.exit(1) + + if len(sys.argv) != 2: + show_usage() + + files = get_test_files() + + if sys.argv[1] == '--pylint': + run_linter('pylint', files) + elif sys.argv[1] == '--mypy': + run_linter('mypy', files) + else: + print(f"Unrecognized argument: '{sys.argv[1]}'", file=sys.stderr) + show_usage() + + +if __name__ == '__main__': + main() From 558dbe9935445af6ab20e18b3664ba6c43eb2311 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:16 -0400 Subject: [PATCH 0919/1334] iotests/linters: Add workaround for mypy bug #9852 This one is insidious: if you write an import as "from {namespace} import {subpackage}" as mirror-top-perms (now) does, mypy will fail on every-other invocation *if* the package being imported is a typed, installed, namespace-scoped package. Upsettingly, that's exactly what 'qemu.[aqmp|qmp|machine]' et al are in the context of Python CI tests. Now, I could just edit mirror-top-perms to avoid this invocation, but since I tripped on a landmine, I might as well head it off at the pass and make sure nobody else trips on that same landmine. It seems to have something to do with the order in which files are checked as well, meaning the random order in which set(os.listdir()) produces the list of files to test will cause problems intermittently and not just strictly "every other run". This will be fixed in mypy >= 0.920, which is not released yet. The workaround for now is to disable incremental checking, which avoids the issue. Note: This workaround is not applied when running iotest 297 directly, because the bug does not surface there! Given the nature of CI jobs not starting with any stale cache to begin with, this really only has a half-second impact on manual runs of the Python test suite when executed directly by a developer on their local machine. The workaround may be removed when the Python package requirements can stipulate mypy 0.920 or higher, which can happen as soon as it is released. (Barring any unforseen compatibility issues that 0.920 may bring with it.) See also: https://github.com/python/mypy/issues/11010 https://github.com/python/mypy/issues/9852 Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-14-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/linters.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/qemu-iotests/linters.py b/tests/qemu-iotests/linters.py index 46c28fdcda..65c4c4e827 100644 --- a/tests/qemu-iotests/linters.py +++ b/tests/qemu-iotests/linters.py @@ -93,7 +93,9 @@ def main() -> None: if sys.argv[1] == '--pylint': run_linter('pylint', files) elif sys.argv[1] == '--mypy': - run_linter('mypy', files) + # mypy bug #9852; disable incremental checking as a workaround. + args = ['--no-incremental'] + files + run_linter('mypy', args) else: print(f"Unrecognized argument: '{sys.argv[1]}'", file=sys.stderr) show_usage() From 461044ceb4fe58eb919dedfeb62f619f79d4d552 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 19 Oct 2021 10:49:17 -0400 Subject: [PATCH 0920/1334] python: Add iotest linters to test suite Run mypy and pylint on the iotests files directly from the Python CI test infrastructure. This ensures that any accidental breakages to the qemu.[qmp|aqmp|machine|utils] packages will be caught by that test suite. It also ensures that these linters are run with well-known versions and test against a wide variety of python versions, which helps to find accidental cross-version python compatibility issues. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Message-id: 20211019144918.3159078-15-jsnow@redhat.com Signed-off-by: John Snow --- python/tests/iotests-mypy.sh | 4 ++++ python/tests/iotests-pylint.sh | 4 ++++ 2 files changed, 8 insertions(+) create mode 100755 python/tests/iotests-mypy.sh create mode 100755 python/tests/iotests-pylint.sh diff --git a/python/tests/iotests-mypy.sh b/python/tests/iotests-mypy.sh new file mode 100755 index 0000000000..ee76470819 --- /dev/null +++ b/python/tests/iotests-mypy.sh @@ -0,0 +1,4 @@ +#!/bin/sh -e + +cd ../tests/qemu-iotests/ +python3 -m linters --mypy diff --git a/python/tests/iotests-pylint.sh b/python/tests/iotests-pylint.sh new file mode 100755 index 0000000000..4cae03424b --- /dev/null +++ b/python/tests/iotests-pylint.sh @@ -0,0 +1,4 @@ +#!/bin/sh -e + +cd ../tests/qemu-iotests/ +python3 -m linters --pylint From b9420e4f4b899c96221fa5a1f7f025214e756c50 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Oct 2021 13:56:05 -0400 Subject: [PATCH 0921/1334] python/machine: remove has_quit argument If we spy on the QMP commands instead, we don't need callers to remember to pass it. Seems like a fair trade-off. The one slightly weird bit is overloading this instance variable for wait(), where we use it to mean "don't issue the qmp 'quit' command". This means that wait() will "fail" if the QEMU process does not terminate of its own accord. In most cases, we probably did already actually issue quit -- some iotests do this -- but in some others, we may be waiting for QEMU to terminate for some other reason, such as a test wherein we tell the guest (directly) to shut down. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Kevin Wolf Message-id: 20211026175612.4127598-2-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 34 +++++++++++++++++++--------------- tests/qemu-iotests/040 | 7 +------ tests/qemu-iotests/218 | 2 +- tests/qemu-iotests/255 | 2 +- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 056d340e35..0bd40bc2f7 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -170,6 +170,7 @@ class QEMUMachine: self._console_socket: Optional[socket.socket] = None self._remove_files: List[str] = [] self._user_killed = False + self._quit_issued = False def __enter__(self: _T) -> _T: return self @@ -368,6 +369,7 @@ class QEMUMachine: command = '' LOG.warning(msg, -int(exitcode), command) + self._quit_issued = False self._user_killed = False self._launched = False @@ -443,15 +445,13 @@ class QEMUMachine: self._subp.kill() self._subp.wait(timeout=60) - def _soft_shutdown(self, timeout: Optional[int], - has_quit: bool = False) -> None: + def _soft_shutdown(self, timeout: Optional[int]) -> None: """ Perform early cleanup, attempt to gracefully shut down the VM, and wait for it to terminate. :param timeout: Timeout in seconds for graceful shutdown. A value of None is an infinite wait. - :param has_quit: When True, don't attempt to issue 'quit' QMP command :raise ConnectionReset: On QMP communication errors :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for @@ -460,21 +460,19 @@ class QEMUMachine: self._early_cleanup() if self._qmp_connection: - if not has_quit: + if not self._quit_issued: # Might raise ConnectionReset - self._qmp.cmd('quit') + self.qmp('quit') # May raise subprocess.TimeoutExpired self._subp.wait(timeout=timeout) - def _do_shutdown(self, timeout: Optional[int], - has_quit: bool = False) -> None: + def _do_shutdown(self, timeout: Optional[int]) -> None: """ Attempt to shutdown the VM gracefully; fallback to a hard shutdown. :param timeout: Timeout in seconds for graceful shutdown. A value of None is an infinite wait. - :param has_quit: When True, don't attempt to issue 'quit' QMP command :raise AbnormalShutdown: When the VM could not be shut down gracefully. The inner exception will likely be ConnectionReset or @@ -482,13 +480,13 @@ class QEMUMachine: may result in its own exceptions, likely subprocess.TimeoutExpired. """ try: - self._soft_shutdown(timeout, has_quit) + self._soft_shutdown(timeout) except Exception as exc: self._hard_shutdown() raise AbnormalShutdown("Could not perform graceful shutdown") \ from exc - def shutdown(self, has_quit: bool = False, + def shutdown(self, hard: bool = False, timeout: Optional[int] = 30) -> None: """ @@ -498,7 +496,6 @@ class QEMUMachine: If the VM has not yet been launched, or shutdown(), wait(), or kill() have already been called, this method does nothing. - :param has_quit: When true, do not attempt to issue 'quit' QMP command. :param hard: When true, do not attempt graceful shutdown, and suppress the SIGKILL warning log message. :param timeout: Optional timeout in seconds for graceful shutdown. @@ -512,7 +509,7 @@ class QEMUMachine: self._user_killed = True self._hard_shutdown() else: - self._do_shutdown(timeout, has_quit) + self._do_shutdown(timeout) finally: self._post_shutdown() @@ -529,7 +526,8 @@ class QEMUMachine: :param timeout: Optional timeout in seconds. Default 30 seconds. A value of `None` is an infinite wait. """ - self.shutdown(has_quit=True, timeout=timeout) + self._quit_issued = True + self.shutdown(timeout=timeout) def set_qmp_monitor(self, enabled: bool = True) -> None: """ @@ -574,7 +572,10 @@ class QEMUMachine: conv_keys = True qmp_args = self._qmp_args(conv_keys, args) - return self._qmp.cmd(cmd, args=qmp_args) + ret = self._qmp.cmd(cmd, args=qmp_args) + if cmd == 'quit' and 'error' not in ret and 'return' in ret: + self._quit_issued = True + return ret def command(self, cmd: str, conv_keys: bool = True, @@ -585,7 +586,10 @@ class QEMUMachine: On failure raise an exception. """ qmp_args = self._qmp_args(conv_keys, args) - return self._qmp.command(cmd, **qmp_args) + ret = self._qmp.command(cmd, **qmp_args) + if cmd == 'quit': + self._quit_issued = True + return ret def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]: """ diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index f3677de9df..6af5ab9e76 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -92,10 +92,9 @@ class TestSingleDrive(ImageCommitTestCase): self.vm.add_device('virtio-scsi') self.vm.add_device("scsi-hd,id=scsi0,drive=drive0") self.vm.launch() - self.has_quit = False def tearDown(self): - self.vm.shutdown(has_quit=self.has_quit) + self.vm.shutdown() os.remove(test_img) os.remove(mid_img) os.remove(backing_img) @@ -127,8 +126,6 @@ class TestSingleDrive(ImageCommitTestCase): result = self.vm.qmp('quit') self.assert_qmp(result, 'return', {}) - self.has_quit = True - # Same as above, but this time we add the filter after starting the job @iotests.skip_if_unsupported(['throttle']) def test_commit_plus_filter_and_quit(self): @@ -147,8 +144,6 @@ class TestSingleDrive(ImageCommitTestCase): result = self.vm.qmp('quit') self.assert_qmp(result, 'return', {}) - self.has_quit = True - def test_device_not_found(self): result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) self.assert_qmp(result, 'error/class', 'DeviceNotFound') diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218 index 325d8244fb..4922b4d3b6 100755 --- a/tests/qemu-iotests/218 +++ b/tests/qemu-iotests/218 @@ -187,4 +187,4 @@ with iotests.VM() as vm, \ log(vm.qmp('quit')) with iotests.Timeout(5, 'Timeout waiting for VM to quit'): - vm.shutdown(has_quit=True) + vm.shutdown() diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255 index c43aa9c67a..3d6d0e80cb 100755 --- a/tests/qemu-iotests/255 +++ b/tests/qemu-iotests/255 @@ -123,4 +123,4 @@ with iotests.FilePath('src.qcow2') as src_path, \ vm.qmp_log('block-job-cancel', device='job0') vm.qmp_log('quit') - vm.shutdown(has_quit=True) + vm.shutdown() From 49a608b8c22db72d47e7676ff9c3b87d1d408736 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Oct 2021 13:56:06 -0400 Subject: [PATCH 0922/1334] python/machine: Handle QMP errors on close more meticulously To use the AQMP backend, Machine just needs to be a little more diligent about what happens when closing a QMP connection. The operation is no longer a freebie in the async world; it may return errors encountered in the async bottom half on incoming message receipt, etc. (AQMP's disconnect, ultimately, serves as the quiescence point where all async contexts are gathered together, and any final errors reported at that point.) Because async QMP continues to check for messages asynchronously, it's almost certainly likely that the loop will have exited due to EOF after issuing the last 'quit' command. That error will ultimately be bubbled up when attempting to close the QMP connection. The manager class here then is free to discard it -- if it was expected. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Kevin Wolf Message-id: 20211026175612.4127598-3-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 48 +++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 0bd40bc2f7..a0cf69786b 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -342,9 +342,15 @@ class QEMUMachine: # Comprehensive reset for the failed launch case: self._early_cleanup() - if self._qmp_connection: - self._qmp.close() - self._qmp_connection = None + try: + self._close_qmp_connection() + except Exception as err: # pylint: disable=broad-except + LOG.warning( + "Exception closing QMP connection: %s", + str(err) if str(err) else type(err).__name__ + ) + finally: + assert self._qmp_connection is None self._close_qemu_log_file() @@ -420,6 +426,31 @@ class QEMUMachine: close_fds=False) self._post_launch() + def _close_qmp_connection(self) -> None: + """ + Close the underlying QMP connection, if any. + + Dutifully report errors that occurred while closing, but assume + that any error encountered indicates an abnormal termination + process and not a failure to close. + """ + if self._qmp_connection is None: + return + + try: + self._qmp.close() + except EOFError: + # EOF can occur as an Exception here when using the Async + # QMP backend. It indicates that the server closed the + # stream. If we successfully issued 'quit' at any point, + # then this was expected. If the remote went away without + # our permission, it's worth reporting that as an abnormal + # shutdown case. + if not (self._user_killed or self._quit_issued): + raise + finally: + self._qmp_connection = None + def _early_cleanup(self) -> None: """ Perform any cleanup that needs to happen before the VM exits. @@ -460,9 +491,14 @@ class QEMUMachine: self._early_cleanup() if self._qmp_connection: - if not self._quit_issued: - # Might raise ConnectionReset - self.qmp('quit') + try: + if not self._quit_issued: + # May raise ExecInterruptedError or StateError if the + # connection dies or has *already* died. + self.qmp('quit') + finally: + # Regardless, we want to quiesce the connection. + self._close_qmp_connection() # May raise subprocess.TimeoutExpired self._subp.wait(timeout=timeout) From 0f71c9a93693de54ca80a47c3996d64cd65f72b7 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Oct 2021 13:56:07 -0400 Subject: [PATCH 0923/1334] python/aqmp: Remove scary message The scary message interferes with the iotests output. Coincidentally, if iotests works by removing this, then it's good evidence that we don't really need to scare people away from using it. Signed-off-by: John Snow Acked-by: Hanna Reitz Reviewed-by: Kevin Wolf Reviewed-by: Hanna Reitz Message-id: 20211026175612.4127598-4-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/__init__.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/python/qemu/aqmp/__init__.py b/python/qemu/aqmp/__init__.py index d1b0e4dc3d..880d5b6fa7 100644 --- a/python/qemu/aqmp/__init__.py +++ b/python/qemu/aqmp/__init__.py @@ -22,7 +22,6 @@ managing QMP events. # the COPYING file in the top-level directory. import logging -import warnings from .error import AQMPError from .events import EventListener @@ -31,17 +30,6 @@ from .protocol import ConnectError, Runstate, StateError from .qmp_client import ExecInterruptedError, ExecuteError, QMPClient -_WMSG = """ - -The Asynchronous QMP library is currently in development and its API -should be considered highly fluid and subject to change. It should -not be used by any other scripts checked into the QEMU tree. - -Proceed with caution! -""" - -warnings.warn(_WMSG, FutureWarning) - # Suppress logging unless an application engages it. logging.getLogger('qemu.aqmp').addHandler(logging.NullHandler()) From 3bd559467d979165065f8406d2016aaa31f2cee2 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Oct 2021 13:56:08 -0400 Subject: [PATCH 0924/1334] iotests: Accommodate async QMP Exception classes (But continue to support the old ones for now, too.) There are very few cases of any user of QEMUMachine or a subclass thereof relying on a QMP Exception type. If you'd like to check for yourself, you want to grep for all of the derivatives of QMPError, excluding 'AQMPError' and its derivatives. That'd be these: - QMPError - QMPConnectError - QMPCapabilitiesError - QMPTimeoutError - QMPProtocolError - QMPResponseError - QMPBadPortError Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Kevin Wolf Message-id: 20211026175612.4127598-5-jsnow@redhat.com Signed-off-by: John Snow --- scripts/simplebench/bench_block_job.py | 3 ++- tests/qemu-iotests/tests/mirror-top-perms | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py index 4f03c12169..a403c35b08 100755 --- a/scripts/simplebench/bench_block_job.py +++ b/scripts/simplebench/bench_block_job.py @@ -28,6 +28,7 @@ import json sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu.machine import QEMUMachine from qemu.qmp import QMPConnectError +from qemu.aqmp import ConnectError def bench_block_job(cmd, cmd_args, qemu_args): @@ -49,7 +50,7 @@ def bench_block_job(cmd, cmd_args, qemu_args): vm.launch() except OSError as e: return {'error': 'popen failed: ' + str(e)} - except (QMPConnectError, socket.timeout): + except (QMPConnectError, ConnectError, socket.timeout): return {'error': 'qemu failed: ' + str(vm.get_log())} try: diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms index 3d475aa3a5..a2d5c269d7 100755 --- a/tests/qemu-iotests/tests/mirror-top-perms +++ b/tests/qemu-iotests/tests/mirror-top-perms @@ -21,8 +21,9 @@ import os -from qemu import qmp +from qemu.aqmp import ConnectError from qemu.machine import machine +from qemu.qmp import QMPConnectError import iotests from iotests import qemu_img @@ -102,7 +103,7 @@ class TestMirrorTopPerms(iotests.QMPTestCase): self.vm_b.launch() print('ERROR: VM B launched successfully, this should not have ' 'happened') - except qmp.QMPConnectError: + except (QMPConnectError, ConnectError): assert 'Is another process using the image' in self.vm_b.get_log() result = self.vm.qmp('block-job-cancel', From 206dc475484c198d2bbfaf2aacb7bc370a734277 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Oct 2021 13:56:09 -0400 Subject: [PATCH 0925/1334] iotests: Conditionally silence certain AQMP errors AQMP likes to be very chatty about errors it encounters. In general, this is good because it allows us to get good diagnostic information for otherwise complex async failures. For example, during a failed QMP connection attempt, we might see: +ERROR:qemu.aqmp.qmp_client.qemub-2536319:Negotiation failed: EOFError +ERROR:qemu.aqmp.qmp_client.qemub-2536319:Failed to establish session: EOFError This might be nice in iotests output, because failure scenarios involving the new QMP library will be spelled out plainly in the output diffs. For tests that are intentionally causing this scenario though, filtering that log output could be a hassle. For now, add a context manager that simply lets us toggle this output off during a critical region. (Additionally, a forthcoming patch allows the use of either legacy or async QMP to be toggled with an environment variable. In this circumstance, we can't amend the iotest output to just always expect the error message, either. Just suppress it for now. More rigorous log filtering can be investigated later if/when it is deemed safe to permanently replace the legacy QMP library.) Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Kevin Wolf Message-id: 20211026175612.4127598-6-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/iotests.py | 20 +++++++++++++++++++- tests/qemu-iotests/tests/mirror-top-perms | 12 ++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index e5fff6ddcf..e2f9d873ad 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -30,7 +30,7 @@ import struct import subprocess import sys import time -from typing import (Any, Callable, Dict, Iterable, +from typing import (Any, Callable, Dict, Iterable, Iterator, List, Optional, Sequence, TextIO, Tuple, Type, TypeVar) import unittest @@ -114,6 +114,24 @@ luks_default_key_secret_opt = 'key-secret=keysec0' sample_img_dir = os.environ['SAMPLE_IMG_DIR'] +@contextmanager +def change_log_level( + logger_name: str, level: int = logging.CRITICAL) -> Iterator[None]: + """ + Utility function for temporarily changing the log level of a logger. + + This can be used to silence errors that are expected or uninteresting. + """ + _logger = logging.getLogger(logger_name) + current_level = _logger.level + _logger.setLevel(level) + + try: + yield + finally: + _logger.setLevel(current_level) + + def unarchive_sample_image(sample, fname): sample_fname = os.path.join(sample_img_dir, sample + '.bz2') with bz2.open(sample_fname) as f_in, open(fname, 'wb') as f_out: diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms index a2d5c269d7..0a51a613f3 100755 --- a/tests/qemu-iotests/tests/mirror-top-perms +++ b/tests/qemu-iotests/tests/mirror-top-perms @@ -26,7 +26,7 @@ from qemu.machine import machine from qemu.qmp import QMPConnectError import iotests -from iotests import qemu_img +from iotests import change_log_level, qemu_img image_size = 1 * 1024 * 1024 @@ -100,9 +100,13 @@ class TestMirrorTopPerms(iotests.QMPTestCase): self.vm_b.add_blockdev(f'file,node-name=drive0,filename={source}') self.vm_b.add_device('virtio-blk,drive=drive0,share-rw=on') try: - self.vm_b.launch() - print('ERROR: VM B launched successfully, this should not have ' - 'happened') + # Silence AQMP errors temporarily. + # TODO: Remove this and just allow the errors to be logged when + # AQMP fully replaces QMP. + with change_log_level('qemu.aqmp'): + self.vm_b.launch() + print('ERROR: VM B launched successfully, ' + 'this should not have happened') except (QMPConnectError, ConnectError): assert 'Is another process using the image' in self.vm_b.get_log() From 8f05aee5333a78e2244008021f72e7b21fdc147f Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Oct 2021 13:56:10 -0400 Subject: [PATCH 0926/1334] iotests/300: avoid abnormal shutdown race condition Wait for the destination VM to close itself instead of racing to shut it down first, which produces different error log messages from AQMP depending on precisely when we tried to shut it down. (For example: We may try to issue 'quit' immediately prior to the target VM closing its QMP socket, which will cause an ECONNRESET error to be logged. Waiting for the VM to exit itself avoids the race on shutdown behavior.) Reported-by: Hanna Reitz Signed-off-by: John Snow Reviewed-by: Kevin Wolf Reviewed-by: Hanna Reitz Message-id: 20211026175612.4127598-7-jsnow@redhat.com Signed-off-by: John Snow --- tests/qemu-iotests/300 | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300 index 10f9f2a8da..dbd28384ec 100755 --- a/tests/qemu-iotests/300 +++ b/tests/qemu-iotests/300 @@ -24,8 +24,6 @@ import random import re from typing import Dict, List, Optional -from qemu.machine import machine - import iotests @@ -461,12 +459,11 @@ class TestBlockBitmapMappingErrors(TestDirtyBitmapMigration): f"'{self.src_node_name}': Name is longer than 255 bytes", log) - # Expect abnormal shutdown of the destination VM because of - # the failed migration - try: - self.vm_b.shutdown() - except machine.AbnormalShutdown: - pass + # Destination VM will terminate w/ error of its own accord + # due to the failed migration. + self.vm_b.wait() + rc = self.vm_b.exitcode() + assert rc is not None and rc > 0 def test_aliased_bitmap_name_too_long(self) -> None: # Longer than the maximum for bitmap names From f122be6093f5fc61fe5784e342781fab07f5defc Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Oct 2021 13:56:11 -0400 Subject: [PATCH 0927/1334] python/aqmp: Create sync QMP wrapper for iotests This is a wrapper around the async QMPClient that mimics the old, synchronous QEMUMonitorProtocol class. It is designed to be interchangeable with the old implementation. It does not, however, attempt to mimic Exception compatibility. Signed-off-by: John Snow Acked-by: Hanna Reitz Reviewed-by: Kevin Wolf Reviewed-by: Hanna Reitz Message-id: 20211026175612.4127598-8-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/legacy.py | 138 +++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 python/qemu/aqmp/legacy.py diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py new file mode 100644 index 0000000000..9e7b9fb80b --- /dev/null +++ b/python/qemu/aqmp/legacy.py @@ -0,0 +1,138 @@ +""" +Sync QMP Wrapper + +This class pretends to be qemu.qmp.QEMUMonitorProtocol. +""" + +import asyncio +from typing import ( + Awaitable, + List, + Optional, + TypeVar, + Union, +) + +import qemu.qmp +from qemu.qmp import QMPMessage, QMPReturnValue, SocketAddrT + +from .qmp_client import QMPClient + + +# pylint: disable=missing-docstring + + +class QEMUMonitorProtocol(qemu.qmp.QEMUMonitorProtocol): + def __init__(self, address: SocketAddrT, + server: bool = False, + nickname: Optional[str] = None): + + # pylint: disable=super-init-not-called + self._aqmp = QMPClient(nickname) + self._aloop = asyncio.get_event_loop() + self._address = address + self._timeout: Optional[float] = None + + _T = TypeVar('_T') + + def _sync( + self, future: Awaitable[_T], timeout: Optional[float] = None + ) -> _T: + return self._aloop.run_until_complete( + asyncio.wait_for(future, timeout=timeout) + ) + + def _get_greeting(self) -> Optional[QMPMessage]: + if self._aqmp.greeting is not None: + # pylint: disable=protected-access + return self._aqmp.greeting._asdict() + return None + + # __enter__ and __exit__ need no changes + # parse_address needs no changes + + def connect(self, negotiate: bool = True) -> Optional[QMPMessage]: + self._aqmp.await_greeting = negotiate + self._aqmp.negotiate = negotiate + + self._sync( + self._aqmp.connect(self._address) + ) + return self._get_greeting() + + def accept(self, timeout: Optional[float] = 15.0) -> QMPMessage: + self._aqmp.await_greeting = True + self._aqmp.negotiate = True + + self._sync( + self._aqmp.accept(self._address), + timeout + ) + + ret = self._get_greeting() + assert ret is not None + return ret + + def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage: + return dict( + self._sync( + # pylint: disable=protected-access + + # _raw() isn't a public API, because turning off + # automatic ID assignment is discouraged. For + # compatibility with iotests *only*, do it anyway. + self._aqmp._raw(qmp_cmd, assign_id=False), + self._timeout + ) + ) + + # Default impl of cmd() delegates to cmd_obj + + def command(self, cmd: str, **kwds: object) -> QMPReturnValue: + return self._sync( + self._aqmp.execute(cmd, kwds), + self._timeout + ) + + def pull_event(self, + wait: Union[bool, float] = False) -> Optional[QMPMessage]: + if not wait: + # wait is False/0: "do not wait, do not except." + if self._aqmp.events.empty(): + return None + + # If wait is 'True', wait forever. If wait is False/0, the events + # queue must not be empty; but it still needs some real amount + # of time to complete. + timeout = None + if wait and isinstance(wait, float): + timeout = wait + + return dict( + self._sync( + self._aqmp.events.get(), + timeout + ) + ) + + def get_events(self, wait: Union[bool, float] = False) -> List[QMPMessage]: + events = [dict(x) for x in self._aqmp.events.clear()] + if events: + return events + + event = self.pull_event(wait) + return [event] if event is not None else [] + + def clear_events(self) -> None: + self._aqmp.events.clear() + + def close(self) -> None: + self._sync( + self._aqmp.disconnect() + ) + + def settimeout(self, timeout: Optional[float]) -> None: + self._timeout = timeout + + def send_fd_scm(self, fd: int) -> None: + self._aqmp.send_fd_scm(fd) From 76cd358671e6b8e7c435ec65b1c44200254514a9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Oct 2021 13:56:12 -0400 Subject: [PATCH 0928/1334] python, iotests: replace qmp with aqmp Swap out the synchronous QEMUMonitorProtocol from qemu.qmp with the sync wrapper from qemu.aqmp instead. Add an escape hatch in the form of the environment variable QEMU_PYTHON_LEGACY_QMP which allows you to cajole QEMUMachine into using the old implementation, proving that both implementations work concurrently. Signed-off-by: John Snow Reviewed-by: Kevin Wolf Reviewed-by: Hanna Reitz Message-id: 20211026175612.4127598-9-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index a0cf69786b..a487c39745 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -41,7 +41,6 @@ from typing import ( ) from qemu.qmp import ( # pylint: disable=import-error - QEMUMonitorProtocol, QMPMessage, QMPReturnValue, SocketAddrT, @@ -50,6 +49,12 @@ from qemu.qmp import ( # pylint: disable=import-error from . import console_socket +if os.environ.get('QEMU_PYTHON_LEGACY_QMP'): + from qemu.qmp import QEMUMonitorProtocol +else: + from qemu.aqmp.legacy import QEMUMonitorProtocol + + LOG = logging.getLogger(__name__) From f36d4fb85f41604038386e1eb4295acd7d372d86 Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Wed, 27 Oct 2021 17:04:05 +0800 Subject: [PATCH 0929/1334] vfio/pci: Add support for mmapping sub-page MMIO BARs after live migration We can expand MemoryRegions of sub-page MMIO BARs in vfio_pci_write_config() to improve IO performance for some devices. However, the MemoryRegions of destination VM are not expanded any more after live migration. Because their addresses have been updated in vmstate_load_state() (vfio_pci_load_config) and vfio_sub_page_bar_update_mapping() will not be called. This may result in poor performance after live migration. So iterate BARs in vfio_pci_load_config() and try to update sub-page BARs. Reported-by: Nianyao Tang Reported-by: Qixin Gan Signed-off-by: Kunkun Jiang Link: https://lore.kernel.org/r/20211027090406.761-2-jiangkunkun@huawei.com Signed-off-by: Alex Williamson --- hw/vfio/pci.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 5cdf1d4298..7b45353ce2 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2453,7 +2453,12 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); PCIDevice *pdev = &vdev->pdev; - int ret; + pcibus_t old_addr[PCI_NUM_REGIONS - 1]; + int bar, ret; + + for (bar = 0; bar < PCI_ROM_SLOT; bar++) { + old_addr[bar] = pdev->io_regions[bar].addr; + } ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1); if (ret) { @@ -2463,6 +2468,18 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) vfio_pci_write_config(pdev, PCI_COMMAND, pci_get_word(pdev->config + PCI_COMMAND), 2); + for (bar = 0; bar < PCI_ROM_SLOT; bar++) { + /* + * The address may not be changed in some scenarios + * (e.g. the VF driver isn't loaded in VM). + */ + if (old_addr[bar] != pdev->io_regions[bar].addr && + vdev->bars[bar].region.size > 0 && + vdev->bars[bar].region.size < qemu_real_host_page_size) { + vfio_sub_page_bar_update_mapping(pdev, bar); + } + } + if (msi_enabled(pdev)) { vfio_msi_enable(vdev); } else if (msix_enabled(pdev)) { From e4b34708388b20f1ceb55f1d563d8da925a32424 Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Wed, 27 Oct 2021 17:04:06 +0800 Subject: [PATCH 0930/1334] vfio/common: Add a trace point when a MMIO RAM section cannot be mapped The MSI-X structures of some devices and other non-MSI-X structures may be in the same BAR. They may share one host page, especially in the case of large page granularity, such as 64K. For example, MSIX-Table size of 82599 NIC is 0x30 and the offset in Bar 3(size 64KB) is 0x0. vfio_listener_region_add() will be called to map the remaining range (0x30-0xffff). If host page size is 64KB, it will return early at 'int128_ge((int128_make64(iova), llend))' without any message. Let's add a trace point to inform users like commit 5c08600547c0 ("vfio: Use a trace point when a RAM section cannot be DMA mapped") did. Signed-off-by: Kunkun Jiang Link: https://lore.kernel.org/r/20211027090406.761-3-jiangkunkun@huawei.com Signed-off-by: Alex Williamson --- hw/vfio/common.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index a784b219e6..dd387b0d39 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -893,6 +893,13 @@ static void vfio_listener_region_add(MemoryListener *listener, llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); if (int128_ge(int128_make64(iova), llend)) { + if (memory_region_is_ram_device(section->mr)) { + trace_vfio_listener_region_add_no_dma_map( + memory_region_name(section->mr), + section->offset_within_address_space, + int128_getlo(section->size), + qemu_real_host_page_size); + } return; } end = int128_get64(int128_sub(llend, int128_one())); From b19a3e2cd9c749f1925f1557a6409d1eefbd9b9c Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 25 Apr 2019 17:00:48 -0300 Subject: [PATCH 0931/1334] machine: Move gpio code to hw/core/gpio.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only softmmu code uses gpio, so move gpio code from qdev.c to gpio.c and compile it only on softmmu mode. Signed-off-by: Eduardo Habkost Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-Id: <20190425200051.19906-2-ehabkost@redhat.com> [PMD: Rebased] Signed-off-by: Philippe Mathieu-Daudé --- hw/core/gpio.c | 197 ++++++++++++++++++++++++++++++++++++++++++++ hw/core/meson.build | 1 + hw/core/qdev.c | 174 -------------------------------------- 3 files changed, 198 insertions(+), 174 deletions(-) create mode 100644 hw/core/gpio.c diff --git a/hw/core/gpio.c b/hw/core/gpio.c new file mode 100644 index 0000000000..8e6b4f5edf --- /dev/null +++ b/hw/core/gpio.c @@ -0,0 +1,197 @@ +/* + * qdev GPIO helpers + * + * Copyright (c) 2009 CodeSourcery + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "hw/irq.h" +#include "qapi/error.h" + +static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev, + const char *name) +{ + NamedGPIOList *ngl; + + QLIST_FOREACH(ngl, &dev->gpios, node) { + /* NULL is a valid and matchable name. */ + if (g_strcmp0(name, ngl->name) == 0) { + return ngl; + } + } + + ngl = g_malloc0(sizeof(*ngl)); + ngl->name = g_strdup(name); + QLIST_INSERT_HEAD(&dev->gpios, ngl, node); + return ngl; +} + +void qdev_init_gpio_in_named_with_opaque(DeviceState *dev, + qemu_irq_handler handler, + void *opaque, + const char *name, int n) +{ + int i; + NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); + + assert(gpio_list->num_out == 0 || !name); + gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler, + opaque, n); + + if (!name) { + name = "unnamed-gpio-in"; + } + for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) { + gchar *propname = g_strdup_printf("%s[%u]", name, i); + + object_property_add_child(OBJECT(dev), propname, + OBJECT(gpio_list->in[i])); + g_free(propname); + } + + gpio_list->num_in += n; +} + +void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n) +{ + qdev_init_gpio_in_named(dev, handler, NULL, n); +} + +void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, + const char *name, int n) +{ + int i; + NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); + + assert(gpio_list->num_in == 0 || !name); + + if (!name) { + name = "unnamed-gpio-out"; + } + memset(pins, 0, sizeof(*pins) * n); + for (i = 0; i < n; ++i) { + gchar *propname = g_strdup_printf("%s[%u]", name, + gpio_list->num_out + i); + + object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, + (Object **)&pins[i], + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + g_free(propname); + } + gpio_list->num_out += n; +} + +void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n) +{ + qdev_init_gpio_out_named(dev, pins, NULL, n); +} + +qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n) +{ + NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); + + assert(n >= 0 && n < gpio_list->num_in); + return gpio_list->in[n]; +} + +qemu_irq qdev_get_gpio_in(DeviceState *dev, int n) +{ + return qdev_get_gpio_in_named(dev, NULL, n); +} + +void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, + qemu_irq pin) +{ + char *propname = g_strdup_printf("%s[%d]", + name ? name : "unnamed-gpio-out", n); + if (pin && !OBJECT(pin)->parent) { + /* We need a name for object_property_set_link to work */ + object_property_add_child(container_get(qdev_get_machine(), + "/unattached"), + "non-qdev-gpio[*]", OBJECT(pin)); + } + object_property_set_link(OBJECT(dev), propname, OBJECT(pin), &error_abort); + g_free(propname); +} + +qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) +{ + g_autofree char *propname = g_strdup_printf("%s[%d]", + name ? name : "unnamed-gpio-out", n); + + qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, + NULL); + + return ret; +} + +/* disconnect a GPIO output, returning the disconnected input (if any) */ + +static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev, + const char *name, int n) +{ + char *propname = g_strdup_printf("%s[%d]", + name ? name : "unnamed-gpio-out", n); + + qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, + NULL); + if (ret) { + object_property_set_link(OBJECT(dev), propname, NULL, NULL); + } + g_free(propname); + return ret; +} + +qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, + const char *name, int n) +{ + qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n); + qdev_connect_gpio_out_named(dev, name, n, icpt); + return disconnected; +} + +void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin) +{ + qdev_connect_gpio_out_named(dev, NULL, n, pin); +} + +void qdev_pass_gpios(DeviceState *dev, DeviceState *container, + const char *name) +{ + int i; + NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name); + + for (i = 0; i < ngl->num_in; i++) { + const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in"; + char *propname = g_strdup_printf("%s[%d]", nm, i); + + object_property_add_alias(OBJECT(container), propname, + OBJECT(dev), propname); + g_free(propname); + } + for (i = 0; i < ngl->num_out; i++) { + const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out"; + char *propname = g_strdup_printf("%s[%d]", nm, i); + + object_property_add_alias(OBJECT(container), propname, + OBJECT(dev), propname); + g_free(propname); + } + QLIST_REMOVE(ngl, node); + QLIST_INSERT_HEAD(&container->gpios, ngl, node); +} diff --git a/hw/core/meson.build b/hw/core/meson.build index 18f44fb7c2..86f26f0a60 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -27,6 +27,7 @@ common_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) softmmu_ss.add(files( 'cpu-sysemu.c', 'fw-path-provider.c', + 'gpio.c', 'loader.c', 'machine-hmp-cmds.c', 'machine.c', diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 7f06403752..1e38f99724 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -436,180 +436,6 @@ BusState *qdev_get_parent_bus(DeviceState *dev) return dev->parent_bus; } -static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev, - const char *name) -{ - NamedGPIOList *ngl; - - QLIST_FOREACH(ngl, &dev->gpios, node) { - /* NULL is a valid and matchable name. */ - if (g_strcmp0(name, ngl->name) == 0) { - return ngl; - } - } - - ngl = g_malloc0(sizeof(*ngl)); - ngl->name = g_strdup(name); - QLIST_INSERT_HEAD(&dev->gpios, ngl, node); - return ngl; -} - -void qdev_init_gpio_in_named_with_opaque(DeviceState *dev, - qemu_irq_handler handler, - void *opaque, - const char *name, int n) -{ - int i; - NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); - - assert(gpio_list->num_out == 0 || !name); - gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler, - opaque, n); - - if (!name) { - name = "unnamed-gpio-in"; - } - for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) { - gchar *propname = g_strdup_printf("%s[%u]", name, i); - - object_property_add_child(OBJECT(dev), propname, - OBJECT(gpio_list->in[i])); - g_free(propname); - } - - gpio_list->num_in += n; -} - -void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n) -{ - qdev_init_gpio_in_named(dev, handler, NULL, n); -} - -void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, - const char *name, int n) -{ - int i; - NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); - - assert(gpio_list->num_in == 0 || !name); - - if (!name) { - name = "unnamed-gpio-out"; - } - memset(pins, 0, sizeof(*pins) * n); - for (i = 0; i < n; ++i) { - gchar *propname = g_strdup_printf("%s[%u]", name, - gpio_list->num_out + i); - - object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, - (Object **)&pins[i], - object_property_allow_set_link, - OBJ_PROP_LINK_STRONG); - g_free(propname); - } - gpio_list->num_out += n; -} - -void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n) -{ - qdev_init_gpio_out_named(dev, pins, NULL, n); -} - -qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n) -{ - NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); - - assert(n >= 0 && n < gpio_list->num_in); - return gpio_list->in[n]; -} - -qemu_irq qdev_get_gpio_in(DeviceState *dev, int n) -{ - return qdev_get_gpio_in_named(dev, NULL, n); -} - -void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, - qemu_irq pin) -{ - char *propname = g_strdup_printf("%s[%d]", - name ? name : "unnamed-gpio-out", n); - if (pin && !OBJECT(pin)->parent) { - /* We need a name for object_property_set_link to work */ - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), - "non-qdev-gpio[*]", OBJECT(pin)); - } - object_property_set_link(OBJECT(dev), propname, OBJECT(pin), &error_abort); - g_free(propname); -} - -qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) -{ - g_autofree char *propname = g_strdup_printf("%s[%d]", - name ? name : "unnamed-gpio-out", n); - - qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, - NULL); - - return ret; -} - -/* disconnect a GPIO output, returning the disconnected input (if any) */ - -static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev, - const char *name, int n) -{ - char *propname = g_strdup_printf("%s[%d]", - name ? name : "unnamed-gpio-out", n); - - qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, - NULL); - if (ret) { - object_property_set_link(OBJECT(dev), propname, NULL, NULL); - } - g_free(propname); - return ret; -} - -qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, - const char *name, int n) -{ - qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n); - qdev_connect_gpio_out_named(dev, name, n, icpt); - return disconnected; -} - -void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin) -{ - qdev_connect_gpio_out_named(dev, NULL, n, pin); -} - -void qdev_pass_gpios(DeviceState *dev, DeviceState *container, - const char *name) -{ - int i; - NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name); - - for (i = 0; i < ngl->num_in; i++) { - const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in"; - char *propname = g_strdup_printf("%s[%d]", nm, i); - - object_property_add_alias(OBJECT(container), propname, - OBJECT(dev), propname); - g_free(propname); - } - for (i = 0; i < ngl->num_out; i++) { - const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out"; - char *propname = g_strdup_printf("%s[%d]", nm, i); - - object_property_add_alias(OBJECT(container), propname, - OBJECT(dev), propname); - g_free(propname); - } - QLIST_REMOVE(ngl, node); - QLIST_INSERT_HEAD(&container->gpios, ngl, node); -} - BusState *qdev_get_child_bus(DeviceState *dev, const char *name) { BusState *bus; From d675b44ecca6e660256a59800dda96b242b74afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Oct 2021 16:37:47 +0200 Subject: [PATCH 0932/1334] hw/core: Restrict sysemu specific files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All these files don't make sense for tools and user emulation, restrict them to system emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Yanan Wang Tested-by: Yanan Wang Acked-by: Eduardo Habkost Message-Id: <20211028150521.1973821-2-philmd@redhat.com> --- hw/core/meson.build | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/core/meson.build b/hw/core/meson.build index 86f26f0a60..6af4c5c5cb 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -14,15 +14,15 @@ hwcore_files = files( ) common_ss.add(files('cpu-common.c')) -common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) -common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) -common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) -common_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c')) -common_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c')) -common_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) -common_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) -common_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) -common_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) +softmmu_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) +softmmu_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) +softmmu_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) +softmmu_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c')) +softmmu_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c')) +softmmu_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) +softmmu_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) +softmmu_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) +softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) softmmu_ss.add(files( 'cpu-sysemu.c', From f73fb063950b9da3a5869fe9ce396abf157c1d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Oct 2021 16:34:19 +0200 Subject: [PATCH 0933/1334] hw/core: Declare meson source set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we want to be able to conditionally add files to the hw/core file list, use a source set. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Yanan Wang Tested-by: Yanan Wang Acked-by: Eduardo Habkost Message-Id: <20211028150521.1973821-3-philmd@redhat.com> --- hw/core/meson.build | 4 ++-- meson.build | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/core/meson.build b/hw/core/meson.build index 6af4c5c5cb..cc1ebb8e0f 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -1,5 +1,5 @@ # core qdev-related obj files, also used by *-user and unit tests -hwcore_files = files( +hwcore_ss.add(files( 'bus.c', 'hotplug.c', 'qdev-properties.c', @@ -11,7 +11,7 @@ hwcore_files = files( 'irq.c', 'clock.c', 'qdev-clock.c', -) +)) common_ss.add(files('cpu-common.c')) softmmu_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) diff --git a/meson.build b/meson.build index b092728397..85f1e43dfe 100644 --- a/meson.build +++ b/meson.build @@ -2365,6 +2365,7 @@ bsd_user_ss = ss.source_set() chardev_ss = ss.source_set() common_ss = ss.source_set() crypto_ss = ss.source_set() +hwcore_ss = ss.source_set() io_ss = ss.source_set() linux_user_ss = ss.source_set() qmp_ss = ss.source_set() @@ -2806,7 +2807,8 @@ libchardev = static_library('chardev', chardev_ss.sources() + genh, chardev = declare_dependency(link_whole: libchardev) -libhwcore = static_library('hwcore', sources: hwcore_files + genh, +hwcore_ss = hwcore_ss.apply(config_host, strict: false) +libhwcore = static_library('hwcore', sources: hwcore_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) hwcore = declare_dependency(link_whole: libhwcore) From 81c7b381558541531fa119e16a58098d6febbf5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Oct 2021 16:36:38 +0200 Subject: [PATCH 0934/1334] hw/core: Extract hotplug-related functions to qdev-hotplug.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Yanan Wang Acked-by: Eduardo Habkost Message-Id: <20211028150521.1973821-4-philmd@redhat.com> --- hw/core/meson.build | 1 + hw/core/qdev-hotplug.c | 73 ++++++++++++++++++++++++++++++++++++++++++ hw/core/qdev.c | 60 ---------------------------------- 3 files changed, 74 insertions(+), 60 deletions(-) create mode 100644 hw/core/qdev-hotplug.c diff --git a/hw/core/meson.build b/hw/core/meson.build index cc1ebb8e0f..c9fe6441d9 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -11,6 +11,7 @@ hwcore_ss.add(files( 'irq.c', 'clock.c', 'qdev-clock.c', + 'qdev-hotplug.c', )) common_ss.add(files('cpu-common.c')) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c new file mode 100644 index 0000000000..d495d0e9c7 --- /dev/null +++ b/hw/core/qdev-hotplug.c @@ -0,0 +1,73 @@ +/* + * QDev Hotplug handlers + * + * Copyright (c) Red Hat + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "hw/boards.h" + +HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) +{ + MachineState *machine; + MachineClass *mc; + Object *m_obj = qdev_get_machine(); + + if (object_dynamic_cast(m_obj, TYPE_MACHINE)) { + machine = MACHINE(m_obj); + mc = MACHINE_GET_CLASS(machine); + if (mc->get_hotplug_handler) { + return mc->get_hotplug_handler(machine, dev); + } + } + + return NULL; +} + +bool qdev_hotplug_allowed(DeviceState *dev, Error **errp) +{ + MachineState *machine; + MachineClass *mc; + Object *m_obj = qdev_get_machine(); + + if (object_dynamic_cast(m_obj, TYPE_MACHINE)) { + machine = MACHINE(m_obj); + mc = MACHINE_GET_CLASS(machine); + if (mc->hotplug_allowed) { + return mc->hotplug_allowed(machine, dev, errp); + } + } + + return true; +} + +HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) +{ + if (dev->parent_bus) { + return dev->parent_bus->hotplug_handler; + } + return NULL; +} + +HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) +{ + HotplugHandler *hotplug_ctrl = qdev_get_machine_hotplug_handler(dev); + + if (hotplug_ctrl == NULL && dev->parent_bus) { + hotplug_ctrl = qdev_get_bus_hotplug_handler(dev); + } + return hotplug_ctrl; +} + +/* can be used as ->unplug() callback for the simple cases */ +void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + qdev_unrealize(dev); +} diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 1e38f99724..84f3019440 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -33,7 +33,6 @@ #include "qapi/visitor.h" #include "qemu/error-report.h" #include "qemu/option.h" -#include "hw/hotplug.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/boards.h" @@ -238,58 +237,6 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, dev->alias_required_for_version = required_for_version; } -HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) -{ - MachineState *machine; - MachineClass *mc; - Object *m_obj = qdev_get_machine(); - - if (object_dynamic_cast(m_obj, TYPE_MACHINE)) { - machine = MACHINE(m_obj); - mc = MACHINE_GET_CLASS(machine); - if (mc->get_hotplug_handler) { - return mc->get_hotplug_handler(machine, dev); - } - } - - return NULL; -} - -bool qdev_hotplug_allowed(DeviceState *dev, Error **errp) -{ - MachineState *machine; - MachineClass *mc; - Object *m_obj = qdev_get_machine(); - - if (object_dynamic_cast(m_obj, TYPE_MACHINE)) { - machine = MACHINE(m_obj); - mc = MACHINE_GET_CLASS(machine); - if (mc->hotplug_allowed) { - return mc->hotplug_allowed(machine, dev, errp); - } - } - - return true; -} - -HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) -{ - if (dev->parent_bus) { - return dev->parent_bus->hotplug_handler; - } - return NULL; -} - -HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) -{ - HotplugHandler *hotplug_ctrl = qdev_get_machine_hotplug_handler(dev); - - if (hotplug_ctrl == NULL && dev->parent_bus) { - hotplug_ctrl = qdev_get_bus_hotplug_handler(dev); - } - return hotplug_ctrl; -} - static int qdev_prereset(DeviceState *dev, void *opaque) { trace_qdev_reset_tree(dev, object_get_typename(OBJECT(dev))); @@ -371,13 +318,6 @@ static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb, } } -/* can be used as ->unplug() callback for the simple cases */ -void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - qdev_unrealize(dev); -} - bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp) { assert(!dev->realized && !dev->parent_bus); From 09112bef740853c9afd5318ecd0e93394648a3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Oct 2021 16:57:35 +0200 Subject: [PATCH 0935/1334] hw/core: Restrict hotplug to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict hotplug to system emulation, add stubs for the other uses. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Yanan Wang Acked-by: Eduardo Habkost Message-Id: <20211028150521.1973821-5-philmd@redhat.com> --- hw/core/hotplug-stubs.c | 34 ++++++++++++++++++++++++++++++++++ hw/core/meson.build | 12 ++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 hw/core/hotplug-stubs.c diff --git a/hw/core/hotplug-stubs.c b/hw/core/hotplug-stubs.c new file mode 100644 index 0000000000..7aadaa29bd --- /dev/null +++ b/hw/core/hotplug-stubs.c @@ -0,0 +1,34 @@ +/* + * Hotplug handler stubs + * + * Copyright (c) Red Hat + * + * Authors: + * Philippe Mathieu-Daudé , + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "hw/qdev-core.h" + +HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) +{ + return NULL; +} + +void hotplug_handler_pre_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + g_assert_not_reached(); +} + +void hotplug_handler_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + g_assert_not_reached(); +} diff --git a/hw/core/meson.build b/hw/core/meson.build index c9fe6441d9..7ec7bb93d5 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -1,7 +1,6 @@ # core qdev-related obj files, also used by *-user and unit tests hwcore_ss.add(files( 'bus.c', - 'hotplug.c', 'qdev-properties.c', 'qdev.c', 'reset.c', @@ -11,8 +10,17 @@ hwcore_ss.add(files( 'irq.c', 'clock.c', 'qdev-clock.c', - 'qdev-hotplug.c', )) +if have_system + hwcore_ss.add(files( + 'hotplug.c', + 'qdev-hotplug.c', + )) +else + hwcore_ss.add(files( + 'hotplug-stubs.c', + )) +endif common_ss.add(files('cpu-common.c')) softmmu_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) From 86ce2d28fa09d15547b5cabdc264cadfb53a848c Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 26 Oct 2021 11:46:58 +0800 Subject: [PATCH 0936/1334] hw/core/machine: Split out the smp parsing code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to introduce an unit test for the parser smp_parse() in hw/core/machine.c, but now machine.c is only built in softmmu. In order to solve the build dependency on the smp parsing code and avoid building unrelated stuff for the unit tests, move the tested code from machine.c into a separate file, i.e., machine-smp.c and build it in common field. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-Id: <20211026034659.22040-2-wangyanan55@huawei.com> Acked-by: Eduardo Habkost Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + hw/core/machine-smp.c | 181 ++++++++++++++++++++++++++++++++++++++++++ hw/core/machine.c | 159 ------------------------------------- hw/core/meson.build | 1 + include/hw/boards.h | 1 + 5 files changed, 184 insertions(+), 159 deletions(-) create mode 100644 hw/core/machine-smp.c diff --git a/MAINTAINERS b/MAINTAINERS index 894dc43105..80ec27d76a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1623,6 +1623,7 @@ F: cpu.c F: hw/core/cpu.c F: hw/core/machine-qmp-cmds.c F: hw/core/machine.c +F: hw/core/machine-smp.c F: hw/core/null-machine.c F: hw/core/numa.c F: hw/cpu/cluster.c diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c new file mode 100644 index 0000000000..116a0cbbfa --- /dev/null +++ b/hw/core/machine-smp.c @@ -0,0 +1,181 @@ +/* + * QEMU Machine core (related to -smp parsing) + * + * Copyright (c) 2021 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "qapi/error.h" + +/* + * Report information of a machine's supported CPU topology hierarchy. + * Topology members will be ordered from the largest to the smallest + * in the string. + */ +static char *cpu_hierarchy_to_string(MachineState *ms) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + GString *s = g_string_new(NULL); + + g_string_append_printf(s, "sockets (%u)", ms->smp.sockets); + + if (mc->smp_props.dies_supported) { + g_string_append_printf(s, " * dies (%u)", ms->smp.dies); + } + + g_string_append_printf(s, " * cores (%u)", ms->smp.cores); + g_string_append_printf(s, " * threads (%u)", ms->smp.threads); + + return g_string_free(s, false); +} + +/* + * smp_parse - Generic function used to parse the given SMP configuration + * + * Any missing parameter in "cpus/maxcpus/sockets/cores/threads" will be + * automatically computed based on the provided ones. + * + * In the calculation of omitted sockets/cores/threads: we prefer sockets + * over cores over threads before 6.2, while preferring cores over sockets + * over threads since 6.2. + * + * In the calculation of cpus/maxcpus: When both maxcpus and cpus are omitted, + * maxcpus will be computed from the given parameters and cpus will be set + * equal to maxcpus. When only one of maxcpus and cpus is given then the + * omitted one will be set to its given counterpart's value. Both maxcpus and + * cpus may be specified, but maxcpus must be equal to or greater than cpus. + * + * For compatibility, apart from the parameters that will be computed, newly + * introduced topology members which are likely to be target specific should + * be directly set as 1 if they are omitted (e.g. dies for PC since 4.1). + */ +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + unsigned cpus = config->has_cpus ? config->cpus : 0; + unsigned sockets = config->has_sockets ? config->sockets : 0; + unsigned dies = config->has_dies ? config->dies : 0; + unsigned cores = config->has_cores ? config->cores : 0; + unsigned threads = config->has_threads ? config->threads : 0; + unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; + + /* + * Specified CPU topology parameters must be greater than zero, + * explicit configuration like "cpus=0" is not allowed. + */ + if ((config->has_cpus && config->cpus == 0) || + (config->has_sockets && config->sockets == 0) || + (config->has_dies && config->dies == 0) || + (config->has_cores && config->cores == 0) || + (config->has_threads && config->threads == 0) || + (config->has_maxcpus && config->maxcpus == 0)) { + warn_report("Deprecated CPU topology (considered invalid): " + "CPU topology parameters must be greater than zero"); + } + + /* + * If not supported by the machine, a topology parameter must be + * omitted or specified equal to 1. + */ + if (!mc->smp_props.dies_supported && dies > 1) { + error_setg(errp, "dies not supported by this machine's CPU topology"); + return; + } + + dies = dies > 0 ? dies : 1; + + /* compute missing values based on the provided ones */ + if (cpus == 0 && maxcpus == 0) { + sockets = sockets > 0 ? sockets : 1; + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + } else { + maxcpus = maxcpus > 0 ? maxcpus : cpus; + + if (mc->smp_props.prefer_sockets) { + /* prefer sockets over cores before 6.2 */ + if (sockets == 0) { + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (dies * cores * threads); + } else if (cores == 0) { + threads = threads > 0 ? threads : 1; + cores = maxcpus / (sockets * dies * threads); + } + } else { + /* prefer cores over sockets since 6.2 */ + if (cores == 0) { + sockets = sockets > 0 ? sockets : 1; + threads = threads > 0 ? threads : 1; + cores = maxcpus / (sockets * dies * threads); + } else if (sockets == 0) { + threads = threads > 0 ? threads : 1; + sockets = maxcpus / (dies * cores * threads); + } + } + + /* try to calculate omitted threads at last */ + if (threads == 0) { + threads = maxcpus / (sockets * dies * cores); + } + } + + maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads; + cpus = cpus > 0 ? cpus : maxcpus; + + ms->smp.cpus = cpus; + ms->smp.sockets = sockets; + ms->smp.dies = dies; + ms->smp.cores = cores; + ms->smp.threads = threads; + ms->smp.max_cpus = maxcpus; + + /* sanity-check of the computed topology */ + if (sockets * dies * cores * threads != maxcpus) { + g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); + error_setg(errp, "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "%s != maxcpus (%u)", + topo_msg, maxcpus); + return; + } + + if (maxcpus < cpus) { + g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); + error_setg(errp, "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "%s == maxcpus (%u) < smp_cpus (%u)", + topo_msg, maxcpus, cpus); + return; + } + + if (ms->smp.cpus < mc->min_cpus) { + error_setg(errp, "Invalid SMP CPUs %d. The min CPUs " + "supported by machine '%s' is %d", + ms->smp.cpus, + mc->name, mc->min_cpus); + return; + } + + if (ms->smp.max_cpus > mc->max_cpus) { + error_setg(errp, "Invalid SMP CPUs %d. The max CPUs " + "supported by machine '%s' is %d", + ms->smp.max_cpus, + mc->name, mc->max_cpus); + return; + } +} diff --git a/hw/core/machine.c b/hw/core/machine.c index b8d95eec32..dc15f5f9e5 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -749,165 +749,6 @@ void machine_set_cpu_numa_node(MachineState *machine, } } -/* - * Report information of a machine's supported CPU topology hierarchy. - * Topology members will be ordered from the largest to the smallest - * in the string. - */ -static char *cpu_hierarchy_to_string(MachineState *ms) -{ - MachineClass *mc = MACHINE_GET_CLASS(ms); - GString *s = g_string_new(NULL); - - g_string_append_printf(s, "sockets (%u)", ms->smp.sockets); - - if (mc->smp_props.dies_supported) { - g_string_append_printf(s, " * dies (%u)", ms->smp.dies); - } - - g_string_append_printf(s, " * cores (%u)", ms->smp.cores); - g_string_append_printf(s, " * threads (%u)", ms->smp.threads); - - return g_string_free(s, false); -} - -/* - * smp_parse - Generic function used to parse the given SMP configuration - * - * Any missing parameter in "cpus/maxcpus/sockets/cores/threads" will be - * automatically computed based on the provided ones. - * - * In the calculation of omitted sockets/cores/threads: we prefer sockets - * over cores over threads before 6.2, while preferring cores over sockets - * over threads since 6.2. - * - * In the calculation of cpus/maxcpus: When both maxcpus and cpus are omitted, - * maxcpus will be computed from the given parameters and cpus will be set - * equal to maxcpus. When only one of maxcpus and cpus is given then the - * omitted one will be set to its given counterpart's value. Both maxcpus and - * cpus may be specified, but maxcpus must be equal to or greater than cpus. - * - * For compatibility, apart from the parameters that will be computed, newly - * introduced topology members which are likely to be target specific should - * be directly set as 1 if they are omitted (e.g. dies for PC since 4.1). - */ -static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) -{ - MachineClass *mc = MACHINE_GET_CLASS(ms); - unsigned cpus = config->has_cpus ? config->cpus : 0; - unsigned sockets = config->has_sockets ? config->sockets : 0; - unsigned dies = config->has_dies ? config->dies : 0; - unsigned cores = config->has_cores ? config->cores : 0; - unsigned threads = config->has_threads ? config->threads : 0; - unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; - - /* - * Specified CPU topology parameters must be greater than zero, - * explicit configuration like "cpus=0" is not allowed. - */ - if ((config->has_cpus && config->cpus == 0) || - (config->has_sockets && config->sockets == 0) || - (config->has_dies && config->dies == 0) || - (config->has_cores && config->cores == 0) || - (config->has_threads && config->threads == 0) || - (config->has_maxcpus && config->maxcpus == 0)) { - warn_report("Deprecated CPU topology (considered invalid): " - "CPU topology parameters must be greater than zero"); - } - - /* - * If not supported by the machine, a topology parameter must be - * omitted or specified equal to 1. - */ - if (!mc->smp_props.dies_supported && dies > 1) { - error_setg(errp, "dies not supported by this machine's CPU topology"); - return; - } - - dies = dies > 0 ? dies : 1; - - /* compute missing values based on the provided ones */ - if (cpus == 0 && maxcpus == 0) { - sockets = sockets > 0 ? sockets : 1; - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - } else { - maxcpus = maxcpus > 0 ? maxcpus : cpus; - - if (mc->smp_props.prefer_sockets) { - /* prefer sockets over cores before 6.2 */ - if (sockets == 0) { - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * cores * threads); - } else if (cores == 0) { - threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * threads); - } - } else { - /* prefer cores over sockets since 6.2 */ - if (cores == 0) { - sockets = sockets > 0 ? sockets : 1; - threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * threads); - } else if (sockets == 0) { - threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * cores * threads); - } - } - - /* try to calculate omitted threads at last */ - if (threads == 0) { - threads = maxcpus / (sockets * dies * cores); - } - } - - maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads; - cpus = cpus > 0 ? cpus : maxcpus; - - ms->smp.cpus = cpus; - ms->smp.sockets = sockets; - ms->smp.dies = dies; - ms->smp.cores = cores; - ms->smp.threads = threads; - ms->smp.max_cpus = maxcpus; - - /* sanity-check of the computed topology */ - if (sockets * dies * cores * threads != maxcpus) { - g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); - error_setg(errp, "Invalid CPU topology: " - "product of the hierarchy must match maxcpus: " - "%s != maxcpus (%u)", - topo_msg, maxcpus); - return; - } - - if (maxcpus < cpus) { - g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); - error_setg(errp, "Invalid CPU topology: " - "maxcpus must be equal to or greater than smp: " - "%s == maxcpus (%u) < smp_cpus (%u)", - topo_msg, maxcpus, cpus); - return; - } - - if (ms->smp.cpus < mc->min_cpus) { - error_setg(errp, "Invalid SMP CPUs %d. The min CPUs " - "supported by machine '%s' is %d", - ms->smp.cpus, - mc->name, mc->min_cpus); - return; - } - - if (ms->smp.max_cpus > mc->max_cpus) { - error_setg(errp, "Invalid SMP CPUs %d. The max CPUs " - "supported by machine '%s' is %d", - ms->smp.max_cpus, - mc->name, mc->max_cpus); - return; - } -} - static void machine_get_smp(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { diff --git a/hw/core/meson.build b/hw/core/meson.build index 7ec7bb93d5..0f884d6fd4 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -23,6 +23,7 @@ else endif common_ss.add(files('cpu-common.c')) +common_ss.add(files('machine-smp.c')) softmmu_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) softmmu_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) softmmu_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) diff --git a/include/hw/boards.h b/include/hw/boards.h index 5adbcbb99b..e36fc7d861 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -34,6 +34,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine); void machine_set_cpu_numa_node(MachineState *machine, const CpuInstanceProperties *props, Error **errp); +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp); /** * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices From 9e8e393bb795ff6c6963c47376369e90b1d40467 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 26 Oct 2021 11:46:59 +0800 Subject: [PATCH 0937/1334] tests/unit: Add an unit test for smp parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have a generic parser smp_parse(), let's add an unit test for the code. All possible valid/invalid SMP configurations that the user can specify are covered. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Tested-by: Philippe Mathieu-Daudé Message-Id: <20211026034659.22040-3-wangyanan55@huawei.com> Acked-by: Eduardo Habkost Message-Id: [PMD: Squashed format string fixup from Yanan Wang] Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + tests/unit/meson.build | 1 + tests/unit/test-smp-parse.c | 594 ++++++++++++++++++++++++++++++++++++ 3 files changed, 596 insertions(+) create mode 100644 tests/unit/test-smp-parse.c diff --git a/MAINTAINERS b/MAINTAINERS index 80ec27d76a..310a9512ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1633,6 +1633,7 @@ F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/sysemu/numa.h +F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next Xtensa Machines diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 5ac2d9e943..acac3622ed 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -46,6 +46,7 @@ tests = { 'test-uuid': [], 'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'], 'test-qapi-util': [], + 'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'], } if have_system or have_tools diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c new file mode 100644 index 0000000000..cbe0c99049 --- /dev/null +++ b/tests/unit/test-smp-parse.c @@ -0,0 +1,594 @@ +/* + * SMP parsing unit-tests + * + * Copyright (c) 2021 Huawei Technologies Co., Ltd + * + * Authors: + * Yanan Wang + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qom/object.h" +#include "qemu/module.h" +#include "qapi/error.h" + +#include "hw/boards.h" + +#define T true +#define F false + +#define MIN_CPUS 1 /* set the min CPUs supported by the machine as 1 */ +#define MAX_CPUS 512 /* set the max CPUs supported by the machine as 512 */ + +/* + * Used to define the generic 3-level CPU topology hierarchy + * -sockets/cores/threads + */ +#define SMP_CONFIG_GENERIC(ha, a, hb, b, hc, c, hd, d, he, e) \ + { \ + .has_cpus = ha, .cpus = a, \ + .has_sockets = hb, .sockets = b, \ + .has_cores = hc, .cores = c, \ + .has_threads = hd, .threads = d, \ + .has_maxcpus = he, .maxcpus = e, \ + } + +#define CPU_TOPOLOGY_GENERIC(a, b, c, d, e) \ + { \ + .cpus = a, \ + .sockets = b, \ + .cores = c, \ + .threads = d, \ + .max_cpus = e, \ + } + +/* + * Currently a 4-level topology hierarchy is supported on PC machines + * -sockets/dies/cores/threads + */ +#define SMP_CONFIG_WITH_DIES(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \ + { \ + .has_cpus = ha, .cpus = a, \ + .has_sockets = hb, .sockets = b, \ + .has_dies = hc, .dies = c, \ + .has_cores = hd, .cores = d, \ + .has_threads = he, .threads = e, \ + .has_maxcpus = hf, .maxcpus = f, \ + } + +/** + * @config - the given SMP configuration + * @expect_prefer_sockets - the expected parsing result for the + * valid configuration, when sockets are preferred over cores + * @expect_prefer_cores - the expected parsing result for the + * valid configuration, when cores are preferred over sockets + * @expect_error - the expected error report when the given + * configuration is invalid + */ +typedef struct SMPTestData { + SMPConfiguration config; + CpuTopology expect_prefer_sockets; + CpuTopology expect_prefer_cores; + const char *expect_error; +} SMPTestData; + +/* Type info of the tested machine */ +static const TypeInfo smp_machine_info = { + .name = TYPE_MACHINE, + .parent = TYPE_OBJECT, + .class_size = sizeof(MachineClass), + .instance_size = sizeof(MachineState), +}; + +/* + * List all the possible valid sub-collections of the generic 5 + * topology parameters (i.e. cpus/maxcpus/sockets/cores/threads), + * then test the automatic calculation algorithm of the missing + * values in the parser. + */ +static struct SMPTestData data_generic_valid[] = { + { + /* config: no configuration provided + * expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */ + .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1), + }, { + /* config: -smp 8 + * prefer_sockets: cpus=8,sockets=8,cores=1,threads=1,maxcpus=8 + * prefer_cores: cpus=8,sockets=1,cores=8,threads=1,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 1, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 8, 1, 8), + }, { + /* config: -smp sockets=2 + * expect: cpus=2,sockets=2,cores=1,threads=1,maxcpus=2 */ + .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2), + }, { + /* config: -smp cores=4 + * expect: cpus=4,sockets=1,cores=4,threads=1,maxcpus=4 */ + .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4), + }, { + /* config: -smp threads=2 + * expect: cpus=2,sockets=1,cores=1,threads=2,maxcpus=2 */ + .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2), + }, { + /* config: -smp maxcpus=16 + * prefer_sockets: cpus=16,sockets=16,cores=1,threads=1,maxcpus=16 + * prefer_cores: cpus=16,sockets=1,cores=16,threads=1,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 16, 1, 1, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 1, 16, 1, 16), + }, { + /* config: -smp 8,sockets=2 + * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + }, { + /* config: -smp 8,cores=4 + * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + }, { + /* config: -smp 8,threads=2 + * prefer_sockets: cpus=8,sockets=4,cores=1,threads=2,maxcpus=8 + * prefer_cores: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 1, 2, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8), + }, { + /* config: -smp 8,maxcpus=16 + * prefer_sockets: cpus=8,sockets=16,cores=1,threads=1,maxcpus=16 + * prefer_cores: cpus=8,sockets=1,cores=16,threads=1,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 16, 1, 1, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 16, 1, 16), + }, { + /* config: -smp sockets=2,cores=4 + * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + }, { + /* config: -smp sockets=2,threads=2 + * expect: cpus=4,sockets=2,cores=1,threads=2,maxcpus=4 */ + .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4), + }, { + /* config: -smp sockets=2,maxcpus=16 + * expect: cpus=16,sockets=2,cores=8,threads=1,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16), + }, { + /* config: -smp cores=4,threads=2 + * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8), + }, { + /* config: -smp cores=4,maxcpus=16 + * expect: cpus=16,sockets=4,cores=4,threads=1,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16), + }, { + /* config: -smp threads=2,maxcpus=16 + * prefer_sockets: cpus=16,sockets=8,cores=1,threads=2,maxcpus=16 + * prefer_cores: cpus=16,sockets=1,cores=8,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 8, 1, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 1, 8, 2, 16), + }, { + /* config: -smp 8,sockets=2,cores=4 + * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + }, { + /* config: -smp 8,sockets=2,threads=2 + * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), + }, { + /* config: -smp 8,sockets=2,maxcpus=16 + * expect: cpus=8,sockets=2,cores=8,threads=1,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16), + }, { + /* config: -smp 8,cores=4,threads=2 + * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8), + }, { + /* config: -smp 8,cores=4,maxcpus=16 + * expect: cpus=8,sockets=4,cores=4,threads=1,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16), + }, { + /* config: -smp 8,threads=2,maxcpus=16 + * prefer_sockets: cpus=8,sockets=8,cores=1,threads=2,maxcpus=16 + * prefer_cores: cpus=8,sockets=1,cores=8,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 8, 2, 16), + }, { + /* config: -smp sockets=2,cores=4,threads=2 + * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + }, { + /* config: -smp sockets=2,cores=4,maxcpus=16 + * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + }, { + /* config: -smp sockets=2,threads=2,maxcpus=16 + * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + }, { + /* config: -smp cores=4,threads=2,maxcpus=16 + * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + }, { + /* config: -smp 8,sockets=2,cores=4,threads=1 + * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 1, F, 0), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8), + }, { + /* config: -smp 8,sockets=2,cores=4,maxcpus=16 + * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + }, { + /* config: -smp 8,sockets=2,threads=2,maxcpus=16 + * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + }, { + /* config: -smp 8,cores=4,threads=2,maxcpus=16 + * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + }, { + /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16 + * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16), + }, { + /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16 + * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + }, +}; + +static struct SMPTestData data_generic_invalid[] = { + { + /* config: -smp 2,dies=2 */ + .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), + .expect_error = "dies not supported by this machine's CPU topology", + }, { + /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */ + .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "sockets (2) * cores (4) * threads (2) " + "!= maxcpus (8)", + }, { + /* config: -smp 18,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_GENERIC(T, 18, T, 2, T, 4, T, 2, T, 16), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "sockets (2) * cores (4) * threads (2) " + "== maxcpus (16) < smp_cpus (18)", + }, { + /* config: -smp 1 + * should tweak the supported min CPUs to 2 for testing */ + .config = SMP_CONFIG_GENERIC(T, 1, F, 0, F, 0, F, 0, F, 0), + .expect_error = "Invalid SMP CPUs 1. The min CPUs supported " + "by machine '(null)' is 2", + }, { + /* config: -smp 512 + * should tweak the supported max CPUs to 511 for testing */ + .config = SMP_CONFIG_GENERIC(T, 512, F, 0, F, 0, F, 0, F, 0), + .expect_error = "Invalid SMP CPUs 512. The max CPUs supported " + "by machine '(null)' is 511", + }, +}; + +static struct SMPTestData data_with_dies_invalid[] = { + { + /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "sockets (2) * dies (2) * cores (4) * threads (2) " + "!= maxcpus (16)", + }, { + /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */ + .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "sockets (2) * dies (2) * cores (4) * threads (2) " + "== maxcpus (32) < smp_cpus (34)", + }, +}; + +static char *smp_config_to_string(SMPConfiguration *config) +{ + return g_strdup_printf( + "(SMPConfiguration) {\n" + " .has_cpus = %5s, cpus = %" PRId64 ",\n" + " .has_sockets = %5s, sockets = %" PRId64 ",\n" + " .has_dies = %5s, dies = %" PRId64 ",\n" + " .has_cores = %5s, cores = %" PRId64 ",\n" + " .has_threads = %5s, threads = %" PRId64 ",\n" + " .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n" + "}", + config->has_cpus ? "true" : "false", config->cpus, + config->has_sockets ? "true" : "false", config->sockets, + config->has_dies ? "true" : "false", config->dies, + config->has_cores ? "true" : "false", config->cores, + config->has_threads ? "true" : "false", config->threads, + config->has_maxcpus ? "true" : "false", config->maxcpus); +} + +static char *cpu_topology_to_string(CpuTopology *topo) +{ + return g_strdup_printf( + "(CpuTopology) {\n" + " .cpus = %u,\n" + " .sockets = %u,\n" + " .dies = %u,\n" + " .cores = %u,\n" + " .threads = %u,\n" + " .max_cpus = %u,\n" + "}", + topo->cpus, topo->sockets, topo->dies, + topo->cores, topo->threads, topo->max_cpus); +} + +static void check_parse(MachineState *ms, SMPConfiguration *config, + CpuTopology *expect_topo, const char *expect_err, + bool is_valid) +{ + g_autofree char *config_str = smp_config_to_string(config); + g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo); + g_autofree char *output_topo_str = NULL; + Error *err = NULL; + + /* call the generic parser smp_parse() */ + smp_parse(ms, config, &err); + + output_topo_str = cpu_topology_to_string(&ms->smp); + + /* when the configuration is supposed to be valid */ + if (is_valid) { + if ((err == NULL) && + (ms->smp.cpus == expect_topo->cpus) && + (ms->smp.sockets == expect_topo->sockets) && + (ms->smp.dies == expect_topo->dies) && + (ms->smp.cores == expect_topo->cores) && + (ms->smp.threads == expect_topo->threads) && + (ms->smp.max_cpus == expect_topo->max_cpus)) { + return; + } + + if (err != NULL) { + g_printerr("Test smp_parse failed!\n" + "Input configuration: %s\n" + "Should be valid: yes\n" + "Expected topology: %s\n\n" + "Result is valid: no\n" + "Output error report: %s\n", + config_str, expect_topo_str, error_get_pretty(err)); + goto end; + } + + g_printerr("Test smp_parse failed!\n" + "Input configuration: %s\n" + "Should be valid: yes\n" + "Expected topology: %s\n\n" + "Result is valid: yes\n" + "Output topology: %s\n", + config_str, expect_topo_str, output_topo_str); + goto end; + } + + /* when the configuration is supposed to be invalid */ + if (err != NULL) { + if (expect_err == NULL || + g_str_equal(expect_err, error_get_pretty(err))) { + error_free(err); + return; + } + + g_printerr("Test smp_parse failed!\n" + "Input configuration: %s\n" + "Should be valid: no\n" + "Expected error report: %s\n\n" + "Result is valid: no\n" + "Output error report: %s\n", + config_str, expect_err, error_get_pretty(err)); + goto end; + } + + g_printerr("Test smp_parse failed!\n" + "Input configuration: %s\n" + "Should be valid: no\n" + "Expected error report: %s\n\n" + "Result is valid: yes\n" + "Output topology: %s\n", + config_str, expect_err, output_topo_str); + +end: + if (err != NULL) { + error_free(err); + } + + abort(); +} + +static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + + mc->smp_props.prefer_sockets = true; + check_parse(ms, &data->config, &data->expect_prefer_sockets, + data->expect_error, is_valid); + + mc->smp_props.prefer_sockets = false; + check_parse(ms, &data->config, &data->expect_prefer_cores, + data->expect_error, is_valid); +} + +/* The parsed results of the unsupported parameters should be 1 */ +static void unsupported_params_init(MachineClass *mc, SMPTestData *data) +{ + if (!mc->smp_props.dies_supported) { + data->expect_prefer_sockets.dies = 1; + data->expect_prefer_cores.dies = 1; + } +} + +/* Reset the related machine properties before each sub-test */ +static void smp_machine_class_init(MachineClass *mc) +{ + mc->min_cpus = MIN_CPUS; + mc->max_cpus = MAX_CPUS; + + mc->smp_props.prefer_sockets = true; + mc->smp_props.dies_supported = false; +} + +static void test_generic(void) +{ + Object *obj = object_new(TYPE_MACHINE); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData *data = &(SMPTestData){{ }}; + int i; + + smp_machine_class_init(mc); + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + *data = data_generic_valid[i]; + unsupported_params_init(mc, data); + + smp_parse_test(ms, data, true); + + /* Unsupported parameters can be provided with their values as 1 */ + data->config.has_dies = true; + data->config.dies = 1; + smp_parse_test(ms, data, true); + } + + /* Reset the supported min CPUs and max CPUs */ + mc->min_cpus = 2; + mc->max_cpus = 511; + + for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) { + *data = data_generic_invalid[i]; + unsupported_params_init(mc, data); + + smp_parse_test(ms, data, false); + } + + object_unref(obj); +} + +static void test_with_dies(void) +{ + Object *obj = object_new(TYPE_MACHINE); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData *data = &(SMPTestData){{ }}; + unsigned int num_dies = 2; + int i; + + smp_machine_class_init(mc); + mc->smp_props.dies_supported = true; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + *data = data_generic_valid[i]; + unsupported_params_init(mc, data); + + /* when dies parameter is omitted, it will be set as 1 */ + data->expect_prefer_sockets.dies = 1; + data->expect_prefer_cores.dies = 1; + + smp_parse_test(ms, data, true); + + /* when dies parameter is specified */ + data->config.has_dies = true; + data->config.dies = num_dies; + if (data->config.has_cpus) { + data->config.cpus *= num_dies; + } + if (data->config.has_maxcpus) { + data->config.maxcpus *= num_dies; + } + + data->expect_prefer_sockets.dies = num_dies; + data->expect_prefer_sockets.cpus *= num_dies; + data->expect_prefer_sockets.max_cpus *= num_dies; + data->expect_prefer_cores.dies = num_dies; + data->expect_prefer_cores.cpus *= num_dies; + data->expect_prefer_cores.max_cpus *= num_dies; + + smp_parse_test(ms, data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) { + *data = data_with_dies_invalid[i]; + unsupported_params_init(mc, data); + + smp_parse_test(ms, data, false); + } + + object_unref(obj); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + type_register_static(&smp_machine_info); + + g_test_add_func("/test-smp-parse/generic", test_generic); + g_test_add_func("/test-smp-parse/with_dies", test_with_dies); + + g_test_run(); + + return 0; +} From b5fdf4102fa3ecc35e7b84a398ccb3e00ca08944 Mon Sep 17 00:00:00 2001 From: Damien Hedde Date: Fri, 29 Oct 2021 16:22:56 +0200 Subject: [PATCH 0938/1334] machine: add device_type_is_dynamic_sysbus function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now the allowance check for adding a sysbus device using -device cli option (or device_add qmp command) is done well after the device has been created. It is done during the machine init done notifier: machine_init_notify() in hw/core/machine.c This new function will allow us to do the check at the right time and issue an error if it fails. Also make device_is_dynamic_sysbus() use the new function. Signed-off-by: Damien Hedde Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Acked-by: Eduardo Habkost Message-Id: <20211029142258.484907-2-damien.hedde@greensocs.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine.c | 13 ++++++++++--- include/hw/boards.h | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index dc15f5f9e5..7c4004ac5a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -548,18 +548,25 @@ void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type) bool device_is_dynamic_sysbus(MachineClass *mc, DeviceState *dev) { - bool allowed = false; - strList *wl; Object *obj = OBJECT(dev); if (!object_dynamic_cast(obj, TYPE_SYS_BUS_DEVICE)) { return false; } + return device_type_is_dynamic_sysbus(mc, object_get_typename(obj)); +} + +bool device_type_is_dynamic_sysbus(MachineClass *mc, const char *type) +{ + bool allowed = false; + strList *wl; + ObjectClass *klass = object_class_by_name(type); + for (wl = mc->allowed_dynamic_sysbus_devices; !allowed && wl; wl = wl->next) { - allowed |= !!object_dynamic_cast(obj, wl->value); + allowed |= !!object_class_dynamic_cast(klass, wl->value); } return allowed; diff --git a/include/hw/boards.h b/include/hw/boards.h index e36fc7d861..602993bd5a 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -52,6 +52,21 @@ void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp); */ void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type); +/** + * device_type_is_dynamic_sysbus: Check if type is an allowed sysbus device + * type for the machine class. + * @mc: Machine class + * @type: type to check (should be a subtype of TYPE_SYS_BUS_DEVICE) + * + * Returns: true if @type is a type in the machine's list of + * dynamically pluggable sysbus devices; otherwise false. + * + * Check if the QOM type @type is in the list of allowed sysbus device + * types (see machine_class_allowed_dynamic_sysbus_dev()). + * Note that if @type has a parent type in the list, it is allowed too. + */ +bool device_type_is_dynamic_sysbus(MachineClass *mc, const char *type); + /** * device_is_dynamic_sysbus: test whether device is a dynamic sysbus device * @mc: Machine class From 312e1b1f3e8400cbba9fb59a57729768e72003ee Mon Sep 17 00:00:00 2001 From: Damien Hedde Date: Fri, 29 Oct 2021 16:22:57 +0200 Subject: [PATCH 0939/1334] qdev-monitor: Check sysbus device type before creating it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an early check to test if the requested sysbus device type is allowed by the current machine before creating the device. This impacts both -device cli option and device_add qmp command. Before this patch, the check was done well after the device has been created (in a machine init done notifier). We can now report the error right away. Signed-off-by: Damien Hedde Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Acked-by: Eduardo Habkost Message-Id: <20211029142258.484907-3-damien.hedde@greensocs.com> Signed-off-by: Philippe Mathieu-Daudé --- softmmu/qdev-monitor.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 4851de51a5..e49d9773d2 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -42,6 +42,7 @@ #include "qemu/cutils.h" #include "hw/qdev-properties.h" #include "hw/clock.h" +#include "hw/boards.h" /* * Aliases were a bad idea from the start. Let's keep them @@ -254,6 +255,16 @@ static DeviceClass *qdev_get_device_class(const char **driver, Error **errp) return NULL; } + if (object_class_dynamic_cast(oc, TYPE_SYS_BUS_DEVICE)) { + /* sysbus devices need to be allowed by the machine */ + MachineClass *mc = MACHINE_CLASS(object_get_class(qdev_get_machine())); + if (!device_type_is_dynamic_sysbus(mc, *driver)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver", + "a dynamic sysbus device type for the machine"); + return NULL; + } + } + return dc; } From db78a6055994d20350a4c2131d67aeec303a7931 Mon Sep 17 00:00:00 2001 From: Damien Hedde Date: Fri, 29 Oct 2021 16:22:58 +0200 Subject: [PATCH 0940/1334] machine: remove the done notifier for dynamic sysbus device type check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we check sysbus device types during device creation, we can remove the check in the machine init done notifier. This was the only thing done by this notifier, so we remove the whole sysbus_notifier structure of the MachineState. Note: This notifier was checking all /peripheral and /peripheral-anon sysbus devices. Now we only check those added by -device cli option or device_add qmp command when handling the command/option. So if there are some devices added in one of these containers manually (eg in machine C code), these will not be checked anymore. This use case does not seem to appear apart from hw/xen/xen-legacy-backend.c (it uses qdev_set_id() and in this case, not for a sysbus device, so it's ok). Signed-off-by: Damien Hedde Acked-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Acked-by: Eduardo Habkost Message-Id: <20211029142258.484907-4-damien.hedde@greensocs.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine.c | 27 --------------------------- include/hw/boards.h | 1 - 2 files changed, 28 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 7c4004ac5a..e24e3e27db 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -572,18 +572,6 @@ bool device_type_is_dynamic_sysbus(MachineClass *mc, const char *type) return allowed; } -static void validate_sysbus_device(SysBusDevice *sbdev, void *opaque) -{ - MachineState *machine = opaque; - MachineClass *mc = MACHINE_GET_CLASS(machine); - - if (!device_is_dynamic_sysbus(mc, DEVICE(sbdev))) { - error_report("Option '-device %s' cannot be handled by this machine", - object_class_get_name(object_get_class(OBJECT(sbdev)))); - exit(1); - } -} - static char *machine_get_memdev(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -599,17 +587,6 @@ static void machine_set_memdev(Object *obj, const char *value, Error **errp) ms->ram_memdev_id = g_strdup(value); } -static void machine_init_notify(Notifier *notifier, void *data) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - - /* - * Loop through all dynamically created sysbus devices and check if they are - * all allowed. If a device is not allowed, error out. - */ - foreach_dynamic_sysbus_device(validate_sysbus_device, machine); -} - HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine) { int i; @@ -949,10 +926,6 @@ static void machine_initfn(Object *obj) "Table (HMAT)"); } - /* Register notifier when init is done for sysbus sanity checks */ - ms->sysbus_notifier.notify = machine_init_notify; - qemu_add_machine_init_done_notifier(&ms->sysbus_notifier); - /* default to mc->default_cpus */ ms->smp.cpus = mc->default_cpus; ms->smp.max_cpus = mc->default_cpus; diff --git a/include/hw/boards.h b/include/hw/boards.h index 602993bd5a..9c1c190104 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -317,7 +317,6 @@ typedef struct CpuTopology { struct MachineState { /*< private >*/ Object parent_obj; - Notifier sysbus_notifier; /*< public >*/ From 97a5b35c17f4bcddc7b550ac1f4d7e39f100aec1 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Mon, 1 Nov 2021 20:38:33 +0000 Subject: [PATCH 0941/1334] roms/openbios: update OpenBIOS images to b9062dea built from submodule Signed-off-by: Mark Cave-Ayland --- pc-bios/openbios-ppc | Bin 696912 -> 696912 bytes pc-bios/openbios-sparc32 | Bin 382048 -> 382048 bytes pc-bios/openbios-sparc64 | Bin 1593408 -> 1593408 bytes roms/openbios | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index 91a73db9a3b3a79d2bad6433bd73eecaac3c948f..67f32a8602e368a2485ec463cf6c8a96da77b46d 100644 GIT binary patch delta 105 zcmca`MC-y4t%fa(`?oSOPCu}fF&sk4x0`Qc+-|;&N%Mq&Uw)Z_p@NZt5s0udHft}a jW8Pj+$C40)DfQq2OZ$ThtU$~L#Oy%KvHigX&Vq{o5>_a; delta 105 zcmca`MC-y4t%fa(`?oSOO+T=eF&sk4x0`Qc+-|;&N%Mq2aB6{qg@TcRk)eWtrInF! mdqExZ_JTT=gdj|*2Nzh{A6#GsVm2UV2V#!x4=!*PTm%3TSSYsu diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index a5b738919119f7dde4e83ceb483fe2aa8a32f5ea..376b01c10b5ecc6c52f6f7f97866b54b3d2a3f3a 100644 GIT binary patch delta 48 ycmaE`Lj1uB@rD-0Eldv90)F{r3Wf?s21X#l%Gk6$z?un&nSq#Pdw?~oz)S#w`VK(= delta 48 zcmaE`Lj1uB@rD-0Eldv90>P;T3Kj}R21bSo29{PvM(qLCOhC*G#4OtbtXTzS0sw-y B4nP0^ diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index f7a501efc6a3917c29ed880d4236db8e983c7c0c..bbd746fde9494bd776ece55951974f393383e1b8 100644 GIT binary patch delta 243 zcmX@GAo0M0#D*5e7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#EdqyYn4djLntrTC zKo!U+n*I*LNZKw|D=?J{B)5HCyMQetnDwqh;A=5Uotwzy?a%)RFtQ8y<(DZKDi|3U zfe0&Ov+X~a1-V5*YTMZ)1-G+F3Y`sQWtXpOR+xSvUr22GyF4LRCM1FBeuYAJnSlzn x>lF!oWe3SLLX=Los}hO@%gn11DrH2HY1gX}0%Bnx76D>WAQs!MS0gTT1pps)Rx1Di delta 243 zcmX@GAo0M0#D*5e7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#EdqyYn7K-frXQ;j zPz5raroV$QjJAu_3QXk!$!%ZPE?~!COTMj diff --git a/roms/openbios b/roms/openbios index d657b65318..b9062deaae 160000 --- a/roms/openbios +++ b/roms/openbios @@ -1 +1 @@ -Subproject commit d657b653186c0fd6e062cab133497415c2a5a5b8 +Subproject commit b9062deaaea7269369eaa46260d75edcaf276afa From 7786ae40ba4e7d5b9ebbfd07924f4379e218c6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=C3=A9=C2=BB=E2=80=9E=C3=A5=E2=80=B9?= =?UTF-8?q?=E2=80=A1=29?= Date: Tue, 29 Jun 2021 16:01:18 +0000 Subject: [PATCH 0942/1334] KVM: introduce dirty_pages and kvm_dirty_ring_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dirty_pages is used to calculate dirtyrate via dirty ring, when enabled, kvm-reaper will increase the dirty pages after gfns being dirtied. kvm_dirty_ring_enabled shows if kvm-reaper is working. dirtyrate thread could use it to check if measurement can base on dirty ring feature. Signed-off-by: Hyman Huang(黄勇) Message-Id: Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- accel/kvm/kvm-all.c | 7 +++++++ accel/stubs/kvm-stub.c | 5 +++++ include/hw/core/cpu.h | 1 + include/sysemu/kvm.h | 1 + 4 files changed, 14 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index db8d83b137..eecd8031cf 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -469,6 +469,7 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) cpu->kvm_fd = ret; cpu->kvm_state = s; cpu->vcpu_dirty = true; + cpu->dirty_pages = 0; mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { @@ -743,6 +744,7 @@ static uint32_t kvm_dirty_ring_reap_one(KVMState *s, CPUState *cpu) count++; } cpu->kvm_fetch_index = fetch; + cpu->dirty_pages += count; return count; } @@ -2296,6 +2298,11 @@ bool kvm_vcpu_id_is_valid(int vcpu_id) return vcpu_id >= 0 && vcpu_id < kvm_max_vcpu_id(s); } +bool kvm_dirty_ring_enabled(void) +{ + return kvm_state->kvm_dirty_ring_size ? true : false; +} + static int kvm_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 5b1d00a222..5319573e00 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -147,4 +147,9 @@ bool kvm_arm_supports_user_irq(void) { return false; } + +bool kvm_dirty_ring_enabled(void) +{ + return false; +} #endif diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 1a10497af3..e948e81f1a 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -381,6 +381,7 @@ struct CPUState { struct kvm_run *kvm_run; struct kvm_dirty_gfn *kvm_dirty_gfns; uint32_t kvm_fetch_index; + uint64_t dirty_pages; /* Used for events with 'vcpu' and *without* the 'disabled' properties */ DECLARE_BITMAP(trace_dstate_delayed, CPU_TRACE_DSTATE_MAX_EVENTS); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index a1ab1ee12d..7b22aeb6ae 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -547,4 +547,5 @@ bool kvm_cpu_check_are_resettable(void); bool kvm_arch_cpu_check_are_resettable(void); +bool kvm_dirty_ring_enabled(void); #endif From 63b41db4bc776e71384d14d68a8ec6e0aae5ea3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=C3=A9=C2=BB=E2=80=9E=C3=A5=E2=80=B9?= =?UTF-8?q?=E2=80=A1=29?= Date: Tue, 29 Jun 2021 16:01:19 +0000 Subject: [PATCH 0943/1334] memory: make global_dirty_tracking a bitmask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit since dirty ring has been introduced, there are two methods to track dirty pages of vm. it seems that "logging" has a hint on the method, so rename the global_dirty_log to global_dirty_tracking would make description more accurate. dirty rate measurement may start or stop dirty tracking during calculation. this conflict with migration because stop dirty tracking make migration leave dirty pages out then that'll be a problem. make global_dirty_tracking a bitmask can let both migration and dirty rate measurement work fine. introduce GLOBAL_DIRTY_MIGRATION and GLOBAL_DIRTY_DIRTY_RATE to distinguish what current dirty tracking aims for, migration or dirty rate. Signed-off-by: Hyman Huang(黄勇) Message-Id: <9c9388657cfa0301bd2c1cfa36e7cf6da4aeca19.1624040308.git.huangy81@chinatelecom.cn> Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- hw/i386/xen/xen-hvm.c | 4 ++-- include/exec/memory.h | 20 +++++++++++++++++--- include/exec/ram_addr.h | 4 ++-- migration/ram.c | 15 +++++++++++---- softmmu/memory.c | 32 +++++++++++++++++++++----------- softmmu/trace-events | 1 + 6 files changed, 54 insertions(+), 22 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index e3d3d5cf89..482be95415 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1613,8 +1613,8 @@ void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) void qmp_xen_set_global_dirty_log(bool enable, Error **errp) { if (enable) { - memory_global_dirty_log_start(); + memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); } else { - memory_global_dirty_log_stop(); + memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); } } diff --git a/include/exec/memory.h b/include/exec/memory.h index a185b6dcb8..04280450c9 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -61,7 +61,17 @@ static inline void fuzz_dma_read_cb(size_t addr, } #endif -extern bool global_dirty_log; +/* Possible bits for global_dirty_log_{start|stop} */ + +/* Dirty tracking enabled because migration is running */ +#define GLOBAL_DIRTY_MIGRATION (1U << 0) + +/* Dirty tracking enabled because measuring dirty rate */ +#define GLOBAL_DIRTY_DIRTY_RATE (1U << 1) + +#define GLOBAL_DIRTY_MASK (0x3) + +extern unsigned int global_dirty_tracking; typedef struct MemoryRegionOps MemoryRegionOps; @@ -2388,13 +2398,17 @@ void memory_listener_unregister(MemoryListener *listener); /** * memory_global_dirty_log_start: begin dirty logging for all regions + * + * @flags: purpose of starting dirty log, migration or dirty rate */ -void memory_global_dirty_log_start(void); +void memory_global_dirty_log_start(unsigned int flags); /** * memory_global_dirty_log_stop: end dirty logging for all regions + * + * @flags: purpose of stopping dirty log, migration or dirty rate */ -void memory_global_dirty_log_stop(void); +void memory_global_dirty_log_stop(unsigned int flags); void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled); diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 551876bed0..45c913264a 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -369,7 +369,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, qatomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); - if (global_dirty_log) { + if (global_dirty_tracking) { qatomic_or( &blocks[DIRTY_MEMORY_MIGRATION][idx][offset], temp); @@ -392,7 +392,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } else { uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; - if (!global_dirty_log) { + if (!global_dirty_tracking) { clients &= ~(1 << DIRTY_MEMORY_MIGRATION); } diff --git a/migration/ram.c b/migration/ram.c index bb908822d5..ae2601bf3b 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2216,7 +2216,14 @@ static void ram_save_cleanup(void *opaque) /* caller have hold iothread lock or is in a bh, so there is * no writing race against the migration bitmap */ - memory_global_dirty_log_stop(); + if (global_dirty_tracking & GLOBAL_DIRTY_MIGRATION) { + /* + * do not stop dirty log without starting it, since + * memory_global_dirty_log_stop will assert that + * memory_global_dirty_log_start/stop used in pairs + */ + memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); + } } RAMBLOCK_FOREACH_NOT_IGNORED(block) { @@ -2678,7 +2685,7 @@ static void ram_init_bitmaps(RAMState *rs) ram_list_init_bitmaps(); /* We don't use dirty log with background snapshots */ if (!migrate_background_snapshot()) { - memory_global_dirty_log_start(); + memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); migration_bitmap_sync_precopy(rs); } } @@ -3434,7 +3441,7 @@ void colo_incoming_start_dirty_log(void) /* Discard this dirty bitmap record */ bitmap_zero(block->bmap, block->max_length >> TARGET_PAGE_BITS); } - memory_global_dirty_log_start(); + memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); } ram_state->migration_dirty_pages = 0; qemu_mutex_unlock_ramlist(); @@ -3446,7 +3453,7 @@ void colo_release_ram_cache(void) { RAMBlock *block; - memory_global_dirty_log_stop(); + memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); RAMBLOCK_FOREACH_NOT_IGNORED(block) { g_free(block->bmap); block->bmap = NULL; diff --git a/softmmu/memory.c b/softmmu/memory.c index e5826faa0c..f2ac0d2e89 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -39,7 +39,7 @@ static unsigned memory_region_transaction_depth; static bool memory_region_update_pending; static bool ioeventfd_update_pending; -bool global_dirty_log; +unsigned int global_dirty_tracking; static QTAILQ_HEAD(, MemoryListener) memory_listeners = QTAILQ_HEAD_INITIALIZER(memory_listeners); @@ -1821,7 +1821,7 @@ uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr) uint8_t mask = mr->dirty_log_mask; RAMBlock *rb = mr->ram_block; - if (global_dirty_log && ((rb && qemu_ram_is_migratable(rb)) || + if (global_dirty_tracking && ((rb && qemu_ram_is_migratable(rb)) || memory_region_is_iommu(mr))) { mask |= (1 << DIRTY_MEMORY_MIGRATION); } @@ -2760,14 +2760,18 @@ void memory_global_after_dirty_log_sync(void) static VMChangeStateEntry *vmstate_change; -void memory_global_dirty_log_start(void) +void memory_global_dirty_log_start(unsigned int flags) { if (vmstate_change) { qemu_del_vm_change_state_handler(vmstate_change); vmstate_change = NULL; } - global_dirty_log = true; + assert(flags && !(flags & (~GLOBAL_DIRTY_MASK))); + assert(!(global_dirty_tracking & flags)); + global_dirty_tracking |= flags; + + trace_global_dirty_changed(global_dirty_tracking); MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward); @@ -2777,9 +2781,13 @@ void memory_global_dirty_log_start(void) memory_region_transaction_commit(); } -static void memory_global_dirty_log_do_stop(void) +static void memory_global_dirty_log_do_stop(unsigned int flags) { - global_dirty_log = false; + assert(flags && !(flags & (~GLOBAL_DIRTY_MASK))); + assert((global_dirty_tracking & flags) == flags); + global_dirty_tracking &= ~flags; + + trace_global_dirty_changed(global_dirty_tracking); /* Refresh DIRTY_MEMORY_MIGRATION bit. */ memory_region_transaction_begin(); @@ -2792,8 +2800,9 @@ static void memory_global_dirty_log_do_stop(void) static void memory_vm_change_state_handler(void *opaque, bool running, RunState state) { + unsigned int flags = (unsigned int)(uintptr_t)opaque; if (running) { - memory_global_dirty_log_do_stop(); + memory_global_dirty_log_do_stop(flags); if (vmstate_change) { qemu_del_vm_change_state_handler(vmstate_change); @@ -2802,18 +2811,19 @@ static void memory_vm_change_state_handler(void *opaque, bool running, } } -void memory_global_dirty_log_stop(void) +void memory_global_dirty_log_stop(unsigned int flags) { if (!runstate_is_running()) { if (vmstate_change) { return; } vmstate_change = qemu_add_vm_change_state_handler( - memory_vm_change_state_handler, NULL); + memory_vm_change_state_handler, + (void *)(uintptr_t)flags); return; } - memory_global_dirty_log_do_stop(); + memory_global_dirty_log_do_stop(flags); } static void listener_add_address_space(MemoryListener *listener, @@ -2825,7 +2835,7 @@ static void listener_add_address_space(MemoryListener *listener, if (listener->begin) { listener->begin(listener); } - if (global_dirty_log) { + if (global_dirty_tracking) { if (listener->log_global_start) { listener->log_global_start(listener); } diff --git a/softmmu/trace-events b/softmmu/trace-events index bf1469990e..9c88887b3c 100644 --- a/softmmu/trace-events +++ b/softmmu/trace-events @@ -19,6 +19,7 @@ memory_region_sync_dirty(const char *mr, const char *listener, int global) "mr ' flatview_new(void *view, void *root) "%p (root %p)" flatview_destroy(void *view, void *root) "%p (root %p)" flatview_destroy_rcu(void *view, void *root) "%p (root %p)" +global_dirty_changed(unsigned int bitmask) "bitmask 0x%"PRIx32 # softmmu.c vm_stop_flush_all(int ret) "ret %d" From 71864eadd9a9933ef7e9460ae2fc93b3af98a181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=C3=A9=C2=BB=E2=80=9E=C3=A5=E2=80=B9?= =?UTF-8?q?=E2=80=A1=29?= Date: Tue, 29 Jun 2021 16:01:20 +0000 Subject: [PATCH 0944/1334] migration/dirtyrate: introduce struct and adjust DirtyRateStat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit introduce "DirtyRateMeasureMode" to specify what method should be used to calculate dirty rate, introduce "DirtyRateVcpu" to store dirty rate for each vcpu. use union to store stat data of specific mode Signed-off-by: Hyman Huang(黄勇) Message-Id: <661c98c40f40e163aa58334337af8f3ddf41316a.1624040308.git.huangy81@chinatelecom.cn> Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/dirtyrate.c | 48 +++++++++++++++++++++++++------------------ migration/dirtyrate.h | 19 ++++++++++++++--- qapi/migration.json | 30 +++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index 320c56ba2c..e0a27a992c 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -88,33 +88,44 @@ static struct DirtyRateInfo *query_dirty_rate_info(void) return info; } -static void init_dirtyrate_stat(int64_t start_time, int64_t calc_time, - uint64_t sample_pages) +static void init_dirtyrate_stat(int64_t start_time, + struct DirtyRateConfig config) { - DirtyStat.total_dirty_samples = 0; - DirtyStat.total_sample_count = 0; - DirtyStat.total_block_mem_MB = 0; DirtyStat.dirty_rate = -1; DirtyStat.start_time = start_time; - DirtyStat.calc_time = calc_time; - DirtyStat.sample_pages = sample_pages; + DirtyStat.calc_time = config.sample_period_seconds; + DirtyStat.sample_pages = config.sample_pages_per_gigabytes; + + switch (config.mode) { + case DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING: + DirtyStat.page_sampling.total_dirty_samples = 0; + DirtyStat.page_sampling.total_sample_count = 0; + DirtyStat.page_sampling.total_block_mem_MB = 0; + break; + case DIRTY_RATE_MEASURE_MODE_DIRTY_RING: + DirtyStat.dirty_ring.nvcpu = -1; + DirtyStat.dirty_ring.rates = NULL; + break; + default: + break; + } } static void update_dirtyrate_stat(struct RamblockDirtyInfo *info) { - DirtyStat.total_dirty_samples += info->sample_dirty_count; - DirtyStat.total_sample_count += info->sample_pages_count; + DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count; + DirtyStat.page_sampling.total_sample_count += info->sample_pages_count; /* size of total pages in MB */ - DirtyStat.total_block_mem_MB += (info->ramblock_pages * - TARGET_PAGE_SIZE) >> 20; + DirtyStat.page_sampling.total_block_mem_MB += (info->ramblock_pages * + TARGET_PAGE_SIZE) >> 20; } static void update_dirtyrate(uint64_t msec) { uint64_t dirtyrate; - uint64_t total_dirty_samples = DirtyStat.total_dirty_samples; - uint64_t total_sample_count = DirtyStat.total_sample_count; - uint64_t total_block_mem_MB = DirtyStat.total_block_mem_MB; + uint64_t total_dirty_samples = DirtyStat.page_sampling.total_dirty_samples; + uint64_t total_sample_count = DirtyStat.page_sampling.total_sample_count; + uint64_t total_block_mem_MB = DirtyStat.page_sampling.total_block_mem_MB; dirtyrate = total_dirty_samples * total_block_mem_MB * 1000 / (total_sample_count * msec); @@ -327,7 +338,7 @@ static bool compare_page_hash_info(struct RamblockDirtyInfo *info, update_dirtyrate_stat(block_dinfo); } - if (DirtyStat.total_sample_count == 0) { + if (DirtyStat.page_sampling.total_sample_count == 0) { return false; } @@ -372,8 +383,6 @@ void *get_dirtyrate_thread(void *arg) struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg; int ret; int64_t start_time; - int64_t calc_time; - uint64_t sample_pages; ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED, DIRTY_RATE_STATUS_MEASURING); @@ -383,9 +392,7 @@ void *get_dirtyrate_thread(void *arg) } start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; - calc_time = config.sample_period_seconds; - sample_pages = config.sample_pages_per_gigabytes; - init_dirtyrate_stat(start_time, calc_time, sample_pages); + init_dirtyrate_stat(start_time, config); calculate_dirtyrate(config); @@ -442,6 +449,7 @@ void qmp_calc_dirty_rate(int64_t calc_time, bool has_sample_pages, config.sample_period_seconds = calc_time; config.sample_pages_per_gigabytes = sample_pages; + config.mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread, (void *)&config, QEMU_THREAD_DETACHED); } diff --git a/migration/dirtyrate.h b/migration/dirtyrate.h index e1fd29089e..69d4c5b865 100644 --- a/migration/dirtyrate.h +++ b/migration/dirtyrate.h @@ -43,6 +43,7 @@ struct DirtyRateConfig { uint64_t sample_pages_per_gigabytes; /* sample pages per GB */ int64_t sample_period_seconds; /* time duration between two sampling */ + DirtyRateMeasureMode mode; /* mode of dirtyrate measurement */ }; /* @@ -58,17 +59,29 @@ struct RamblockDirtyInfo { uint32_t *hash_result; /* array of hash result for sampled pages */ }; +typedef struct SampleVMStat { + uint64_t total_dirty_samples; /* total dirty sampled page */ + uint64_t total_sample_count; /* total sampled pages */ + uint64_t total_block_mem_MB; /* size of total sampled pages in MB */ +} SampleVMStat; + +typedef struct VcpuStat { + int nvcpu; /* number of vcpu */ + DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */ +} VcpuStat; + /* * Store calculation statistics for each measure. */ struct DirtyRateStat { - uint64_t total_dirty_samples; /* total dirty sampled page */ - uint64_t total_sample_count; /* total sampled pages */ - uint64_t total_block_mem_MB; /* size of total sampled pages in MB */ int64_t dirty_rate; /* dirty rate in MB/s */ int64_t start_time; /* calculation start time in units of second */ int64_t calc_time; /* time duration of two sampling in units of second */ uint64_t sample_pages; /* sample pages per GB */ + union { + SampleVMStat page_sampling; + VcpuStat dirty_ring; + }; }; void *get_dirtyrate_thread(void *arg); diff --git a/qapi/migration.json b/qapi/migration.json index 9aa8bc5759..94eece16e1 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1731,6 +1731,21 @@ { 'event': 'UNPLUG_PRIMARY', 'data': { 'device-id': 'str' } } +## +# @DirtyRateVcpu: +# +# Dirty rate of vcpu. +# +# @id: vcpu index. +# +# @dirty-rate: dirty rate. +# +# Since: 6.1 +# +## +{ 'struct': 'DirtyRateVcpu', + 'data': { 'id': 'int', 'dirty-rate': 'int64' } } + ## # @DirtyRateStatus: # @@ -1748,6 +1763,21 @@ { 'enum': 'DirtyRateStatus', 'data': [ 'unstarted', 'measuring', 'measured'] } +## +# @DirtyRateMeasureMode: +# +# An enumeration of mode of measuring dirtyrate. +# +# @page-sampling: calculate dirtyrate by sampling pages. +# +# @dirty-ring: calculate dirtyrate by via dirty ring. +# +# Since: 6.1 +# +## +{ 'enum': 'DirtyRateMeasureMode', + 'data': ['page-sampling', 'dirty-ring'] } + ## # @DirtyRateInfo: # From 15eb2d644cbda3dd2ad5989e273b211e83c16623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=C3=A9=C2=BB=E2=80=9E=C3=A5=E2=80=B9?= =?UTF-8?q?=E2=80=A1=29?= Date: Tue, 29 Jun 2021 16:01:21 +0000 Subject: [PATCH 0945/1334] migration/dirtyrate: adjust order of registering thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit registering get_dirtyrate thread in advance so that both page-sampling and dirty-ring mode can be covered. Signed-off-by: Hyman Huang(黄勇) Message-Id: Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/dirtyrate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index e0a27a992c..a9bdd60034 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -352,7 +352,6 @@ static void calculate_dirtyrate(struct DirtyRateConfig config) int64_t msec = 0; int64_t initial_time; - rcu_register_thread(); rcu_read_lock(); initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); if (!record_ramblock_hash_info(&block_dinfo, config, &block_count)) { @@ -375,7 +374,6 @@ static void calculate_dirtyrate(struct DirtyRateConfig config) out: rcu_read_unlock(); free_ramblock_dirty_info(block_dinfo, block_count); - rcu_unregister_thread(); } void *get_dirtyrate_thread(void *arg) @@ -383,6 +381,7 @@ void *get_dirtyrate_thread(void *arg) struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg; int ret; int64_t start_time; + rcu_register_thread(); ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED, DIRTY_RATE_STATUS_MEASURING); @@ -401,6 +400,8 @@ void *get_dirtyrate_thread(void *arg) if (ret == -1) { error_report("change dirtyrate state failed."); } + + rcu_unregister_thread(); return NULL; } From 9865d0f68fbb4785fcc80d5a28fece8913f4098a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=C3=A9=C2=BB=E2=80=9E=C3=A5=E2=80=B9?= =?UTF-8?q?=E2=80=A1=29?= Date: Tue, 29 Jun 2021 16:01:22 +0000 Subject: [PATCH 0946/1334] migration/dirtyrate: move init step of calculation to main thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit since main thread may "query dirty rate" at any time, it's better to move init step into main thead so that synchronization overhead between "main" and "get_dirtyrate" can be reduced. Signed-off-by: Hyman Huang(黄勇) Message-Id: <109f8077518ed2f13068e3bfb10e625e964780f1.1624040308.git.huangy81@chinatelecom.cn> Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/dirtyrate.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index a9bdd60034..b8f61cc650 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -380,7 +380,6 @@ void *get_dirtyrate_thread(void *arg) { struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg; int ret; - int64_t start_time; rcu_register_thread(); ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED, @@ -390,9 +389,6 @@ void *get_dirtyrate_thread(void *arg) return NULL; } - start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; - init_dirtyrate_stat(start_time, config); - calculate_dirtyrate(config); ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_MEASURING, @@ -411,6 +407,7 @@ void qmp_calc_dirty_rate(int64_t calc_time, bool has_sample_pages, static struct DirtyRateConfig config; QemuThread thread; int ret; + int64_t start_time; /* * If the dirty rate is already being measured, don't attempt to start. @@ -451,6 +448,10 @@ void qmp_calc_dirty_rate(int64_t calc_time, bool has_sample_pages, config.sample_period_seconds = calc_time; config.sample_pages_per_gigabytes = sample_pages; config.mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; + + start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; + init_dirtyrate_stat(start_time, config); + qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread, (void *)&config, QEMU_THREAD_DETACHED); } From 0e21bf24608b801bcdf6057f47808613061ecbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=C3=A9=C2=BB=E2=80=9E=C3=A5=E2=80=B9?= =?UTF-8?q?=E2=80=A1=29?= Date: Tue, 29 Jun 2021 16:01:23 +0000 Subject: [PATCH 0947/1334] migration/dirtyrate: implement dirty-ring dirtyrate calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use dirty ring feature to implement dirtyrate calculation. introduce mode option in qmp calc_dirty_rate to specify what method should be used when calculating dirtyrate, either page-sampling or dirty-ring should be passed. introduce "dirty_ring:-r" option in hmp calc_dirty_rate to indicate dirty ring method should be used for calculation. Signed-off-by: Hyman Huang(黄勇) Message-Id: <7db445109bd18125ce8ec86816d14f6ab5de6a7d.1624040308.git.huangy81@chinatelecom.cn> Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- hmp-commands.hx | 7 +- migration/dirtyrate.c | 208 +++++++++++++++++++++++++++++++++++++++-- migration/trace-events | 2 + qapi/migration.json | 16 +++- 4 files changed, 218 insertions(+), 15 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index cf723c69ac..b6d47bd03f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1737,8 +1737,9 @@ ERST { .name = "calc_dirty_rate", - .args_type = "second:l,sample_pages_per_GB:l?", - .params = "second [sample_pages_per_GB]", - .help = "start a round of guest dirty rate measurement", + .args_type = "dirty_ring:-r,second:l,sample_pages_per_GB:l?", + .params = "[-r] second [sample_pages_per_GB]", + .help = "start a round of guest dirty rate measurement (using -d to" + "\n\t\t\t specify dirty ring as the method of calculation)", .cmd = hmp_calc_dirty_rate, }, diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index b8f61cc650..f92c4b498e 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -16,6 +16,7 @@ #include "cpu.h" #include "exec/ramblock.h" #include "qemu/rcu_queue.h" +#include "qemu/main-loop.h" #include "qapi/qapi-commands-migration.h" #include "ram.h" #include "trace.h" @@ -23,9 +24,19 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/qmp/qdict.h" +#include "sysemu/kvm.h" +#include "sysemu/runstate.h" +#include "exec/memory.h" + +typedef struct DirtyPageRecord { + uint64_t start_pages; + uint64_t end_pages; +} DirtyPageRecord; static int CalculatingState = DIRTY_RATE_STATUS_UNSTARTED; static struct DirtyRateStat DirtyStat; +static DirtyRateMeasureMode dirtyrate_mode = + DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; static int64_t set_sample_page_period(int64_t msec, int64_t initial_time) { @@ -70,18 +81,37 @@ static int dirtyrate_set_state(int *state, int old_state, int new_state) static struct DirtyRateInfo *query_dirty_rate_info(void) { + int i; int64_t dirty_rate = DirtyStat.dirty_rate; struct DirtyRateInfo *info = g_malloc0(sizeof(DirtyRateInfo)); - - if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) { - info->has_dirty_rate = true; - info->dirty_rate = dirty_rate; - } + DirtyRateVcpuList *head = NULL, **tail = &head; info->status = CalculatingState; info->start_time = DirtyStat.start_time; info->calc_time = DirtyStat.calc_time; info->sample_pages = DirtyStat.sample_pages; + info->mode = dirtyrate_mode; + + if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) { + info->has_dirty_rate = true; + info->dirty_rate = dirty_rate; + + if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { + /* + * set sample_pages with 0 to indicate page sampling + * isn't enabled + **/ + info->sample_pages = 0; + info->has_vcpu_dirty_rate = true; + for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) { + DirtyRateVcpu *rate = g_malloc0(sizeof(DirtyRateVcpu)); + rate->id = DirtyStat.dirty_ring.rates[i].id; + rate->dirty_rate = DirtyStat.dirty_ring.rates[i].dirty_rate; + QAPI_LIST_APPEND(tail, rate); + } + info->vcpu_dirty_rate = head; + } + } trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState)); @@ -111,6 +141,15 @@ static void init_dirtyrate_stat(int64_t start_time, } } +static void cleanup_dirtyrate_stat(struct DirtyRateConfig config) +{ + /* last calc-dirty-rate qmp use dirty ring mode */ + if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { + free(DirtyStat.dirty_ring.rates); + DirtyStat.dirty_ring.rates = NULL; + } +} + static void update_dirtyrate_stat(struct RamblockDirtyInfo *info) { DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count; @@ -345,7 +384,97 @@ static bool compare_page_hash_info(struct RamblockDirtyInfo *info, return true; } -static void calculate_dirtyrate(struct DirtyRateConfig config) +static inline void record_dirtypages(DirtyPageRecord *dirty_pages, + CPUState *cpu, bool start) +{ + if (start) { + dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages; + } else { + dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages; + } +} + +static void dirtyrate_global_dirty_log_start(void) +{ + qemu_mutex_lock_iothread(); + memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE); + qemu_mutex_unlock_iothread(); +} + +static void dirtyrate_global_dirty_log_stop(void) +{ + qemu_mutex_lock_iothread(); + memory_global_dirty_log_sync(); + memory_global_dirty_log_stop(GLOBAL_DIRTY_DIRTY_RATE); + qemu_mutex_unlock_iothread(); +} + +static int64_t do_calculate_dirtyrate_vcpu(DirtyPageRecord dirty_pages) +{ + uint64_t memory_size_MB; + int64_t time_s; + uint64_t increased_dirty_pages = + dirty_pages.end_pages - dirty_pages.start_pages; + + memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20; + time_s = DirtyStat.calc_time; + + return memory_size_MB / time_s; +} + +static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config) +{ + CPUState *cpu; + int64_t msec = 0; + int64_t start_time; + uint64_t dirtyrate = 0; + uint64_t dirtyrate_sum = 0; + DirtyPageRecord *dirty_pages; + int nvcpu = 0; + int i = 0; + + CPU_FOREACH(cpu) { + nvcpu++; + } + + dirty_pages = malloc(sizeof(*dirty_pages) * nvcpu); + + DirtyStat.dirty_ring.nvcpu = nvcpu; + DirtyStat.dirty_ring.rates = malloc(sizeof(DirtyRateVcpu) * nvcpu); + + dirtyrate_global_dirty_log_start(); + + CPU_FOREACH(cpu) { + record_dirtypages(dirty_pages, cpu, true); + } + + start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + DirtyStat.start_time = start_time / 1000; + + msec = config.sample_period_seconds * 1000; + msec = set_sample_page_period(msec, start_time); + DirtyStat.calc_time = msec / 1000; + + dirtyrate_global_dirty_log_stop(); + + CPU_FOREACH(cpu) { + record_dirtypages(dirty_pages, cpu, false); + } + + for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) { + dirtyrate = do_calculate_dirtyrate_vcpu(dirty_pages[i]); + trace_dirtyrate_do_calculate_vcpu(i, dirtyrate); + + DirtyStat.dirty_ring.rates[i].id = i; + DirtyStat.dirty_ring.rates[i].dirty_rate = dirtyrate; + dirtyrate_sum += dirtyrate; + } + + DirtyStat.dirty_rate = dirtyrate_sum; + free(dirty_pages); +} + +static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config) { struct RamblockDirtyInfo *block_dinfo = NULL; int block_count = 0; @@ -376,6 +505,17 @@ out: free_ramblock_dirty_info(block_dinfo, block_count); } +static void calculate_dirtyrate(struct DirtyRateConfig config) +{ + if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { + calculate_dirtyrate_dirty_ring(config); + } else { + calculate_dirtyrate_sample_vm(config); + } + + trace_dirtyrate_calculate(DirtyStat.dirty_rate); +} + void *get_dirtyrate_thread(void *arg) { struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg; @@ -401,8 +541,12 @@ void *get_dirtyrate_thread(void *arg) return NULL; } -void qmp_calc_dirty_rate(int64_t calc_time, bool has_sample_pages, - int64_t sample_pages, Error **errp) +void qmp_calc_dirty_rate(int64_t calc_time, + bool has_sample_pages, + int64_t sample_pages, + bool has_mode, + DirtyRateMeasureMode mode, + Error **errp) { static struct DirtyRateConfig config; QemuThread thread; @@ -424,6 +568,15 @@ void qmp_calc_dirty_rate(int64_t calc_time, bool has_sample_pages, return; } + if (!has_mode) { + mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; + } + + if (has_sample_pages && mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { + error_setg(errp, "either sample-pages or dirty-ring can be specified."); + return; + } + if (has_sample_pages) { if (!is_sample_pages_valid(sample_pages)) { error_setg(errp, "sample-pages is out of range[%d, %d].", @@ -435,6 +588,16 @@ void qmp_calc_dirty_rate(int64_t calc_time, bool has_sample_pages, sample_pages = DIRTYRATE_DEFAULT_SAMPLE_PAGES; } + /* + * dirty ring mode only works when kvm dirty ring is enabled. + */ + if ((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) && + !kvm_dirty_ring_enabled()) { + error_setg(errp, "dirty ring is disabled, use sample-pages method " + "or remeasure later."); + return; + } + /* * Init calculation state as unstarted. */ @@ -447,7 +610,15 @@ void qmp_calc_dirty_rate(int64_t calc_time, bool has_sample_pages, config.sample_period_seconds = calc_time; config.sample_pages_per_gigabytes = sample_pages; - config.mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; + config.mode = mode; + + cleanup_dirtyrate_stat(config); + + /* + * update dirty rate mode so that we can figure out what mode has + * been used in last calculation + **/ + dirtyrate_mode = mode; start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; init_dirtyrate_stat(start_time, config); @@ -473,12 +644,24 @@ void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict) info->sample_pages); monitor_printf(mon, "Period: %"PRIi64" (sec)\n", info->calc_time); + monitor_printf(mon, "Mode: %s\n", + DirtyRateMeasureMode_str(info->mode)); monitor_printf(mon, "Dirty rate: "); if (info->has_dirty_rate) { monitor_printf(mon, "%"PRIi64" (MB/s)\n", info->dirty_rate); + if (info->has_vcpu_dirty_rate) { + DirtyRateVcpuList *rate, *head = info->vcpu_dirty_rate; + for (rate = head; rate != NULL; rate = rate->next) { + monitor_printf(mon, "vcpu[%"PRIi64"], Dirty rate: %"PRIi64 + " (MB/s)\n", rate->value->id, + rate->value->dirty_rate); + } + } } else { monitor_printf(mon, "(not ready)\n"); } + + qapi_free_DirtyRateVcpuList(info->vcpu_dirty_rate); g_free(info); } @@ -487,6 +670,10 @@ void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict) int64_t sec = qdict_get_try_int(qdict, "second", 0); int64_t sample_pages = qdict_get_try_int(qdict, "sample_pages_per_GB", -1); bool has_sample_pages = (sample_pages != -1); + bool dirty_ring = qdict_get_try_bool(qdict, "dirty_ring", false); + DirtyRateMeasureMode mode = + (dirty_ring ? DIRTY_RATE_MEASURE_MODE_DIRTY_RING : + DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING); Error *err = NULL; if (!sec) { @@ -494,7 +681,8 @@ void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict) return; } - qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages, &err); + qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages, true, + mode, &err); if (err) { hmp_handle_error(mon, err); return; diff --git a/migration/trace-events b/migration/trace-events index a8ae163707..b48d873b8a 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -333,6 +333,8 @@ get_ramblock_vfn_hash(const char *idstr, uint64_t vfn, uint32_t crc) "ramblock n calc_page_dirty_rate(const char *idstr, uint32_t new_crc, uint32_t old_crc) "ramblock name: %s, new crc: %" PRIu32 ", old crc: %" PRIu32 skip_sample_ramblock(const char *idstr, uint64_t ramblock_size) "ramblock name: %s, ramblock size: %" PRIu64 find_page_matched(const char *idstr) "ramblock %s addr or size changed" +dirtyrate_calculate(int64_t dirtyrate) "dirty rate: %" PRIi64 " MB/s" +dirtyrate_do_calculate_vcpu(int idx, uint64_t rate) "vcpu[%d]: %"PRIu64 " MB/s" # block.c migration_block_init_shared(const char *blk_device_name) "Start migration for %s with shared base image" diff --git a/qapi/migration.json b/qapi/migration.json index 94eece16e1..fae4bc608c 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1796,6 +1796,12 @@ # @sample-pages: page count per GB for sample dirty pages # the default value is 512 (since 6.1) # +# @mode: mode containing method of calculate dirtyrate includes +# 'page-sampling' and 'dirty-ring' (Since 6.1) +# +# @vcpu-dirty-rate: dirtyrate for each vcpu if dirty-ring +# mode specified (Since 6.1) +# # Since: 5.2 # ## @@ -1804,7 +1810,9 @@ 'status': 'DirtyRateStatus', 'start-time': 'int64', 'calc-time': 'int64', - 'sample-pages': 'uint64'} } + 'sample-pages': 'uint64', + 'mode': 'DirtyRateMeasureMode', + '*vcpu-dirty-rate': [ 'DirtyRateVcpu' ] } } ## # @calc-dirty-rate: @@ -1816,6 +1824,9 @@ # @sample-pages: page count per GB for sample dirty pages # the default value is 512 (since 6.1) # +# @mode: mechanism of calculating dirtyrate includes +# 'page-sampling' and 'dirty-ring' (Since 6.1) +# # Since: 5.2 # # Example: @@ -1824,7 +1835,8 @@ # ## { 'command': 'calc-dirty-rate', 'data': {'calc-time': 'int64', - '*sample-pages': 'int'} } + '*sample-pages': 'int', + '*mode': 'DirtyRateMeasureMode'} } ## # @query-dirty-rate: From 4c170330aae4a4ed75c3a8638b7d4c5d9f365244 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 22 Sep 2021 12:20:07 -0400 Subject: [PATCH 0948/1334] migration: Make migration blocker work for snapshots too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit save_snapshot() checks migration blocker, which looks sane. At the meantime we should also teach the blocker add helper to fail if during a snapshot, just like for migrations. Reviewed-by: Marc-André Lureau Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 9172686b89..e81e473f5a 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2058,15 +2058,16 @@ int migrate_add_blocker(Error *reason, Error **errp) return -EACCES; } - if (migration_is_idle()) { - migration_blockers = g_slist_prepend(migration_blockers, reason); - return 0; + /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */ + if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) { + error_propagate_prepend(errp, error_copy(reason), + "disallowing migration blocker " + "(migration/snapshot in progress) for: "); + return -EBUSY; } - error_propagate_prepend(errp, error_copy(reason), - "disallowing migration blocker " - "(migration in progress) for: "); - return -EBUSY; + migration_blockers = g_slist_prepend(migration_blockers, reason); + return 0; } void migrate_del_blocker(Error *reason) From 60fd680193119e7e4d50eccff7b55a0aadc934ab Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 22 Sep 2021 12:20:08 -0400 Subject: [PATCH 0949/1334] migration: Add migrate_add_blocker_internal() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An internal version that removes -only-migratable implications. It can be used for temporary migration blockers like dump-guest-memory. Reviewed-by: Marc-André Lureau Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Signed-off-by: Juan Quintela --- include/migration/blocker.h | 16 ++++++++++++++++ migration/migration.c | 21 +++++++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/include/migration/blocker.h b/include/migration/blocker.h index acd27018e9..9cebe2ba06 100644 --- a/include/migration/blocker.h +++ b/include/migration/blocker.h @@ -25,6 +25,22 @@ */ int migrate_add_blocker(Error *reason, Error **errp); +/** + * @migrate_add_blocker_internal - prevent migration from proceeding without + * only-migrate implications + * + * @reason - an error to be returned whenever migration is attempted + * + * @errp - [out] The reason (if any) we cannot block migration right now. + * + * @returns - 0 on success, -EBUSY on failure, with errp set. + * + * Some of the migration blockers can be temporary (e.g., for a few seconds), + * so it shouldn't need to conflict with "-only-migratable". For those cases, + * we can call this function rather than @migrate_add_blocker(). + */ +int migrate_add_blocker_internal(Error *reason, Error **errp); + /** * @migrate_del_blocker - remove a blocking error from migration * diff --git a/migration/migration.c b/migration/migration.c index e81e473f5a..e1c0082530 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2049,15 +2049,8 @@ void migrate_init(MigrationState *s) s->threshold_size = 0; } -int migrate_add_blocker(Error *reason, Error **errp) +int migrate_add_blocker_internal(Error *reason, Error **errp) { - if (only_migratable) { - error_propagate_prepend(errp, error_copy(reason), - "disallowing migration blocker " - "(--only-migratable) for: "); - return -EACCES; - } - /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */ if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) { error_propagate_prepend(errp, error_copy(reason), @@ -2070,6 +2063,18 @@ int migrate_add_blocker(Error *reason, Error **errp) return 0; } +int migrate_add_blocker(Error *reason, Error **errp) +{ + if (only_migratable) { + error_propagate_prepend(errp, error_copy(reason), + "disallowing migration blocker " + "(--only-migratable) for: "); + return -EACCES; + } + + return migrate_add_blocker_internal(reason, errp); +} + void migrate_del_blocker(Error *reason) { migration_blockers = g_slist_remove(migration_blockers, reason); From b7bc6b182883bb3097dde2a25d041f28bde2b89c Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 22 Sep 2021 12:20:09 -0400 Subject: [PATCH 0950/1334] dump-guest-memory: Block live migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both dump-guest-memory and live migration caches vm state at the beginning. Either of them entering the other one will cause race on the vm state, and even more severe on that (please refer to the crash report in the bug link). Let's block live migration in dump-guest-memory, and that'll also block dump-guest-memory if it detected that we're during a live migration. Side note: migrate_del_blocker() can be called even if the blocker is not inserted yet, so it's safe to unconditionally delete that blocker in dump_cleanup (g_slist_remove allows no-entry-found case). Suggested-by: Dr. David Alan Gilbert Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1996609 Signed-off-by: Peter Xu Reviewed-by: Marc-André Lureau Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- dump/dump.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dump/dump.c b/dump/dump.c index ab625909f3..662d0a62cd 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -29,6 +29,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "hw/misc/vmcoreinfo.h" +#include "migration/blocker.h" #ifdef TARGET_X86_64 #include "win_dump.h" @@ -47,6 +48,8 @@ #define MAX_GUEST_NOTE_SIZE (1 << 20) /* 1MB should be enough */ +static Error *dump_migration_blocker; + #define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \ ((DIV_ROUND_UP((hdr_size), 4) + \ DIV_ROUND_UP((name_size), 4) + \ @@ -101,6 +104,7 @@ static int dump_cleanup(DumpState *s) qemu_mutex_unlock_iothread(); } } + migrate_del_blocker(dump_migration_blocker); return 0; } @@ -2005,6 +2009,21 @@ void qmp_dump_guest_memory(bool paging, const char *file, return; } + if (!dump_migration_blocker) { + error_setg(&dump_migration_blocker, + "Live migration disabled: dump-guest-memory in progress"); + } + + /* + * Allows even for -only-migratable, but forbid migration during the + * process of dump guest memory. + */ + if (migrate_add_blocker_internal(dump_migration_blocker, errp)) { + /* Remember to release the fd before passing it over to dump state */ + close(fd); + return; + } + s = &dump_state_global; dump_state_prepare(s); From adaf9d92f868e6b9f23cb9a3929635e1ba961b3b Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 19:53:38 +0200 Subject: [PATCH 0951/1334] memory: Introduce replay_discarded callback for RamDiscardManager Introduce replay_discarded callback similar to our existing replay_populated callback, to be used my migration code to never migrate discarded memory. Acked-by: Peter Xu Signed-off-by: David Hildenbrand Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- include/exec/memory.h | 21 +++++++++++++++++++++ softmmu/memory.c | 11 +++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/exec/memory.h b/include/exec/memory.h index 04280450c9..20f1b27377 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -550,6 +550,7 @@ static inline void ram_discard_listener_init(RamDiscardListener *rdl, } typedef int (*ReplayRamPopulate)(MemoryRegionSection *section, void *opaque); +typedef void (*ReplayRamDiscard)(MemoryRegionSection *section, void *opaque); /* * RamDiscardManagerClass: @@ -638,6 +639,21 @@ struct RamDiscardManagerClass { MemoryRegionSection *section, ReplayRamPopulate replay_fn, void *opaque); + /** + * @replay_discarded: + * + * Call the #ReplayRamDiscard callback for all discarded parts within the + * #MemoryRegionSection via the #RamDiscardManager. + * + * @rdm: the #RamDiscardManager + * @section: the #MemoryRegionSection + * @replay_fn: the #ReplayRamDiscard callback + * @opaque: pointer to forward to the callback + */ + void (*replay_discarded)(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscard replay_fn, void *opaque); + /** * @register_listener: * @@ -682,6 +698,11 @@ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, ReplayRamPopulate replay_fn, void *opaque); +void ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscard replay_fn, + void *opaque); + void ram_discard_manager_register_listener(RamDiscardManager *rdm, RamDiscardListener *rdl, MemoryRegionSection *section); diff --git a/softmmu/memory.c b/softmmu/memory.c index f2ac0d2e89..7340e19ff5 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -2081,6 +2081,17 @@ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, return rdmc->replay_populated(rdm, section, replay_fn, opaque); } +void ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscard replay_fn, + void *opaque) +{ + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); + + g_assert(rdmc->replay_discarded); + rdmc->replay_discarded(rdm, section, replay_fn, opaque); +} + void ram_discard_manager_register_listener(RamDiscardManager *rdm, RamDiscardListener *rdl, MemoryRegionSection *section) From 372aa6fd738ef4c13dc6201fa34d2c8b9a6dde29 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 19:53:39 +0200 Subject: [PATCH 0952/1334] virtio-mem: Implement replay_discarded RamDiscardManager callback Implement it similar to the replay_populated callback. Acked-by: Peter Xu Signed-off-by: David Hildenbrand Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- hw/virtio/virtio-mem.c | 58 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index df91e454b2..284096ec5f 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -228,6 +228,38 @@ static int virtio_mem_for_each_plugged_section(const VirtIOMEM *vmem, return ret; } +static int virtio_mem_for_each_unplugged_section(const VirtIOMEM *vmem, + MemoryRegionSection *s, + void *arg, + virtio_mem_section_cb cb) +{ + unsigned long first_bit, last_bit; + uint64_t offset, size; + int ret = 0; + + first_bit = s->offset_within_region / vmem->bitmap_size; + first_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, first_bit); + while (first_bit < vmem->bitmap_size) { + MemoryRegionSection tmp = *s; + + offset = first_bit * vmem->block_size; + last_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, + first_bit + 1) - 1; + size = (last_bit - first_bit + 1) * vmem->block_size; + + if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + break; + } + ret = cb(&tmp, arg); + if (ret) { + break; + } + first_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, + last_bit + 2); + } + return ret; +} + static int virtio_mem_notify_populate_cb(MemoryRegionSection *s, void *arg) { RamDiscardListener *rdl = arg; @@ -1170,6 +1202,31 @@ static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm, virtio_mem_rdm_replay_populated_cb); } +static int virtio_mem_rdm_replay_discarded_cb(MemoryRegionSection *s, + void *arg) +{ + struct VirtIOMEMReplayData *data = arg; + + ((ReplayRamDiscard)data->fn)(s, data->opaque); + return 0; +} + +static void virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *s, + ReplayRamDiscard replay_fn, + void *opaque) +{ + const VirtIOMEM *vmem = VIRTIO_MEM(rdm); + struct VirtIOMEMReplayData data = { + .fn = replay_fn, + .opaque = opaque, + }; + + g_assert(s->mr == &vmem->memdev->mr); + virtio_mem_for_each_unplugged_section(vmem, s, &data, + virtio_mem_rdm_replay_discarded_cb); +} + static void virtio_mem_rdm_register_listener(RamDiscardManager *rdm, RamDiscardListener *rdl, MemoryRegionSection *s) @@ -1234,6 +1291,7 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data) rdmc->get_min_granularity = virtio_mem_rdm_get_min_granularity; rdmc->is_populated = virtio_mem_rdm_is_populated; rdmc->replay_populated = virtio_mem_rdm_replay_populated; + rdmc->replay_discarded = virtio_mem_rdm_replay_discarded; rdmc->register_listener = virtio_mem_rdm_register_listener; rdmc->unregister_listener = virtio_mem_rdm_unregister_listener; } From be39b4cd20695b95a479c941b2bad7d53a6971de Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 19:53:41 +0200 Subject: [PATCH 0953/1334] migration/ram: Handle RAMBlocks with a RamDiscardManager on the migration source We don't want to migrate memory that corresponds to discarded ranges as managed by a RamDiscardManager responsible for the mapped memory region of the RAMBlock. The content of these pages is essentially stale and without any guarantees for the VM ("logically unplugged"). Depending on the underlying memory type, even reading memory might populate memory on the source, resulting in an undesired memory consumption. Of course, on the destination, even writing a zeropage consumes memory, which we also want to avoid (similar to free page hinting). Currently, virtio-mem tries achieving that goal (not migrating "unplugged" memory that was discarded) by going via qemu_guest_free_page_hint() - but it's hackish and incomplete. For example, background snapshots still end up reading all memory, as they don't do bitmap syncs. Postcopy recovery code will re-add previously cleared bits to the dirty bitmap and migrate them. Let's consult the RamDiscardManager after setting up our dirty bitmap initially and when postcopy recovery code reinitializes it: clear corresponding bits in the dirty bitmaps (e.g., of the RAMBlock and inside KVM). It's important to fixup the dirty bitmap *after* our initial bitmap sync, such that the corresponding dirty bits in KVM are actually cleared. As colo is incompatible with discarding of RAM and inhibits it, we don't have to bother. Note: if a misbehaving guest would use discarded ranges after migration started we would still migrate that memory: however, then we already populated that memory on the migration source. Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/migration/ram.c b/migration/ram.c index ae2601bf3b..e8c06f207c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -858,6 +858,60 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, return ret; } +static void dirty_bitmap_clear_section(MemoryRegionSection *section, + void *opaque) +{ + const hwaddr offset = section->offset_within_region; + const hwaddr size = int128_get64(section->size); + const unsigned long start = offset >> TARGET_PAGE_BITS; + const unsigned long npages = size >> TARGET_PAGE_BITS; + RAMBlock *rb = section->mr->ram_block; + uint64_t *cleared_bits = opaque; + + /* + * We don't grab ram_state->bitmap_mutex because we expect to run + * only when starting migration or during postcopy recovery where + * we don't have concurrent access. + */ + if (!migration_in_postcopy() && !migrate_background_snapshot()) { + migration_clear_memory_region_dirty_bitmap_range(rb, start, npages); + } + *cleared_bits += bitmap_count_one_with_offset(rb->bmap, start, npages); + bitmap_clear(rb->bmap, start, npages); +} + +/* + * Exclude all dirty pages from migration that fall into a discarded range as + * managed by a RamDiscardManager responsible for the mapped memory region of + * the RAMBlock. Clear the corresponding bits in the dirty bitmaps. + * + * Discarded pages ("logically unplugged") have undefined content and must + * not get migrated, because even reading these pages for migration might + * result in undesired behavior. + * + * Returns the number of cleared bits in the RAMBlock dirty bitmap. + * + * Note: The result is only stable while migrating (precopy/postcopy). + */ +static uint64_t ramblock_dirty_bitmap_clear_discarded_pages(RAMBlock *rb) +{ + uint64_t cleared_bits = 0; + + if (rb->mr && rb->bmap && memory_region_has_ram_discard_manager(rb->mr)) { + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr); + MemoryRegionSection section = { + .mr = rb->mr, + .offset_within_region = 0, + .size = int128_make64(qemu_ram_get_used_length(rb)), + }; + + ram_discard_manager_replay_discarded(rdm, §ion, + dirty_bitmap_clear_section, + &cleared_bits); + } + return cleared_bits; +} + /* Called with RCU critical section */ static void ramblock_sync_dirty_bitmap(RAMState *rs, RAMBlock *rb) { @@ -2675,6 +2729,19 @@ static void ram_list_init_bitmaps(void) } } +static void migration_bitmap_clear_discarded_pages(RAMState *rs) +{ + unsigned long pages; + RAMBlock *rb; + + RCU_READ_LOCK_GUARD(); + + RAMBLOCK_FOREACH_NOT_IGNORED(rb) { + pages = ramblock_dirty_bitmap_clear_discarded_pages(rb); + rs->migration_dirty_pages -= pages; + } +} + static void ram_init_bitmaps(RAMState *rs) { /* For memory_global_dirty_log_start below. */ @@ -2691,6 +2758,12 @@ static void ram_init_bitmaps(RAMState *rs) } qemu_mutex_unlock_ramlist(); qemu_mutex_unlock_iothread(); + + /* + * After an eventual first bitmap sync, fixup the initial bitmap + * containing all 1s to exclude any discarded pages from migration. + */ + migration_bitmap_clear_discarded_pages(rs); } static int ram_init_all(RAMState **rsp) @@ -4119,6 +4192,10 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) */ bitmap_complement(block->bmap, block->bmap, nbits); + /* Clear dirty bits of discarded ranges that we don't want to migrate. */ + ramblock_dirty_bitmap_clear_discarded_pages(block); + + /* We'll recalculate migration_dirty_pages in ram_state_resume_prepare(). */ trace_ram_dirty_bitmap_reload_complete(block->idstr); /* From f4578df3992fa0b68a096346bb615b9c9e01128d Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 19:53:42 +0200 Subject: [PATCH 0954/1334] virtio-mem: Drop precopy notifier Migration code now properly handles RAMBlocks which are indirectly managed by a RamDiscardManager. No need for manual handling via the free page optimization interface, let's get rid of it. Acked-by: Michael S. Tsirkin Acked-by: Peter Xu Signed-off-by: David Hildenbrand Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- hw/virtio/virtio-mem.c | 34 ---------------------------------- include/hw/virtio/virtio-mem.h | 3 --- 2 files changed, 37 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 284096ec5f..d5a578142b 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -776,7 +776,6 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) host_memory_backend_set_mapped(vmem->memdev, true); vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem)); qemu_register_reset(virtio_mem_system_reset, vmem); - precopy_add_notifier(&vmem->precopy_notifier); /* * Set ourselves as RamDiscardManager before the plug handler maps the @@ -796,7 +795,6 @@ static void virtio_mem_device_unrealize(DeviceState *dev) * found via an address space anymore. Unset ourselves. */ memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); - precopy_remove_notifier(&vmem->precopy_notifier); qemu_unregister_reset(virtio_mem_system_reset, vmem); vmstate_unregister_ram(&vmem->memdev->mr, DEVICE(vmem)); host_memory_backend_set_mapped(vmem->memdev, false); @@ -1089,43 +1087,11 @@ static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name, vmem->block_size = value; } -static int virtio_mem_precopy_exclude_range_cb(const VirtIOMEM *vmem, void *arg, - uint64_t offset, uint64_t size) -{ - void * const host = qemu_ram_get_host_addr(vmem->memdev->mr.ram_block); - - qemu_guest_free_page_hint(host + offset, size); - return 0; -} - -static void virtio_mem_precopy_exclude_unplugged(VirtIOMEM *vmem) -{ - virtio_mem_for_each_unplugged_range(vmem, NULL, - virtio_mem_precopy_exclude_range_cb); -} - -static int virtio_mem_precopy_notify(NotifierWithReturn *n, void *data) -{ - VirtIOMEM *vmem = container_of(n, VirtIOMEM, precopy_notifier); - PrecopyNotifyData *pnd = data; - - switch (pnd->reason) { - case PRECOPY_NOTIFY_AFTER_BITMAP_SYNC: - virtio_mem_precopy_exclude_unplugged(vmem); - break; - default: - break; - } - - return 0; -} - static void virtio_mem_instance_init(Object *obj) { VirtIOMEM *vmem = VIRTIO_MEM(obj); notifier_list_init(&vmem->size_change_notifiers); - vmem->precopy_notifier.notify = virtio_mem_precopy_notify; QLIST_INIT(&vmem->rdl_list); object_property_add(obj, VIRTIO_MEM_SIZE_PROP, "size", virtio_mem_get_size, diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h index 9a6e348fa2..a5dd6a493b 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -65,9 +65,6 @@ struct VirtIOMEM { /* notifiers to notify when "size" changes */ NotifierList size_change_notifiers; - /* don't migrate unplugged memory */ - NotifierWithReturn precopy_notifier; - /* listeners to notify on plug/unplug activity. */ QLIST_HEAD(, RamDiscardListener) rdl_list; }; From 9470c5e0829c1ebd3ccfc7960de065d4fd6f212d Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 19:53:43 +0200 Subject: [PATCH 0955/1334] migration/postcopy: Handle RAMBlocks with a RamDiscardManager on the destination Currently, when someone (i.e., the VM) accesses discarded parts inside a RAMBlock with a RamDiscardManager managing the corresponding mapped memory region, postcopy will request migration of the corresponding page from the source. The source, however, will never answer, because it refuses to migrate such pages with undefined content ("logically unplugged"): the pages are never dirty, and get_queued_page() will consequently skip processing these postcopy requests. Especially reading discarded ("logically unplugged") ranges is supposed to work in some setups (for example with current virtio-mem), although it barely ever happens: still, not placing a page would currently stall the VM, as it cannot make forward progress. Let's check the state via the RamDiscardManager (the state e.g., of virtio-mem is migrated during precopy) and avoid sending a request that will never get answered. Place a fresh zero page instead to keep the VM working. This is the same behavior that would happen automatically without userfaultfd being active, when accessing virtual memory regions without populated pages -- "populate on demand". For now, there are valid cases (as documented in the virtio-mem spec) where a VM might read discarded memory; in the future, we will disallow that. Then, we might want to handle that case differently, e.g., warning the user that the VM seems to be mis-behaving. Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/postcopy-ram.c | 31 +++++++++++++++++++++++++++---- migration/ram.c | 21 +++++++++++++++++++++ migration/ram.h | 1 + 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 2e9697bdd2..3609ce7e52 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -671,6 +671,29 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd, return ret; } +static int postcopy_request_page(MigrationIncomingState *mis, RAMBlock *rb, + ram_addr_t start, uint64_t haddr) +{ + void *aligned = (void *)(uintptr_t)ROUND_DOWN(haddr, qemu_ram_pagesize(rb)); + + /* + * Discarded pages (via RamDiscardManager) are never migrated. On unlikely + * access, place a zeropage, which will also set the relevant bits in the + * recv_bitmap accordingly, so we won't try placing a zeropage twice. + * + * Checking a single bit is sufficient to handle pagesize > TPS as either + * all relevant bits are set or not. + */ + assert(QEMU_IS_ALIGNED(start, qemu_ram_pagesize(rb))); + if (ramblock_page_is_discarded(rb, start)) { + bool received = ramblock_recv_bitmap_test_byte_offset(rb, start); + + return received ? 0 : postcopy_place_page_zero(mis, aligned, rb); + } + + return migrate_send_rp_req_pages(mis, rb, start, haddr); +} + /* * Callback from shared fault handlers to ask for a page, * the page must be specified by a RAMBlock and an offset in that rb @@ -690,7 +713,7 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, qemu_ram_get_idstr(rb), rb_offset); return postcopy_wake_shared(pcfd, client_addr, rb); } - migrate_send_rp_req_pages(mis, rb, aligned_rbo, client_addr); + postcopy_request_page(mis, rb, aligned_rbo, client_addr); return 0; } @@ -984,8 +1007,8 @@ retry: * Send the request to the source - we want to request one * of our host page sizes (which is >= TPS) */ - ret = migrate_send_rp_req_pages(mis, rb, rb_offset, - msg.arg.pagefault.address); + ret = postcopy_request_page(mis, rb, rb_offset, + msg.arg.pagefault.address); if (ret) { /* May be network failure, try to wait for recovery */ if (ret == -EIO && postcopy_pause_fault_thread(mis)) { @@ -993,7 +1016,7 @@ retry: goto retry; } else { /* This is a unavoidable fault */ - error_report("%s: migrate_send_rp_req_pages() get %d", + error_report("%s: postcopy_request_page() get %d", __func__, ret); break; } diff --git a/migration/ram.c b/migration/ram.c index e8c06f207c..4f629de7d0 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -912,6 +912,27 @@ static uint64_t ramblock_dirty_bitmap_clear_discarded_pages(RAMBlock *rb) return cleared_bits; } +/* + * Check if a host-page aligned page falls into a discarded range as managed by + * a RamDiscardManager responsible for the mapped memory region of the RAMBlock. + * + * Note: The result is only stable while migrating (precopy/postcopy). + */ +bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start) +{ + if (rb->mr && memory_region_has_ram_discard_manager(rb->mr)) { + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr); + MemoryRegionSection section = { + .mr = rb->mr, + .offset_within_region = start, + .size = int128_make64(qemu_ram_pagesize(rb)), + }; + + return !ram_discard_manager_is_populated(rdm, §ion); + } + return false; +} + /* Called with RCU critical section */ static void ramblock_sync_dirty_bitmap(RAMState *rs, RAMBlock *rb) { diff --git a/migration/ram.h b/migration/ram.h index 4833e9fd5b..dda1988f3d 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -72,6 +72,7 @@ void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); int64_t ramblock_recv_bitmap_send(QEMUFile *file, const char *block_name); int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); +bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start); /* ram cache */ int colo_init_ram_cache(void); From 7648297d40649bbffb296b2d8f2f388f19868759 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 19:53:44 +0200 Subject: [PATCH 0956/1334] migration: Simplify alignment and alignment checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's use QEMU_ALIGN_DOWN() and friends to make the code a bit easier to read. Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 6 +++--- migration/postcopy-ram.c | 9 ++++----- migration/ram.c | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index e1c0082530..53b9a8af96 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -391,7 +391,7 @@ int migrate_send_rp_message_req_pages(MigrationIncomingState *mis, int migrate_send_rp_req_pages(MigrationIncomingState *mis, RAMBlock *rb, ram_addr_t start, uint64_t haddr) { - void *aligned = (void *)(uintptr_t)(haddr & (-qemu_ram_pagesize(rb))); + void *aligned = (void *)(uintptr_t)ROUND_DOWN(haddr, qemu_ram_pagesize(rb)); bool received = false; WITH_QEMU_LOCK_GUARD(&mis->page_request_mutex) { @@ -2637,8 +2637,8 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, * Since we currently insist on matching page sizes, just sanity check * we're being asked for whole host pages. */ - if (start & (our_host_ps - 1) || - (len & (our_host_ps - 1))) { + if (!QEMU_IS_ALIGNED(start, our_host_ps) || + !QEMU_IS_ALIGNED(len, our_host_ps)) { error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT " len: %zd", __func__, start, len); mark_source_rp_bad(ms); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 3609ce7e52..e721f69d0f 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -402,7 +402,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) strerror(errno)); goto out; } - g_assert(((size_t)testarea & (pagesize - 1)) == 0); + g_assert(QEMU_PTR_IS_ALIGNED(testarea, pagesize)); reg_struct.range.start = (uintptr_t)testarea; reg_struct.range.len = pagesize; @@ -660,7 +660,7 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd, struct uffdio_range range; int ret; trace_postcopy_wake_shared(client_addr, qemu_ram_get_idstr(rb)); - range.start = client_addr & ~(pagesize - 1); + range.start = ROUND_DOWN(client_addr, pagesize); range.len = pagesize; ret = ioctl(pcfd->fd, UFFDIO_WAKE, &range); if (ret) { @@ -702,8 +702,7 @@ static int postcopy_request_page(MigrationIncomingState *mis, RAMBlock *rb, int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, uint64_t client_addr, uint64_t rb_offset) { - size_t pagesize = qemu_ram_pagesize(rb); - uint64_t aligned_rbo = rb_offset & ~(pagesize - 1); + uint64_t aligned_rbo = ROUND_DOWN(rb_offset, qemu_ram_pagesize(rb)); MigrationIncomingState *mis = migration_incoming_get_current(); trace_postcopy_request_shared_page(pcfd->idstr, qemu_ram_get_idstr(rb), @@ -993,7 +992,7 @@ static void *postcopy_ram_fault_thread(void *opaque) break; } - rb_offset &= ~(qemu_ram_pagesize(rb) - 1); + rb_offset = ROUND_DOWN(rb_offset, qemu_ram_pagesize(rb)); trace_postcopy_ram_fault_thread_request(msg.arg.pagefault.address, qemu_ram_get_idstr(rb), rb_offset, diff --git a/migration/ram.c b/migration/ram.c index 4f629de7d0..54df5dc0fc 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -811,7 +811,7 @@ static void migration_clear_memory_region_dirty_bitmap(RAMBlock *rb, assert(shift >= 6); size = 1ULL << (TARGET_PAGE_BITS + shift); - start = (((ram_addr_t)page) << TARGET_PAGE_BITS) & (-size); + start = QEMU_ALIGN_DOWN((ram_addr_t)page << TARGET_PAGE_BITS, size); trace_migration_bitmap_clear_dirty(rb->idstr, start, size, page); memory_region_clear_dirty_bitmap(rb->mr, start, size); } From f7b9dcfbcf44aa38d132038e9b675ea7e714a570 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 19:53:45 +0200 Subject: [PATCH 0957/1334] migration/ram: Factor out populating pages readable in ram_block_populate_pages() Let's factor out prefaulting/populating to make further changes easier to review and add a comment what we are actually expecting to happen. While at it, use the actual page size of the ramblock, which defaults to qemu_real_host_page_size for anonymous memory. Further, rename ram_block_populate_pages() to ram_block_populate_read() as well, to make it clearer what we are doing. In the future, we might want to use MADV_POPULATE_READ to speed up population. Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 54df5dc0fc..92c7b788ae 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1639,26 +1639,35 @@ out: return ret; } +static inline void populate_read_range(RAMBlock *block, ram_addr_t offset, + ram_addr_t size) +{ + /* + * We read one byte of each page; this will preallocate page tables if + * required and populate the shared zeropage on MAP_PRIVATE anonymous memory + * where no page was populated yet. This might require adaption when + * supporting other mappings, like shmem. + */ + for (; offset < size; offset += block->page_size) { + char tmp = *((char *)block->host + offset); + + /* Don't optimize the read out */ + asm volatile("" : "+r" (tmp)); + } +} + /* - * ram_block_populate_pages: populate memory in the RAM block by reading - * an integer from the beginning of each page. + * ram_block_populate_read: preallocate page tables and populate pages in the + * RAM block by reading a byte of each page. * * Since it's solely used for userfault_fd WP feature, here we just * hardcode page size to qemu_real_host_page_size. * * @block: RAM block to populate */ -static void ram_block_populate_pages(RAMBlock *block) +static void ram_block_populate_read(RAMBlock *block) { - char *ptr = (char *) block->host; - - for (ram_addr_t offset = 0; offset < block->used_length; - offset += qemu_real_host_page_size) { - char tmp = *(ptr + offset); - - /* Don't optimize the read out */ - asm volatile("" : "+r" (tmp)); - } + populate_read_range(block, 0, block->used_length); } /* @@ -1684,7 +1693,7 @@ void ram_write_tracking_prepare(void) * UFFDIO_WRITEPROTECT_MODE_WP mode setting would silently skip * pages with pte_none() entries in page table. */ - ram_block_populate_pages(block); + ram_block_populate_read(block); } } From 6fee3a1fd9ecde99c43e659cf8eb6c35c116d05e Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 19:53:46 +0200 Subject: [PATCH 0958/1334] migration/ram: Handle RAMBlocks with a RamDiscardManager on background snapshots We already don't ever migrate memory that corresponds to discarded ranges as managed by a RamDiscardManager responsible for the mapped memory region of the RAMBlock. virtio-mem uses this mechanism to logically unplug parts of a RAMBlock. Right now, we still populate zeropages for the whole usable part of the RAMBlock, which is undesired because: 1. Even populating the shared zeropage will result in memory getting consumed for page tables. 2. Memory backends without a shared zeropage (like hugetlbfs and shmem) will populate an actual, fresh page, resulting in an unintended memory consumption. Discarded ("logically unplugged") parts have to remain discarded. As these pages are never part of the migration stream, there is no need to track modifications via userfaultfd WP reliably for these parts. Further, any writes to these ranges by the VM are invalid and the behavior is undefined. Note that Linux only supports userfaultfd WP on private anonymous memory for now, which usually results in the shared zeropage getting populated. The issue will become more relevant once userfaultfd WP supports shmem and hugetlb. Acked-by: Peter Xu Signed-off-by: David Hildenbrand Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 92c7b788ae..680a5158aa 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1656,6 +1656,17 @@ static inline void populate_read_range(RAMBlock *block, ram_addr_t offset, } } +static inline int populate_read_section(MemoryRegionSection *section, + void *opaque) +{ + const hwaddr size = int128_get64(section->size); + hwaddr offset = section->offset_within_region; + RAMBlock *block = section->mr->ram_block; + + populate_read_range(block, offset, size); + return 0; +} + /* * ram_block_populate_read: preallocate page tables and populate pages in the * RAM block by reading a byte of each page. @@ -1665,9 +1676,32 @@ static inline void populate_read_range(RAMBlock *block, ram_addr_t offset, * * @block: RAM block to populate */ -static void ram_block_populate_read(RAMBlock *block) +static void ram_block_populate_read(RAMBlock *rb) { - populate_read_range(block, 0, block->used_length); + /* + * Skip populating all pages that fall into a discarded range as managed by + * a RamDiscardManager responsible for the mapped memory region of the + * RAMBlock. Such discarded ("logically unplugged") parts of a RAMBlock + * must not get populated automatically. We don't have to track + * modifications via userfaultfd WP reliably, because these pages will + * not be part of the migration stream either way -- see + * ramblock_dirty_bitmap_exclude_discarded_pages(). + * + * Note: The result is only stable while migrating (precopy/postcopy). + */ + if (rb->mr && memory_region_has_ram_discard_manager(rb->mr)) { + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr); + MemoryRegionSection section = { + .mr = rb->mr, + .offset_within_region = 0, + .size = rb->mr->size, + }; + + ram_discard_manager_replay_populated(rdm, §ion, + populate_read_section, NULL); + } else { + populate_read_range(rb, 0, rb->used_length); + } } /* From 4998a37e4bf2bc47f76775e6e6a0cd50bacfb16a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=E9=BB=84=E5=8B=87=29?= Date: Tue, 20 Jul 2021 23:19:16 +0800 Subject: [PATCH 0959/1334] memory: introduce total_dirty_pages to stat dirty pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit introduce global var total_dirty_pages to stat dirty pages along with memory_global_dirty_log_sync. Signed-off-by: Hyman Huang(黄勇) Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- include/exec/ram_addr.h | 9 +++++++++ migration/dirtyrate.c | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 45c913264a..64fb936c7c 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -26,6 +26,8 @@ #include "exec/ramlist.h" #include "exec/ramblock.h" +extern uint64_t total_dirty_pages; + /** * clear_bmap_size: calculate clear bitmap size * @@ -373,6 +375,10 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, qatomic_or( &blocks[DIRTY_MEMORY_MIGRATION][idx][offset], temp); + if (unlikely( + global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { + total_dirty_pages += ctpopl(temp); + } } if (tcg_enabled()) { @@ -403,6 +409,9 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, for (i = 0; i < len; i++) { if (bitmap[i] != 0) { c = leul_to_cpu(bitmap[i]); + if (unlikely(global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { + total_dirty_pages += ctpopl(c); + } do { j = ctzl(c); c &= ~(1ul << j); diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index f92c4b498e..17b3d2cbb5 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -28,6 +28,13 @@ #include "sysemu/runstate.h" #include "exec/memory.h" +/* + * total_dirty_pages is procted by BQL and is used + * to stat dirty pages during the period of two + * memory_global_dirty_log_sync + */ +uint64_t total_dirty_pages; + typedef struct DirtyPageRecord { uint64_t start_pages; uint64_t end_pages; From 826b8bc80cb191557a4ce7cf0e155b436d2d1afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=E9=BB=84=E5=8B=87=29?= Date: Tue, 20 Jul 2021 23:19:17 +0800 Subject: [PATCH 0960/1334] migration/dirtyrate: implement dirty-bitmap dirtyrate calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit introduce dirty-bitmap mode as the third method of calc-dirty-rate. implement dirty-bitmap dirtyrate calculation, which can be used to measuring dirtyrate in the absence of dirty-ring. introduce "dirty_bitmap:-b" option in hmp calc_dirty_rate to indicate dirty bitmap method should be used for calculation. Signed-off-by: Hyman Huang(黄勇) Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- hmp-commands.hx | 9 ++-- migration/dirtyrate.c | 112 ++++++++++++++++++++++++++++++++++++++---- qapi/migration.json | 6 ++- 3 files changed, 112 insertions(+), 15 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index b6d47bd03f..3a5aeba3fe 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1737,9 +1737,10 @@ ERST { .name = "calc_dirty_rate", - .args_type = "dirty_ring:-r,second:l,sample_pages_per_GB:l?", - .params = "[-r] second [sample_pages_per_GB]", - .help = "start a round of guest dirty rate measurement (using -d to" - "\n\t\t\t specify dirty ring as the method of calculation)", + .args_type = "dirty_ring:-r,dirty_bitmap:-b,second:l,sample_pages_per_GB:l?", + .params = "[-r] [-b] second [sample_pages_per_GB]", + .help = "start a round of guest dirty rate measurement (using -r to" + "\n\t\t\t specify dirty ring as the method of calculation and" + "\n\t\t\t -b to specify dirty bitmap as method of calculation)", .cmd = hmp_calc_dirty_rate, }, diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index 17b3d2cbb5..d65e744af9 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -15,6 +15,7 @@ #include "qapi/error.h" #include "cpu.h" #include "exec/ramblock.h" +#include "exec/ram_addr.h" #include "qemu/rcu_queue.h" #include "qemu/main-loop.h" #include "qapi/qapi-commands-migration.h" @@ -118,6 +119,10 @@ static struct DirtyRateInfo *query_dirty_rate_info(void) } info->vcpu_dirty_rate = head; } + + if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) { + info->sample_pages = 0; + } } trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState)); @@ -429,6 +434,79 @@ static int64_t do_calculate_dirtyrate_vcpu(DirtyPageRecord dirty_pages) return memory_size_MB / time_s; } +static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages, + bool start) +{ + if (start) { + dirty_pages->start_pages = total_dirty_pages; + } else { + dirty_pages->end_pages = total_dirty_pages; + } +} + +static void do_calculate_dirtyrate_bitmap(DirtyPageRecord dirty_pages) +{ + DirtyStat.dirty_rate = do_calculate_dirtyrate_vcpu(dirty_pages); +} + +static inline void dirtyrate_manual_reset_protect(void) +{ + RAMBlock *block = NULL; + + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_MIGRATABLE(block) { + memory_region_clear_dirty_bitmap(block->mr, 0, + block->used_length); + } + } +} + +static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config) +{ + int64_t msec = 0; + int64_t start_time; + DirtyPageRecord dirty_pages; + + qemu_mutex_lock_iothread(); + memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE); + + /* + * 1'round of log sync may return all 1 bits with + * KVM_DIRTY_LOG_INITIALLY_SET enable + * skip it unconditionally and start dirty tracking + * from 2'round of log sync + */ + memory_global_dirty_log_sync(); + + /* + * reset page protect manually and unconditionally. + * this make sure kvm dirty log be cleared if + * KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE cap is enabled. + */ + dirtyrate_manual_reset_protect(); + qemu_mutex_unlock_iothread(); + + record_dirtypages_bitmap(&dirty_pages, true); + + start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + DirtyStat.start_time = start_time / 1000; + + msec = config.sample_period_seconds * 1000; + msec = set_sample_page_period(msec, start_time); + DirtyStat.calc_time = msec / 1000; + + /* + * dirtyrate_global_dirty_log_stop do two things. + * 1. fetch dirty bitmap from kvm + * 2. stop dirty tracking + */ + dirtyrate_global_dirty_log_stop(); + + record_dirtypages_bitmap(&dirty_pages, false); + + do_calculate_dirtyrate_bitmap(dirty_pages); +} + static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config) { CPUState *cpu; @@ -514,7 +592,9 @@ out: static void calculate_dirtyrate(struct DirtyRateConfig config) { - if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { + if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) { + calculate_dirtyrate_dirty_bitmap(config); + } else if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { calculate_dirtyrate_dirty_ring(config); } else { calculate_dirtyrate_sample_vm(config); @@ -597,12 +677,15 @@ void qmp_calc_dirty_rate(int64_t calc_time, /* * dirty ring mode only works when kvm dirty ring is enabled. + * on the contrary, dirty bitmap mode is not. */ - if ((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) && - !kvm_dirty_ring_enabled()) { - error_setg(errp, "dirty ring is disabled, use sample-pages method " - "or remeasure later."); - return; + if (((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) && + !kvm_dirty_ring_enabled()) || + ((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) && + kvm_dirty_ring_enabled())) { + error_setg(errp, "mode %s is not enabled, use other method instead.", + DirtyRateMeasureMode_str(mode)); + return; } /* @@ -678,9 +761,8 @@ void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict) int64_t sample_pages = qdict_get_try_int(qdict, "sample_pages_per_GB", -1); bool has_sample_pages = (sample_pages != -1); bool dirty_ring = qdict_get_try_bool(qdict, "dirty_ring", false); - DirtyRateMeasureMode mode = - (dirty_ring ? DIRTY_RATE_MEASURE_MODE_DIRTY_RING : - DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING); + bool dirty_bitmap = qdict_get_try_bool(qdict, "dirty_bitmap", false); + DirtyRateMeasureMode mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; Error *err = NULL; if (!sec) { @@ -688,6 +770,18 @@ void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict) return; } + if (dirty_ring && dirty_bitmap) { + monitor_printf(mon, "Either dirty ring or dirty bitmap " + "can be specified!\n"); + return; + } + + if (dirty_bitmap) { + mode = DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP; + } else if (dirty_ring) { + mode = DIRTY_RATE_MEASURE_MODE_DIRTY_RING; + } + qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages, true, mode, &err); if (err) { diff --git a/qapi/migration.json b/qapi/migration.json index fae4bc608c..87146ceea2 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1770,13 +1770,15 @@ # # @page-sampling: calculate dirtyrate by sampling pages. # -# @dirty-ring: calculate dirtyrate by via dirty ring. +# @dirty-ring: calculate dirtyrate by dirty ring. +# +# @dirty-bitmap: calculate dirtyrate by dirty bitmap. # # Since: 6.1 # ## { 'enum': 'DirtyRateMeasureMode', - 'data': ['page-sampling', 'dirty-ring'] } + 'data': ['page-sampling', 'dirty-ring', 'dirty-bitmap'] } ## # @DirtyRateInfo: From 44d3d8981402ce7eef435dd912f21fb84f1f7c4d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 27 Oct 2021 11:10:12 -0400 Subject: [PATCH 0961/1334] qtest: fix 'expression is always false' build failure in qtest_has_accel() If KVM is disabled or not present, qtest library build may fail with: libqtest.c: In function 'qtest_has_accel': comparison of unsigned expression < 0 is always false [-Werror=type-limits] for (i = 0; i < ARRAY_SIZE(targets); i++) { due to empty 'targets' array. Fix it by making sure that CONFIG_KVM_TARGETS isn't empty. Fixes: e741aff0f43343 ("tests: qtest: add qtest_has_accel() to check if tested binary supports accelerator") Reported-by: Jason Andryuk Suggested-by: "Michael S. Tsirkin" Signed-off-by: Igor Mammedov Message-Id: <20211027151012.2639284-1-imammedo@redhat.com> Tested-by: Jason Andryuk Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index b092728397..ab4a5723f0 100644 --- a/meson.build +++ b/meson.build @@ -75,7 +75,7 @@ else kvm_targets = [] endif -kvm_targets_c = '' +kvm_targets_c = '""' if not get_option('kvm').disabled() and targetos == 'linux' kvm_targets_c = '"' + '" ,"'.join(kvm_targets) + '"' endif From e1c1915befbd2a991b20812eef6ad650b5637a36 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Oct 2021 15:03:24 +0200 Subject: [PATCH 0962/1334] vhost-vdpa: Set discarding of RAM broken when initializing the backend Similar to VFIO, vDPA will go ahead an map+pin all guest memory. Memory that used to be discarded will get re-populated and if we discard+re-access memory after mapping+pinning, the pages mapped into the vDPA IOMMU will go out of sync with the actual pages mapped into the user space page tables. Set discarding of RAM broken such that: - virtio-mem and vhost-vdpa run mutually exclusive - virtio-balloon is inhibited and no memory discards will get issued In the future, we might be able to support coordinated discarding of RAM as used by virtio-mem and already supported by vfio via the RamDiscardManager. Acked-by: Jason Wang Cc: Jason Wang Cc: Michael S. Tsirkin Cc: Cindy Lu Signed-off-by: David Hildenbrand Message-Id: <20211027130324.59791-1-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella --- hw/virtio/vhost-vdpa.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 12661fd5b1..0d8051426c 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -331,6 +331,17 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) struct vhost_vdpa *v; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); trace_vhost_vdpa_init(dev, opaque); + int ret; + + /* + * Similar to VFIO, we end up pinning all guest memory and have to + * disable discarding of RAM. + */ + ret = ram_block_discard_disable(true); + if (ret) { + error_report("Cannot set discarding of RAM broken"); + return ret; + } v = opaque; v->dev = dev; @@ -442,6 +453,8 @@ static int vhost_vdpa_cleanup(struct vhost_dev *dev) memory_listener_unregister(&v->listener); dev->opaque = NULL; + ram_block_discard_disable(false); + return 0; } From 1f85d74ac542d57559babbe0525633837e9fd52f Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Tue, 26 Oct 2021 19:20:20 +0100 Subject: [PATCH 0963/1334] hw/acpi: Add VIOT table Add a function that generates a Virtual I/O Translation table (VIOT), describing the topology of paravirtual IOMMUs. The table is created if a virtio-iommu device is present. It contains a virtio-iommu node and PCI Range nodes for endpoints managed by the IOMMU. By default, a single node describes all PCI devices. When passing the "default_bus_bypass_iommu" machine option and "bypass_iommu" PXB option, only buses that do not bypass the IOMMU are described by PCI Range nodes. Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Jean-Philippe Brucker Message-Id: <20211026182024.2642038-2-jean-philippe@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/Kconfig | 4 ++ hw/acpi/meson.build | 1 + hw/acpi/viot.c | 114 ++++++++++++++++++++++++++++++++++++++++++++ hw/acpi/viot.h | 13 +++++ 4 files changed, 132 insertions(+) create mode 100644 hw/acpi/viot.c create mode 100644 hw/acpi/viot.h diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 3b5e118c54..622b0b50b7 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -51,6 +51,10 @@ config ACPI_VMGENID default y depends on PC +config ACPI_VIOT + bool + depends on ACPI + config ACPI_HW_REDUCED bool select ACPI diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index 7d8c0eb43e..adf6347bc4 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -20,6 +20,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_APEI', if_true: files('ghes.c'), if_false: files( acpi_ss.add(when: 'CONFIG_ACPI_PIIX4', if_true: files('piix4.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_true: files('pcihp.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_false: files('acpi-pci-hotplug-stub.c')) +acpi_ss.add(when: 'CONFIG_ACPI_VIOT', if_true: files('viot.c')) acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c')) acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c')) acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) diff --git a/hw/acpi/viot.c b/hw/acpi/viot.c new file mode 100644 index 0000000000..c1af75206e --- /dev/null +++ b/hw/acpi/viot.c @@ -0,0 +1,114 @@ +/* + * ACPI Virtual I/O Translation table implementation + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/viot.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" + +struct viot_pci_ranges { + GArray *blob; + size_t count; + uint16_t output_node; +}; + +/* Build PCI range for a given PCI host bridge */ +static int build_pci_range_node(Object *obj, void *opaque) +{ + struct viot_pci_ranges *pci_ranges = opaque; + GArray *blob = pci_ranges->blob; + + if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) { + PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus; + + if (bus && !pci_bus_bypass_iommu(bus)) { + int min_bus, max_bus; + + pci_bus_range(bus, &min_bus, &max_bus); + + /* Type */ + build_append_int_noprefix(blob, 1 /* PCI range */, 1); + /* Reserved */ + build_append_int_noprefix(blob, 0, 1); + /* Length */ + build_append_int_noprefix(blob, 24, 2); + /* Endpoint start */ + build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 4); + /* PCI Segment start */ + build_append_int_noprefix(blob, 0, 2); + /* PCI Segment end */ + build_append_int_noprefix(blob, 0, 2); + /* PCI BDF start */ + build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 2); + /* PCI BDF end */ + build_append_int_noprefix(blob, PCI_BUILD_BDF(max_bus, 0xff), 2); + /* Output node */ + build_append_int_noprefix(blob, pci_ranges->output_node, 2); + /* Reserved */ + build_append_int_noprefix(blob, 0, 6); + + pci_ranges->count++; + } + } + + return 0; +} + +/* + * Generate a VIOT table with one PCI-based virtio-iommu that manages PCI + * endpoints. + * + * Defined in the ACPI Specification (Version TBD) + */ +void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker, + uint16_t virtio_iommu_bdf, const char *oem_id, + const char *oem_table_id) +{ + /* The virtio-iommu node follows the 48-bytes header */ + int viommu_off = 48; + AcpiTable table = { .sig = "VIOT", .rev = 0, + .oem_id = oem_id, .oem_table_id = oem_table_id }; + struct viot_pci_ranges pci_ranges = { + .output_node = viommu_off, + .blob = g_array_new(false, true /* clear */, 1), + }; + + /* Build the list of PCI ranges that this viommu manages */ + object_child_foreach_recursive(OBJECT(ms), build_pci_range_node, + &pci_ranges); + + /* ACPI table header */ + acpi_table_begin(&table, table_data); + /* Node count */ + build_append_int_noprefix(table_data, pci_ranges.count + 1, 2); + /* Node offset */ + build_append_int_noprefix(table_data, viommu_off, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 8); + + /* Virtio-iommu node */ + /* Type */ + build_append_int_noprefix(table_data, 3 /* virtio-pci IOMMU */, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 1); + /* Length */ + build_append_int_noprefix(table_data, 16, 2); + /* PCI Segment */ + build_append_int_noprefix(table_data, 0, 2); + /* PCI BDF number */ + build_append_int_noprefix(table_data, virtio_iommu_bdf, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 8); + + /* PCI ranges found above */ + g_array_append_vals(table_data, pci_ranges.blob->data, + pci_ranges.blob->len); + g_array_free(pci_ranges.blob, true); + + acpi_table_end(linker, &table); +} + diff --git a/hw/acpi/viot.h b/hw/acpi/viot.h new file mode 100644 index 0000000000..9fe565bb87 --- /dev/null +++ b/hw/acpi/viot.h @@ -0,0 +1,13 @@ +/* + * ACPI Virtual I/O Translation Table implementation + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef VIOT_H +#define VIOT_H + +void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker, + uint16_t virtio_iommu_bdf, const char *oem_id, + const char *oem_table_id); + +#endif /* VIOT_H */ From 867e9c9f4cbc152aee5ed6157257986ecad29096 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Tue, 26 Oct 2021 19:20:21 +0100 Subject: [PATCH 0964/1334] hw/i386/pc: Remove x86_iommu_get_type() To generate the IOMMU ACPI table, acpi-build.c can use base QEMU types instead of a special IommuType value. Reviewed-by: Eric Auger Reviewed-by: Igor Mammedov Signed-off-by: Jean-Philippe Brucker Message-Id: <20211026182024.2642038-3-jean-philippe@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 20 +++++++++----------- hw/i386/amd_iommu.c | 2 -- hw/i386/intel_iommu.c | 3 --- hw/i386/x86-iommu-stub.c | 5 ----- hw/i386/x86-iommu.c | 5 ----- include/hw/i386/x86-iommu.h | 12 ------------ 6 files changed, 9 insertions(+), 38 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 81418b7911..ab49e799ff 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2488,6 +2488,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); + X86IOMMUState *iommu = x86_iommu_get_default(); GArray *table_offsets; unsigned facs, dsdt, rsdt, fadt; AcpiPmInfo pm; @@ -2604,17 +2605,14 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) build_mcfg(tables_blob, tables->linker, &mcfg, x86ms->oem_id, x86ms->oem_table_id); } - if (x86_iommu_get_default()) { - IommuType IOMMUType = x86_iommu_get_type(); - if (IOMMUType == TYPE_AMD) { - acpi_add_table(table_offsets, tables_blob); - build_amd_iommu(tables_blob, tables->linker, x86ms->oem_id, - x86ms->oem_table_id); - } else if (IOMMUType == TYPE_INTEL) { - acpi_add_table(table_offsets, tables_blob); - build_dmar_q35(tables_blob, tables->linker, x86ms->oem_id, - x86ms->oem_table_id); - } + if (object_dynamic_cast(OBJECT(iommu), TYPE_AMD_IOMMU_DEVICE)) { + acpi_add_table(table_offsets, tables_blob); + build_amd_iommu(tables_blob, tables->linker, x86ms->oem_id, + x86ms->oem_table_id); + } else if (object_dynamic_cast(OBJECT(iommu), TYPE_INTEL_IOMMU_DEVICE)) { + acpi_add_table(table_offsets, tables_blob); + build_dmar_q35(tables_blob, tables->linker, x86ms->oem_id, + x86ms->oem_table_id); } if (machine->nvdimms_state->is_enabled) { nvdimm_build_acpi(table_offsets, tables_blob, tables->linker, diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 9242a0d3ed..91fe34ae58 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1538,7 +1538,6 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) { int ret = 0; AMDVIState *s = AMD_IOMMU_DEVICE(dev); - X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev); MachineState *ms = MACHINE(qdev_get_machine()); PCMachineState *pcms = PC_MACHINE(ms); X86MachineState *x86ms = X86_MACHINE(ms); @@ -1548,7 +1547,6 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) amdvi_uint64_equal, g_free, g_free); /* This device should take care of IOMMU PCI properties */ - x86_iommu->type = TYPE_AMD; if (!qdev_realize(DEVICE(&s->pci), &bus->qbus, errp)) { return; } diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 75f075547f..c27b20090e 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3806,9 +3806,6 @@ static void vtd_realize(DeviceState *dev, Error **errp) X86MachineState *x86ms = X86_MACHINE(ms); PCIBus *bus = pcms->bus; IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); - X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev); - - x86_iommu->type = TYPE_INTEL; if (!vtd_decide_config(s, errp)) { return; diff --git a/hw/i386/x86-iommu-stub.c b/hw/i386/x86-iommu-stub.c index c5ba077f9d..781b5ff922 100644 --- a/hw/i386/x86-iommu-stub.c +++ b/hw/i386/x86-iommu-stub.c @@ -36,8 +36,3 @@ bool x86_iommu_ir_supported(X86IOMMUState *s) { return false; } - -IommuType x86_iommu_get_type(void) -{ - abort(); -} diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 86ad03972e..dc968c7a53 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -98,11 +98,6 @@ X86IOMMUState *x86_iommu_get_default(void) return x86_iommu_default; } -IommuType x86_iommu_get_type(void) -{ - return x86_iommu_default->type; -} - static void x86_iommu_realize(DeviceState *dev, Error **errp) { X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev); diff --git a/include/hw/i386/x86-iommu.h b/include/hw/i386/x86-iommu.h index 9de92d33a1..5ba0c056d6 100644 --- a/include/hw/i386/x86-iommu.h +++ b/include/hw/i386/x86-iommu.h @@ -33,12 +33,6 @@ OBJECT_DECLARE_TYPE(X86IOMMUState, X86IOMMUClass, X86_IOMMU_DEVICE) typedef struct X86IOMMUIrq X86IOMMUIrq; typedef struct X86IOMMU_MSIMessage X86IOMMU_MSIMessage; -typedef enum IommuType { - TYPE_INTEL, - TYPE_AMD, - TYPE_NONE -} IommuType; - struct X86IOMMUClass { SysBusDeviceClass parent; /* Intel/AMD specific realize() hook */ @@ -71,7 +65,6 @@ struct X86IOMMUState { OnOffAuto intr_supported; /* Whether vIOMMU supports IR */ bool dt_supported; /* Whether vIOMMU supports DT */ bool pt_supported; /* Whether vIOMMU supports pass-through */ - IommuType type; /* IOMMU type - AMD/Intel */ QLIST_HEAD(, IEC_Notifier) iec_notifiers; /* IEC notify list */ }; @@ -140,11 +133,6 @@ struct X86IOMMU_MSIMessage { */ X86IOMMUState *x86_iommu_get_default(void); -/* - * x86_iommu_get_type - get IOMMU type - */ -IommuType x86_iommu_get_type(void); - /** * x86_iommu_iec_register_notifier - register IEC (Interrupt Entry * Cache) notifiers From 1b3bf13890fd849b2628ca8c059f8d63c74b9572 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Tue, 26 Oct 2021 19:20:22 +0100 Subject: [PATCH 0965/1334] hw/i386/pc: Move IOMMU singleton into PCMachineState We're about to support a third vIOMMU for x86, virtio-iommu which doesn't inherit X86IOMMUState. Move the IOMMU singleton into PCMachineState, so it can be shared between all three vIOMMUs. The x86_iommu_get_default() helper is still needed by KVM and IOAPIC to fetch the default IRQ-remapping IOMMU. Since virtio-iommu doesn't support IRQ remapping, this interface doesn't need to change for the moment. We could later replace X86IOMMUState with an "IRQ remapping IOMMU" interface if necessary. Reviewed-by: Eric Auger Reviewed-by: Igor Mammedov Signed-off-by: Jean-Philippe Brucker Message-Id: <20211026182024.2642038-4-jean-philippe@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 12 +++++++++++- hw/i386/x86-iommu.c | 28 +++++++++------------------- include/hw/i386/pc.h | 1 + 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 86223acfd3..7b1c4f41cd 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1330,6 +1330,15 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { pc_virtio_md_pci_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE)) { + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + + if (pcms->iommu) { + error_setg(errp, "QEMU does not support multiple vIOMMUs " + "for x86 yet."); + return; + } + pcms->iommu = dev; } } @@ -1384,7 +1393,8 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || object_dynamic_cast(OBJECT(dev), TYPE_CPU) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE)) { return HOTPLUG_HANDLER(machine); } diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index dc968c7a53..01d11325a6 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -77,25 +77,17 @@ void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out) msg_out->data = msg.msi_data; } -/* Default X86 IOMMU device */ -static X86IOMMUState *x86_iommu_default = NULL; - -static void x86_iommu_set_default(X86IOMMUState *x86_iommu) -{ - assert(x86_iommu); - - if (x86_iommu_default) { - error_report("QEMU does not support multiple vIOMMUs " - "for x86 yet."); - exit(1); - } - - x86_iommu_default = x86_iommu; -} - X86IOMMUState *x86_iommu_get_default(void) { - return x86_iommu_default; + MachineState *ms = MACHINE(qdev_get_machine()); + PCMachineState *pcms = + PC_MACHINE(object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE)); + + if (pcms && + object_dynamic_cast(OBJECT(pcms->iommu), TYPE_X86_IOMMU_DEVICE)) { + return X86_IOMMU_DEVICE(pcms->iommu); + } + return NULL; } static void x86_iommu_realize(DeviceState *dev, Error **errp) @@ -131,8 +123,6 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp) if (x86_class->realize) { x86_class->realize(dev, errp); } - - x86_iommu_set_default(X86_IOMMU_DEVICE(dev)); } static Property x86_iommu_properties[] = { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 11426e26dc..b72e5bf9d1 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -35,6 +35,7 @@ typedef struct PCMachineState { I2CBus *smbus; PFlashCFI01 *flash[2]; ISADevice *pcspk; + DeviceState *iommu; /* Configuration options: */ uint64_t max_ram_below_4g; From 36efa250a4ff56a73a5a73540c5097ffd7a67170 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Tue, 26 Oct 2021 19:20:23 +0100 Subject: [PATCH 0966/1334] hw/i386/pc: Allow instantiating a virtio-iommu device Allow instantiating a virtio-iommu device by adding an ACPI Virtual I/O Translation table (VIOT), which describes the relation between the virtio-iommu and the endpoints it manages. Add a hotplug handler for virtio-iommu on x86 and set the necessary reserved region property. On x86, the [0xfee00000, 0xfeefffff] DMA region is reserved for MSIs. DMA transactions to this range either trigger IRQ remapping in the IOMMU or bypasses IOMMU translation. Although virtio-iommu does not support IRQ remapping it must be informed of the reserved region so that it can forward DMA transactions targeting this region. Reviewed-by: Eric Auger Reviewed-by: Igor Mammedov Tested-by: Eric Auger Signed-off-by: Jean-Philippe Brucker Message-Id: <20211026182024.2642038-5-jean-philippe@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/Kconfig | 1 + hw/i386/acpi-build.c | 10 +++++++++- hw/i386/pc.c | 16 +++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 962d2c981b..d22ac4a4b9 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -59,6 +59,7 @@ config PC_ACPI select ACPI_X86 select ACPI_CPU_HOTPLUG select ACPI_MEMORY_HOTPLUG + select ACPI_VIOT select SMBUS_EEPROM select PFLASH_CFI01 depends on ACPI_SMBUS diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index ab49e799ff..3ca6cc8118 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -68,9 +68,11 @@ #include "qom/qom-qobject.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" +#include "hw/virtio/virtio-iommu.h" #include "hw/acpi/ipmi.h" #include "hw/acpi/hmat.h" +#include "hw/acpi/viot.h" /* These are used to size the ACPI tables for -M pc-i440fx-1.7 and * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows @@ -2488,7 +2490,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); - X86IOMMUState *iommu = x86_iommu_get_default(); + DeviceState *iommu = pcms->iommu; GArray *table_offsets; unsigned facs, dsdt, rsdt, fadt; AcpiPmInfo pm; @@ -2613,6 +2615,12 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); build_dmar_q35(tables_blob, tables->linker, x86ms->oem_id, x86ms->oem_table_id); + } else if (object_dynamic_cast(OBJECT(iommu), TYPE_VIRTIO_IOMMU_PCI)) { + PCIDevice *pdev = PCI_DEVICE(iommu); + + acpi_add_table(table_offsets, tables_blob); + build_viot(machine, tables_blob, tables->linker, pci_get_bdf(pdev), + x86ms->oem_id, x86ms->oem_table_id); } if (machine->nvdimms_state->is_enabled) { nvdimm_build_acpi(table_offsets, tables_blob, tables->linker, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7b1c4f41cd..e99017e662 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -83,6 +83,7 @@ #include "hw/i386/intel_iommu.h" #include "hw/net/ne2000-isa.h" #include "standard-headers/asm-x86/bootparam.h" +#include "hw/virtio/virtio-iommu.h" #include "hw/virtio/virtio-pmem-pci.h" #include "hw/virtio/virtio-mem-pci.h" #include "hw/mem/memory-device.h" @@ -1330,7 +1331,19 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { pc_virtio_md_pci_pre_plug(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE)) { + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { + /* Declare the APIC range as the reserved MSI region */ + char *resv_prop_str = g_strdup_printf("0xfee00000:0xfeefffff:%d", + VIRTIO_IOMMU_RESV_MEM_T_MSI); + + object_property_set_uint(OBJECT(dev), "len-reserved-regions", 1, errp); + object_property_set_str(OBJECT(dev), "reserved-regions[0]", + resv_prop_str, errp); + g_free(resv_prop_str); + } + + if (object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { PCMachineState *pcms = PC_MACHINE(hotplug_dev); if (pcms->iommu) { @@ -1394,6 +1407,7 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, object_dynamic_cast(OBJECT(dev), TYPE_CPU) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE)) { return HOTPLUG_HANDLER(machine); } From b3dcf94f77fdf5bbea76d7101cc2a300d2550adb Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 28 Oct 2021 12:31:25 +0800 Subject: [PATCH 0967/1334] pci: Define pci_bus_dev_fn/pci_bus_fn/pci_bus_ret_fn They're used in quite a few places of pci.[ch] and also in the rest of the code base. Define them so that it doesn't need to be defined all over the places. The pci_bus_fn is similar to pci_bus_dev_fn that only takes a PCIBus* and an opaque. The pci_bus_ret_fn is similar to pci_bus_fn but it allows to return a void* pointer. Reviewed-by: David Hildenbrand Reviewed-by: Eric Auger Signed-off-by: Peter Xu Message-Id: <20211028043129.38871-2-peterx@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 20 ++++++-------------- include/hw/pci/pci.h | 19 +++++++++---------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 186758ee11..17e59cb3a3 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1655,9 +1655,7 @@ static const pci_class_desc pci_class_descriptions[] = }; static void pci_for_each_device_under_bus_reverse(PCIBus *bus, - void (*fn)(PCIBus *b, - PCIDevice *d, - void *opaque), + pci_bus_dev_fn fn, void *opaque) { PCIDevice *d; @@ -1672,8 +1670,7 @@ static void pci_for_each_device_under_bus_reverse(PCIBus *bus, } void pci_for_each_device_reverse(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *b, PCIDevice *d, void *opaque), - void *opaque) + pci_bus_dev_fn fn, void *opaque) { bus = pci_find_bus_nr(bus, bus_num); @@ -1683,9 +1680,7 @@ void pci_for_each_device_reverse(PCIBus *bus, int bus_num, } static void pci_for_each_device_under_bus(PCIBus *bus, - void (*fn)(PCIBus *b, PCIDevice *d, - void *opaque), - void *opaque) + pci_bus_dev_fn fn, void *opaque) { PCIDevice *d; int devfn; @@ -1699,8 +1694,7 @@ static void pci_for_each_device_under_bus(PCIBus *bus, } void pci_for_each_device(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *b, PCIDevice *d, void *opaque), - void *opaque) + pci_bus_dev_fn fn, void *opaque) { bus = pci_find_bus_nr(bus, bus_num); @@ -2078,10 +2072,8 @@ static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) return NULL; } -void pci_for_each_bus_depth_first(PCIBus *bus, - void *(*begin)(PCIBus *bus, void *parent_state), - void (*end)(PCIBus *bus, void *state), - void *parent_state) +void pci_for_each_bus_depth_first(PCIBus *bus, pci_bus_ret_fn begin, + pci_bus_fn end, void *parent_state) { PCIBus *sec; void *state; diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 7fc90132cf..4a8740b76b 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -401,6 +401,10 @@ typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); OBJECT_DECLARE_TYPE(PCIBus, PCIBusClass, PCI_BUS) #define TYPE_PCIE_BUS "PCIE" +typedef void (*pci_bus_dev_fn)(PCIBus *b, PCIDevice *d, void *opaque); +typedef void (*pci_bus_fn)(PCIBus *b, void *opaque); +typedef void *(*pci_bus_ret_fn)(PCIBus *b, void *opaque); + bool pci_bus_is_express(PCIBus *bus); void pci_root_bus_init(PCIBus *bus, size_t bus_size, DeviceState *parent, @@ -458,23 +462,18 @@ static inline int pci_dev_bus_num(const PCIDevice *dev) int pci_bus_numa_node(PCIBus *bus); void pci_for_each_device(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), + pci_bus_dev_fn fn, void *opaque); void pci_for_each_device_reverse(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *bus, PCIDevice *d, - void *opaque), + pci_bus_dev_fn fn, void *opaque); -void pci_for_each_bus_depth_first(PCIBus *bus, - void *(*begin)(PCIBus *bus, void *parent_state), - void (*end)(PCIBus *bus, void *state), - void *parent_state); +void pci_for_each_bus_depth_first(PCIBus *bus, pci_bus_ret_fn begin, + pci_bus_fn end, void *parent_state); PCIDevice *pci_get_function_0(PCIDevice *pci_dev); /* Use this wrapper when specific scan order is not required. */ static inline -void pci_for_each_bus(PCIBus *bus, - void (*fn)(PCIBus *bus, void *opaque), - void *opaque) +void pci_for_each_bus(PCIBus *bus, pci_bus_fn fn, void *opaque) { pci_for_each_bus_depth_first(bus, NULL, fn, opaque); } From 2914fc61d5d72c6010d2b1fe8b4048b561e44ef3 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 28 Oct 2021 12:31:26 +0800 Subject: [PATCH 0968/1334] pci: Export pci_for_each_device_under_bus*() They're actually more commonly used than the helper without _under_bus, because most callers do have the pci bus on hand. After exporting we can switch a lot of the call sites to use these two helpers. Reviewed-by: David Hildenbrand Reviewed-by: Eric Auger Signed-off-by: Peter Xu Message-Id: <20211028043129.38871-3-peterx@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: David Gibson --- hw/i386/acpi-build.c | 5 ++--- hw/pci/pci.c | 10 +++++----- hw/pci/pcie.c | 4 +--- hw/ppc/spapr_pci.c | 12 +++++------- hw/ppc/spapr_pci_nvlink2.c | 7 +++---- hw/ppc/spapr_pci_vfio.c | 4 ++-- hw/s390x/s390-pci-bus.c | 5 ++--- hw/xen/xen_pt.c | 4 ++-- include/hw/pci/pci.h | 5 +++++ 9 files changed, 27 insertions(+), 29 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 3ca6cc8118..a3ad6abd33 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2134,8 +2134,7 @@ dmar_host_bridges(Object *obj, void *opaque) PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus; if (bus && !pci_bus_bypass_iommu(bus)) { - pci_for_each_device(bus, pci_bus_num(bus), insert_scope, - scope_blob); + pci_for_each_device_under_bus(bus, insert_scope, scope_blob); } } @@ -2341,7 +2340,7 @@ ivrs_host_bridges(Object *obj, void *opaque) PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus; if (bus && !pci_bus_bypass_iommu(bus)) { - pci_for_each_device(bus, pci_bus_num(bus), insert_ivhd, ivhd_blob); + pci_for_each_device_under_bus(bus, insert_ivhd, ivhd_blob); } } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 17e59cb3a3..4a84e478ce 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1654,9 +1654,9 @@ static const pci_class_desc pci_class_descriptions[] = { 0, NULL} }; -static void pci_for_each_device_under_bus_reverse(PCIBus *bus, - pci_bus_dev_fn fn, - void *opaque) +void pci_for_each_device_under_bus_reverse(PCIBus *bus, + pci_bus_dev_fn fn, + void *opaque) { PCIDevice *d; int devfn; @@ -1679,8 +1679,8 @@ void pci_for_each_device_reverse(PCIBus *bus, int bus_num, } } -static void pci_for_each_device_under_bus(PCIBus *bus, - pci_bus_dev_fn fn, void *opaque) +void pci_for_each_device_under_bus(PCIBus *bus, + pci_bus_dev_fn fn, void *opaque) { PCIDevice *d; int devfn; diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 6e95d82903..914a9bf3d1 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -694,9 +694,7 @@ void pcie_cap_slot_write_config(PCIDevice *dev, (!(old_slt_ctl & PCI_EXP_SLTCTL_PCC) || (old_slt_ctl & PCI_EXP_SLTCTL_PIC_OFF) != PCI_EXP_SLTCTL_PIC_OFF)) { PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev)); - pci_for_each_device(sec_bus, pci_bus_num(sec_bus), - pcie_unplug_device, NULL); - + pci_for_each_device_under_bus(sec_bus, pcie_unplug_device, NULL); pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); if (dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA || diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 7430bd6314..5bfd4aa9e5 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1317,8 +1317,7 @@ static int spapr_dt_pci_bus(SpaprPhbState *sphb, PCIBus *bus, RESOURCE_CELLS_SIZE)); assert(bus); - pci_for_each_device_reverse(bus, pci_bus_num(bus), - spapr_dt_pci_device_cb, &cbinfo); + pci_for_each_device_under_bus_reverse(bus, spapr_dt_pci_device_cb, &cbinfo); if (cbinfo.err) { return cbinfo.err; } @@ -2306,8 +2305,8 @@ static void spapr_phb_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, return; } - pci_for_each_device(sec_bus, pci_bus_num(sec_bus), - spapr_phb_pci_enumerate_bridge, bus_no); + pci_for_each_device_under_bus(sec_bus, spapr_phb_pci_enumerate_bridge, + bus_no); pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, *bus_no, 1); } @@ -2316,9 +2315,8 @@ static void spapr_phb_pci_enumerate(SpaprPhbState *phb) PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus; unsigned int bus_no = 0; - pci_for_each_device(bus, pci_bus_num(bus), - spapr_phb_pci_enumerate_bridge, - &bus_no); + pci_for_each_device_under_bus(bus, spapr_phb_pci_enumerate_bridge, + &bus_no); } diff --git a/hw/ppc/spapr_pci_nvlink2.c b/hw/ppc/spapr_pci_nvlink2.c index 8ef9b40a18..7fb0cf4d04 100644 --- a/hw/ppc/spapr_pci_nvlink2.c +++ b/hw/ppc/spapr_pci_nvlink2.c @@ -164,8 +164,7 @@ static void spapr_phb_pci_collect_nvgpu(PCIBus *bus, PCIDevice *pdev, return; } - pci_for_each_device(sec_bus, pci_bus_num(sec_bus), - spapr_phb_pci_collect_nvgpu, opaque); + pci_for_each_device_under_bus(sec_bus, spapr_phb_pci_collect_nvgpu, opaque); } void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp) @@ -183,8 +182,8 @@ void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp) sphb->nvgpus->nv2_atsd_current = sphb->nv2_atsd_win_addr; bus = PCI_HOST_BRIDGE(sphb)->bus; - pci_for_each_device(bus, pci_bus_num(bus), - spapr_phb_pci_collect_nvgpu, sphb->nvgpus); + pci_for_each_device_under_bus(bus, spapr_phb_pci_collect_nvgpu, + sphb->nvgpus); if (sphb->nvgpus->err) { error_propagate(errp, sphb->nvgpus->err); diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index f3b37df8ea..2a76b4e0b5 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -164,8 +164,8 @@ static void spapr_phb_vfio_eeh_clear_dev_msix(PCIBus *bus, static void spapr_phb_vfio_eeh_clear_bus_msix(PCIBus *bus, void *opaque) { - pci_for_each_device(bus, pci_bus_num(bus), - spapr_phb_vfio_eeh_clear_dev_msix, NULL); + pci_for_each_device_under_bus(bus, spapr_phb_vfio_eeh_clear_dev_msix, + NULL); } static void spapr_phb_vfio_eeh_pre_reset(SpaprPhbState *sphb) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 6fafffb029..1b51a72838 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1163,8 +1163,7 @@ static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, } /* Assign numbers to all child bridges. The last is the highest number. */ - pci_for_each_device(sec_bus, pci_bus_num(sec_bus), - s390_pci_enumerate_bridge, s); + pci_for_each_device_under_bus(sec_bus, s390_pci_enumerate_bridge, s); pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1); } @@ -1193,7 +1192,7 @@ static void s390_pcihost_reset(DeviceState *dev) * on every system reset, we also have to reassign numbers. */ s->bus_no = 0; - pci_for_each_device(bus, pci_bus_num(bus), s390_pci_enumerate_bridge, s); + pci_for_each_device_under_bus(bus, s390_pci_enumerate_bridge, s); } static void s390_pcihost_class_init(ObjectClass *klass, void *data) diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index ca0a98187e..027190fa44 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -615,8 +615,8 @@ static void xen_pt_region_update(XenPCIPassthroughState *s, } args.type = d->io_regions[bar].type; - pci_for_each_device(pci_get_bus(d), pci_dev_bus_num(d), - xen_pt_check_bar_overlap, &args); + pci_for_each_device_under_bus(pci_get_bus(d), + xen_pt_check_bar_overlap, &args); if (args.rc) { XEN_PT_WARN(d, "Region: %d (addr: 0x%"FMT_PCIBUS ", len: 0x%"FMT_PCIBUS") is overlapped.\n", diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 4a8740b76b..5c4016b995 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -467,6 +467,11 @@ void pci_for_each_device(PCIBus *bus, int bus_num, void pci_for_each_device_reverse(PCIBus *bus, int bus_num, pci_bus_dev_fn fn, void *opaque); +void pci_for_each_device_under_bus(PCIBus *bus, + pci_bus_dev_fn fn, void *opaque); +void pci_for_each_device_under_bus_reverse(PCIBus *bus, + pci_bus_dev_fn fn, + void *opaque); void pci_for_each_bus_depth_first(PCIBus *bus, pci_bus_ret_fn begin, pci_bus_fn end, void *parent_state); PCIDevice *pci_get_function_0(PCIDevice *pci_dev); From d99e8b5fcb138b19f751c027ed5599224f9b5036 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Tue, 26 Oct 2021 12:54:05 +0300 Subject: [PATCH 0969/1334] hw/i386: fix vmmouse registration According to the logic of vmmouse_update_handler function, vmmouse should be registered as an event handler when it's status is zero. vmmouse_read_id resets the status but does not register the handler. This patch adds vmmouse registration and activation when status is reset. Signed-off-by: Pavel Dovgalyuk Message-Id: <163524204515.1914131.16465061981774791228.stgit@pasha-ThinkPad-X280> Signed-off-by: Michael S. Tsirkin --- hw/i386/vmmouse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index df4798f502..3d66368286 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -158,6 +158,7 @@ static void vmmouse_read_id(VMMouseState *s) s->queue[s->nb_queue++] = VMMOUSE_VERSION; s->status = 0; + vmmouse_update_handler(s, s->absolute); } static void vmmouse_request_relative(VMMouseState *s) From 85442fce49eb81c216112f87e871cf1a6a2e6c7b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 16 Sep 2021 19:44:47 -0700 Subject: [PATCH 0970/1334] linux-user/host/x86: Populate host_signal.h Split host_signal_pc and host_signal_write out of user-exec.c. Drop the *BSD code, to be re-created under bsd-user/ later. Reviewed-by: Warner Losh Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 136 +-------------------------- linux-user/host/i386/host-signal.h | 26 ++++- linux-user/host/x32/host-signal.h | 2 +- linux-user/host/x86_64/host-signal.h | 25 ++++- 4 files changed, 51 insertions(+), 138 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index b1183aa4b3..b121e6c2e9 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -29,19 +29,6 @@ #include "trace/trace-root.h" #include "internal.h" -#undef EAX -#undef ECX -#undef EDX -#undef EBX -#undef ESP -#undef EBP -#undef ESI -#undef EDI -#undef EIP -#ifdef __linux__ -#include -#endif - __thread uintptr_t helper_retaddr; //#define DEBUG_SIGNAL @@ -266,123 +253,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(__i386__) - -#if defined(__NetBSD__) -#include -#include - -#define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP]) -#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) -#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) -#define MASK_sig(context) ((context)->uc_sigmask) -#define PAGE_FAULT_TRAP T_PAGEFLT -#elif defined(__FreeBSD__) || defined(__DragonFly__) -#include -#include - -#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_eip)) -#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) -#define ERROR_sig(context) ((context)->uc_mcontext.mc_err) -#define MASK_sig(context) ((context)->uc_sigmask) -#define PAGE_FAULT_TRAP T_PAGEFLT -#elif defined(__OpenBSD__) -#include -#define EIP_sig(context) ((context)->sc_eip) -#define TRAP_sig(context) ((context)->sc_trapno) -#define ERROR_sig(context) ((context)->sc_err) -#define MASK_sig(context) ((context)->sc_mask) -#define PAGE_FAULT_TRAP T_PAGEFLT -#else -#define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP]) -#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) -#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) -#define MASK_sig(context) ((context)->uc_sigmask) -#define PAGE_FAULT_TRAP 0xe -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; -#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) - ucontext_t *uc = puc; -#elif defined(__OpenBSD__) - struct sigcontext *uc = puc; -#else - ucontext_t *uc = puc; -#endif - unsigned long pc; - int trapno; - -#ifndef REG_EIP -/* for glibc 2.1 */ -#define REG_EIP EIP -#define REG_ERR ERR -#define REG_TRAPNO TRAPNO -#endif - pc = EIP_sig(uc); - trapno = TRAP_sig(uc); - return handle_cpu_signal(pc, info, - trapno == PAGE_FAULT_TRAP ? - (ERROR_sig(uc) >> 1) & 1 : 0, - &MASK_sig(uc)); -} - -#elif defined(__x86_64__) - -#ifdef __NetBSD__ -#include -#define PC_sig(context) _UC_MACHINE_PC(context) -#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) -#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) -#define MASK_sig(context) ((context)->uc_sigmask) -#define PAGE_FAULT_TRAP T_PAGEFLT -#elif defined(__OpenBSD__) -#include -#define PC_sig(context) ((context)->sc_rip) -#define TRAP_sig(context) ((context)->sc_trapno) -#define ERROR_sig(context) ((context)->sc_err) -#define MASK_sig(context) ((context)->sc_mask) -#define PAGE_FAULT_TRAP T_PAGEFLT -#elif defined(__FreeBSD__) || defined(__DragonFly__) -#include -#include - -#define PC_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_rip)) -#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) -#define ERROR_sig(context) ((context)->uc_mcontext.mc_err) -#define MASK_sig(context) ((context)->uc_sigmask) -#define PAGE_FAULT_TRAP T_PAGEFLT -#else -#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP]) -#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) -#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) -#define MASK_sig(context) ((context)->uc_sigmask) -#define PAGE_FAULT_TRAP 0xe -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - unsigned long pc; -#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) - ucontext_t *uc = puc; -#elif defined(__OpenBSD__) - struct sigcontext *uc = puc; -#else - ucontext_t *uc = puc; -#endif - - pc = PC_sig(uc); - return handle_cpu_signal(pc, info, - TRAP_sig(uc) == PAGE_FAULT_TRAP ? - (ERROR_sig(uc) >> 1) & 1 : 0, - &MASK_sig(uc)); -} - -#elif defined(_ARCH_PPC) +#if defined(_ARCH_PPC) /*********************************************************************** * signal context platform-specific definitions @@ -893,11 +764,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); } - -#else - -#error host CPU specific signal handler needed - #endif /* The softmmu versions of these helpers are in cputlb.c. */ diff --git a/linux-user/host/i386/host-signal.h b/linux-user/host/i386/host-signal.h index f4b4d65031..4c8eef99ce 100644 --- a/linux-user/host/i386/host-signal.h +++ b/linux-user/host/i386/host-signal.h @@ -1 +1,25 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef I386_HOST_SIGNAL_H +#define I386_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.gregs[REG_EIP]; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe + && (uc->uc_mcontext.gregs[REG_ERR] & 0x2); +} + +#endif diff --git a/linux-user/host/x32/host-signal.h b/linux-user/host/x32/host-signal.h index f4b4d65031..26800591d3 100644 --- a/linux-user/host/x32/host-signal.h +++ b/linux-user/host/x32/host-signal.h @@ -1 +1 @@ -#define HOST_SIGNAL_PLACEHOLDER +#include "../x86_64/host-signal.h" diff --git a/linux-user/host/x86_64/host-signal.h b/linux-user/host/x86_64/host-signal.h index f4b4d65031..883d2fcf65 100644 --- a/linux-user/host/x86_64/host-signal.h +++ b/linux-user/host/x86_64/host-signal.h @@ -1 +1,24 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (C) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef X86_64_HOST_SIGNAL_H +#define X86_64_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.gregs[REG_RIP]; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe + && (uc->uc_mcontext.gregs[REG_ERR] & 0x2); +} + +#endif From 8cc7b85d56b629e4ffceea85c3a0a4ad8754153a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 10:01:37 -0700 Subject: [PATCH 0971/1334] linux-user/host/ppc: Populate host_signal.h Split host_signal_pc and host_signal_write out of user-exec.c. Drop the *BSD code, to be re-created under bsd-user/ later. Reviewed-by: Warner Losh Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 79 +---------------------------- linux-user/host/ppc/host-signal.h | 26 +++++++++- linux-user/host/ppc64/host-signal.h | 2 +- 3 files changed, 27 insertions(+), 80 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index b121e6c2e9..5a0a65fa46 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -253,84 +253,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(_ARCH_PPC) - -/*********************************************************************** - * signal context platform-specific definitions - * From Wine - */ -#ifdef linux -/* All Registers access - only for local access */ -#define REG_sig(reg_name, context) \ - ((context)->uc_mcontext.regs->reg_name) -/* Gpr Registers access */ -#define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context) -/* Program counter */ -#define IAR_sig(context) REG_sig(nip, context) -/* Machine State Register (Supervisor) */ -#define MSR_sig(context) REG_sig(msr, context) -/* Count register */ -#define CTR_sig(context) REG_sig(ctr, context) -/* User's integer exception register */ -#define XER_sig(context) REG_sig(xer, context) -/* Link register */ -#define LR_sig(context) REG_sig(link, context) -/* Condition register */ -#define CR_sig(context) REG_sig(ccr, context) - -/* Float Registers access */ -#define FLOAT_sig(reg_num, context) \ - (((double *)((char *)((context)->uc_mcontext.regs + 48 * 4)))[reg_num]) -#define FPSCR_sig(context) \ - (*(int *)((char *)((context)->uc_mcontext.regs + (48 + 32 * 2) * 4))) -/* Exception Registers access */ -#define DAR_sig(context) REG_sig(dar, context) -#define DSISR_sig(context) REG_sig(dsisr, context) -#define TRAP_sig(context) REG_sig(trap, context) -#endif /* linux */ - -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -#include -#define IAR_sig(context) ((context)->uc_mcontext.mc_srr0) -#define MSR_sig(context) ((context)->uc_mcontext.mc_srr1) -#define CTR_sig(context) ((context)->uc_mcontext.mc_ctr) -#define XER_sig(context) ((context)->uc_mcontext.mc_xer) -#define LR_sig(context) ((context)->uc_mcontext.mc_lr) -#define CR_sig(context) ((context)->uc_mcontext.mc_cr) -/* Exception Registers access */ -#define DAR_sig(context) ((context)->uc_mcontext.mc_dar) -#define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr) -#define TRAP_sig(context) ((context)->uc_mcontext.mc_exc) -#endif /* __FreeBSD__|| __FreeBSD_kernel__ */ - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - ucontext_t *uc = puc; -#else - ucontext_t *uc = puc; -#endif - unsigned long pc; - int is_write; - - pc = IAR_sig(uc); - is_write = 0; -#if 0 - /* ppc 4xx case */ - if (DSISR_sig(uc) & 0x00800000) { - is_write = 1; - } -#else - if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) { - is_write = 1; - } -#endif - return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); -} - -#elif defined(__alpha__) +#if defined(__alpha__) int cpu_signal_handler(int host_signum, void *pinfo, void *puc) diff --git a/linux-user/host/ppc/host-signal.h b/linux-user/host/ppc/host-signal.h index f4b4d65031..a491c413dc 100644 --- a/linux-user/host/ppc/host-signal.h +++ b/linux-user/host/ppc/host-signal.h @@ -1 +1,25 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef PPC_HOST_SIGNAL_H +#define PPC_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.regs->nip; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + return uc->uc_mcontext.regs->trap != 0x400 + && (uc->uc_mcontext.regs->dsisr & 0x02000000); +} + +#endif diff --git a/linux-user/host/ppc64/host-signal.h b/linux-user/host/ppc64/host-signal.h index f4b4d65031..a353c22a90 100644 --- a/linux-user/host/ppc64/host-signal.h +++ b/linux-user/host/ppc64/host-signal.h @@ -1 +1 @@ -#define HOST_SIGNAL_PLACEHOLDER +#include "../ppc/host-signal.h" From 44c8f2cd905434358a292f581f33f434f374327b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 10:05:32 -0700 Subject: [PATCH 0972/1334] linux-user/host/alpha: Populate host_signal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split host_signal_pc and host_signal_write out of user-exec.c. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 31 +-------------------- linux-user/host/alpha/host-signal.h | 42 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 linux-user/host/alpha/host-signal.h diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 5a0a65fa46..e9b6eb696f 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -253,36 +253,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(__alpha__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - ucontext_t *uc = puc; - uint32_t *pc = uc->uc_mcontext.sc_pc; - uint32_t insn = *pc; - int is_write = 0; - - /* XXX: need kernel patch to get write flag faster */ - switch (insn >> 26) { - case 0x0d: /* stw */ - case 0x0e: /* stb */ - case 0x0f: /* stq_u */ - case 0x24: /* stf */ - case 0x25: /* stg */ - case 0x26: /* sts */ - case 0x27: /* stt */ - case 0x2c: /* stl */ - case 0x2d: /* stq */ - case 0x2e: /* stl_c */ - case 0x2f: /* stq_c */ - is_write = 1; - } - - return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); -} -#elif defined(__sparc__) +#if defined(__sparc__) int cpu_signal_handler(int host_signum, void *pinfo, void *puc) diff --git a/linux-user/host/alpha/host-signal.h b/linux-user/host/alpha/host-signal.h new file mode 100644 index 0000000000..e080be412f --- /dev/null +++ b/linux-user/host/alpha/host-signal.h @@ -0,0 +1,42 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef ALPHA_HOST_SIGNAL_H +#define ALPHA_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.sc_pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint32_t *pc = (uint32_t *)host_signal_pc(uc); + uint32_t insn = *pc; + + /* XXX: need kernel patch to get write flag faster */ + switch (insn >> 26) { + case 0x0d: /* stw */ + case 0x0e: /* stb */ + case 0x0f: /* stq_u */ + case 0x24: /* stf */ + case 0x25: /* stg */ + case 0x26: /* sts */ + case 0x27: /* stt */ + case 0x2c: /* stl */ + case 0x2d: /* stq */ + case 0x2e: /* stl_c */ + case 0x2f: /* stq_c */ + return true; + } + return false; +} + +#endif From 8b5bd461935b7682302366f95ad192434fd99e67 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 10:14:26 -0700 Subject: [PATCH 0973/1334] linux-user/host/sparc: Populate host_signal.h Split host_signal_pc and host_signal_write out of user-exec.c. Drop the *BSD code, to be re-created under bsd-user/ later. Drop the Solaris code as completely unused. Reviewed-by: Warner Losh Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 62 +-------------------------- linux-user/host/sparc/host-signal.h | 55 +++++++++++++++++++++++- linux-user/host/sparc64/host-signal.h | 2 +- 3 files changed, 56 insertions(+), 63 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index e9b6eb696f..694eff7f04 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -253,67 +253,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(__sparc__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - int is_write; - uint32_t insn; -#if !defined(__arch64__) || defined(CONFIG_SOLARIS) - uint32_t *regs = (uint32_t *)(info + 1); - void *sigmask = (regs + 20); - /* XXX: is there a standard glibc define ? */ - unsigned long pc = regs[1]; -#else -#ifdef __linux__ - struct sigcontext *sc = puc; - unsigned long pc = sc->sigc_regs.tpc; - void *sigmask = (void *)sc->sigc_mask; -#elif defined(__OpenBSD__) - struct sigcontext *uc = puc; - unsigned long pc = uc->sc_pc; - void *sigmask = (void *)(long)uc->sc_mask; -#elif defined(__NetBSD__) - ucontext_t *uc = puc; - unsigned long pc = _UC_MACHINE_PC(uc); - void *sigmask = (void *)&uc->uc_sigmask; -#endif -#endif - - /* XXX: need kernel patch to get write flag faster */ - is_write = 0; - insn = *(uint32_t *)pc; - if ((insn >> 30) == 3) { - switch ((insn >> 19) & 0x3f) { - case 0x05: /* stb */ - case 0x15: /* stba */ - case 0x06: /* sth */ - case 0x16: /* stha */ - case 0x04: /* st */ - case 0x14: /* sta */ - case 0x07: /* std */ - case 0x17: /* stda */ - case 0x0e: /* stx */ - case 0x1e: /* stxa */ - case 0x24: /* stf */ - case 0x34: /* stfa */ - case 0x27: /* stdf */ - case 0x37: /* stdfa */ - case 0x26: /* stqf */ - case 0x36: /* stqfa */ - case 0x25: /* stfsr */ - case 0x3c: /* casa */ - case 0x3e: /* casxa */ - is_write = 1; - break; - } - } - return handle_cpu_signal(pc, info, is_write, sigmask); -} - -#elif defined(__arm__) +#if defined(__arm__) #if defined(__NetBSD__) #include diff --git a/linux-user/host/sparc/host-signal.h b/linux-user/host/sparc/host-signal.h index f4b4d65031..5e71d33f8e 100644 --- a/linux-user/host/sparc/host-signal.h +++ b/linux-user/host/sparc/host-signal.h @@ -1 +1,54 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef SPARC_HOST_SIGNAL_H +#define SPARC_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ +#ifdef __arch64__ + return uc->uc_mcontext.mc_gregs[MC_PC]; +#else + return uc->uc_mcontext.gregs[REG_PC]; +#endif +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint32_t insn = *(uint32_t *)host_signal_pc(uc); + + if ((insn >> 30) == 3) { + switch ((insn >> 19) & 0x3f) { + case 0x05: /* stb */ + case 0x15: /* stba */ + case 0x06: /* sth */ + case 0x16: /* stha */ + case 0x04: /* st */ + case 0x14: /* sta */ + case 0x07: /* std */ + case 0x17: /* stda */ + case 0x0e: /* stx */ + case 0x1e: /* stxa */ + case 0x24: /* stf */ + case 0x34: /* stfa */ + case 0x27: /* stdf */ + case 0x37: /* stdfa */ + case 0x26: /* stqf */ + case 0x36: /* stqfa */ + case 0x25: /* stfsr */ + case 0x3c: /* casa */ + case 0x3e: /* casxa */ + return true; + } + } + return false; +} + +#endif diff --git a/linux-user/host/sparc64/host-signal.h b/linux-user/host/sparc64/host-signal.h index f4b4d65031..1191fe2d40 100644 --- a/linux-user/host/sparc64/host-signal.h +++ b/linux-user/host/sparc64/host-signal.h @@ -1 +1 @@ -#define HOST_SIGNAL_PLACEHOLDER +#include "../sparc/host-signal.h" From a30bfaa7bd1f721a5b936fd1b4bf009eb9c2a6f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 10:34:42 -0700 Subject: [PATCH 0974/1334] linux-user/host/arm: Populate host_signal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split host_signal_pc and host_signal_write out of user-exec.c. Drop the *BSD code, to be re-created under bsd-user/ later. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 45 +------------------------------ linux-user/host/arm/host-signal.h | 31 ++++++++++++++++++++- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 694eff7f04..fabc8855a9 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -253,50 +253,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(__arm__) - -#if defined(__NetBSD__) -#include -#include -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; -#if defined(__NetBSD__) - ucontext_t *uc = puc; - siginfo_t *si = pinfo; -#else - ucontext_t *uc = puc; -#endif - unsigned long pc; - uint32_t fsr; - int is_write; - -#if defined(__NetBSD__) - pc = uc->uc_mcontext.__gregs[_REG_R15]; -#elif defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) - pc = uc->uc_mcontext.gregs[R15]; -#else - pc = uc->uc_mcontext.arm_pc; -#endif - -#ifdef __NetBSD__ - fsr = si->si_trap; -#else - fsr = uc->uc_mcontext.error_code; -#endif - /* - * In the FSR, bit 11 is WnR, assuming a v6 or - * later processor. On v5 we will always report - * this as a read, which will fail later. - */ - is_write = extract32(fsr, 11, 1); - return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); -} - -#elif defined(__aarch64__) +#if defined(__aarch64__) #if defined(__NetBSD__) diff --git a/linux-user/host/arm/host-signal.h b/linux-user/host/arm/host-signal.h index f4b4d65031..efb165c0c5 100644 --- a/linux-user/host/arm/host-signal.h +++ b/linux-user/host/arm/host-signal.h @@ -1 +1,30 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef ARM_HOST_SIGNAL_H +#define ARM_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.arm_pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * In the FSR, bit 11 is WnR, assuming a v6 or + * later processor. On v5 we will always report + * this as a read, which will fail later. + */ + uint32_t fsr = uc->uc_mcontext.error_code; + return extract32(fsr, 11, 1); +} + +#endif From cf5f42fd07fa7ca4cfcf940093ef2506f990d1db Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 10:39:15 -0700 Subject: [PATCH 0975/1334] linux-user/host/aarch64: Populate host_signal.h Split host_signal_pc and host_signal_write out of user-exec.c. Drop the *BSD code, to be re-created under bsd-user/ later. Reviewed-by: Warner Losh Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 94 +-------------------------- linux-user/host/aarch64/host-signal.h | 75 ++++++++++++++++++++- 2 files changed, 75 insertions(+), 94 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index fabc8855a9..5cdbfab35b 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -253,99 +253,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(__aarch64__) - -#if defined(__NetBSD__) - -#include -#include - -int cpu_signal_handler(int host_signum, void *pinfo, void *puc) -{ - ucontext_t *uc = puc; - siginfo_t *si = pinfo; - unsigned long pc; - int is_write; - uint32_t esr; - - pc = uc->uc_mcontext.__gregs[_REG_PC]; - esr = si->si_trap; - - /* - * siginfo_t::si_trap is the ESR value, for data aborts ESR.EC - * is 0b10010x: then bit 6 is the WnR bit - */ - is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; - return handle_cpu_signal(pc, si, is_write, &uc->uc_sigmask); -} - -#else - -#ifndef ESR_MAGIC -/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ -#define ESR_MAGIC 0x45535201 -struct esr_context { - struct _aarch64_ctx head; - uint64_t esr; -}; -#endif - -static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) -{ - return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; -} - -static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) -{ - return (struct _aarch64_ctx *)((char *)hdr + hdr->size); -} - -int cpu_signal_handler(int host_signum, void *pinfo, void *puc) -{ - siginfo_t *info = pinfo; - ucontext_t *uc = puc; - uintptr_t pc = uc->uc_mcontext.pc; - bool is_write; - struct _aarch64_ctx *hdr; - struct esr_context const *esrctx = NULL; - - /* Find the esr_context, which has the WnR bit in it */ - for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { - if (hdr->magic == ESR_MAGIC) { - esrctx = (struct esr_context const *)hdr; - break; - } - } - - if (esrctx) { - /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */ - uint64_t esr = esrctx->esr; - is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; - } else { - /* - * Fall back to parsing instructions; will only be needed - * for really ancient (pre-3.16) kernels. - */ - uint32_t insn = *(uint32_t *)pc; - - is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */ - || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */ - || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */ - || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */ - || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */ - || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */ - || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */ - /* Ignore bits 10, 11 & 21, controlling indexing. */ - || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */ - || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */ - /* Ignore bits 23 & 24, controlling indexing. */ - || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ - } - return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); -} -#endif - -#elif defined(__s390__) +#if defined(__s390__) int cpu_signal_handler(int host_signum, void *pinfo, void *puc) diff --git a/linux-user/host/aarch64/host-signal.h b/linux-user/host/aarch64/host-signal.h index f4b4d65031..0c0b08383a 100644 --- a/linux-user/host/aarch64/host-signal.h +++ b/linux-user/host/aarch64/host-signal.h @@ -1 +1,74 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef AARCH64_HOST_SIGNAL_H +#define AARCH64_HOST_SIGNAL_H + +/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ +#ifndef ESR_MAGIC +#define ESR_MAGIC 0x45535201 +struct esr_context { + struct _aarch64_ctx head; + uint64_t esr; +}; +#endif + +static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) +{ + return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; +} + +static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) +{ + return (struct _aarch64_ctx *)((char *)hdr + hdr->size); +} + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + struct _aarch64_ctx *hdr; + uint32_t insn; + + /* Find the esr_context, which has the WnR bit in it */ + for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { + if (hdr->magic == ESR_MAGIC) { + struct esr_context const *ec = (struct esr_context const *)hdr; + uint64_t esr = ec->esr; + + /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */ + return extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; + } + } + + /* + * Fall back to parsing instructions; will only be needed + * for really ancient (pre-3.16) kernels. + */ + insn = *(uint32_t *)host_signal_pc(uc); + + return (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */ + || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */ + || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */ + || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */ + || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */ + || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */ + || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */ + /* Ignore bits 10, 11 & 21, controlling indexing. */ + || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */ + || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */ + /* Ignore bits 23 & 24, controlling indexing. */ + || (insn & 0x3a400000) == 0x28000000; /* C3.3.7,14-16 */ +} + +#endif From 66ee11d407c07cd5e093fabb48e81232388189d0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 10:44:05 -0700 Subject: [PATCH 0976/1334] linux-user/host/s390: Populate host_signal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split host_signal_pc and host_signal_write out of user-exec.c. Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 88 +-------------------------- linux-user/host/s390/host-signal.h | 94 ++++++++++++++++++++++++++++- linux-user/host/s390x/host-signal.h | 2 +- 3 files changed, 95 insertions(+), 89 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 5cdbfab35b..f18f3b2a5c 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -253,93 +253,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(__s390__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - ucontext_t *uc = puc; - unsigned long pc; - uint16_t *pinsn; - int is_write = 0; - - pc = uc->uc_mcontext.psw.addr; - - /* - * ??? On linux, the non-rt signal handler has 4 (!) arguments instead - * of the normal 2 arguments. The 4th argument contains the "Translation- - * Exception Identification for DAT Exceptions" from the hardware (aka - * "int_parm_long"), which does in fact contain the is_write value. - * The rt signal handler, as far as I can tell, does not give this value - * at all. Not that we could get to it from here even if it were. - * So fall back to parsing instructions. Treat read-modify-write ones as - * writes, which is not fully correct, but for tracking self-modifying code - * this is better than treating them as reads. Checking si_addr page flags - * might be a viable improvement, albeit a racy one. - */ - /* ??? This is not even close to complete. */ - pinsn = (uint16_t *)pc; - switch (pinsn[0] >> 8) { - case 0x50: /* ST */ - case 0x42: /* STC */ - case 0x40: /* STH */ - case 0xba: /* CS */ - case 0xbb: /* CDS */ - is_write = 1; - break; - case 0xc4: /* RIL format insns */ - switch (pinsn[0] & 0xf) { - case 0xf: /* STRL */ - case 0xb: /* STGRL */ - case 0x7: /* STHRL */ - is_write = 1; - } - break; - case 0xc8: /* SSF format insns */ - switch (pinsn[0] & 0xf) { - case 0x2: /* CSST */ - is_write = 1; - } - break; - case 0xe3: /* RXY format insns */ - switch (pinsn[2] & 0xff) { - case 0x50: /* STY */ - case 0x24: /* STG */ - case 0x72: /* STCY */ - case 0x70: /* STHY */ - case 0x8e: /* STPQ */ - case 0x3f: /* STRVH */ - case 0x3e: /* STRV */ - case 0x2f: /* STRVG */ - is_write = 1; - } - break; - case 0xeb: /* RSY format insns */ - switch (pinsn[2] & 0xff) { - case 0x14: /* CSY */ - case 0x30: /* CSG */ - case 0x31: /* CDSY */ - case 0x3e: /* CDSG */ - case 0xe4: /* LANG */ - case 0xe6: /* LAOG */ - case 0xe7: /* LAXG */ - case 0xe8: /* LAAG */ - case 0xea: /* LAALG */ - case 0xf4: /* LAN */ - case 0xf6: /* LAO */ - case 0xf7: /* LAX */ - case 0xfa: /* LAAL */ - case 0xf8: /* LAA */ - is_write = 1; - } - break; - } - - return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); -} - -#elif defined(__mips__) +#if defined(__mips__) #if defined(__misp16) || defined(__mips_micromips) #error "Unsupported encoding" diff --git a/linux-user/host/s390/host-signal.h b/linux-user/host/s390/host-signal.h index f4b4d65031..26990e4893 100644 --- a/linux-user/host/s390/host-signal.h +++ b/linux-user/host/s390/host-signal.h @@ -1 +1,93 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef S390_HOST_SIGNAL_H +#define S390_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.psw.addr; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint16_t *pinsn = (uint16_t *)host_signal_pc(uc); + + /* + * ??? On linux, the non-rt signal handler has 4 (!) arguments instead + * of the normal 2 arguments. The 4th argument contains the "Translation- + * Exception Identification for DAT Exceptions" from the hardware (aka + * "int_parm_long"), which does in fact contain the is_write value. + * The rt signal handler, as far as I can tell, does not give this value + * at all. Not that we could get to it from here even if it were. + * So fall back to parsing instructions. Treat read-modify-write ones as + * writes, which is not fully correct, but for tracking self-modifying code + * this is better than treating them as reads. Checking si_addr page flags + * might be a viable improvement, albeit a racy one. + */ + /* ??? This is not even close to complete. */ + switch (pinsn[0] >> 8) { + case 0x50: /* ST */ + case 0x42: /* STC */ + case 0x40: /* STH */ + case 0xba: /* CS */ + case 0xbb: /* CDS */ + return true; + case 0xc4: /* RIL format insns */ + switch (pinsn[0] & 0xf) { + case 0xf: /* STRL */ + case 0xb: /* STGRL */ + case 0x7: /* STHRL */ + return true; + } + break; + case 0xc8: /* SSF format insns */ + switch (pinsn[0] & 0xf) { + case 0x2: /* CSST */ + return true; + } + break; + case 0xe3: /* RXY format insns */ + switch (pinsn[2] & 0xff) { + case 0x50: /* STY */ + case 0x24: /* STG */ + case 0x72: /* STCY */ + case 0x70: /* STHY */ + case 0x8e: /* STPQ */ + case 0x3f: /* STRVH */ + case 0x3e: /* STRV */ + case 0x2f: /* STRVG */ + return true; + } + break; + case 0xeb: /* RSY format insns */ + switch (pinsn[2] & 0xff) { + case 0x14: /* CSY */ + case 0x30: /* CSG */ + case 0x31: /* CDSY */ + case 0x3e: /* CDSG */ + case 0xe4: /* LANG */ + case 0xe6: /* LAOG */ + case 0xe7: /* LAXG */ + case 0xe8: /* LAAG */ + case 0xea: /* LAALG */ + case 0xf4: /* LAN */ + case 0xf6: /* LAO */ + case 0xf7: /* LAX */ + case 0xfa: /* LAAL */ + case 0xf8: /* LAA */ + return true; + } + break; + } + return false; +} + +#endif diff --git a/linux-user/host/s390x/host-signal.h b/linux-user/host/s390x/host-signal.h index f4b4d65031..0e83f9358d 100644 --- a/linux-user/host/s390x/host-signal.h +++ b/linux-user/host/s390x/host-signal.h @@ -1 +1 @@ -#define HOST_SIGNAL_PLACEHOLDER +#include "../s390/host-signal.h" From b12161120af8467bc28159adf0c1bfb0fbc4ed70 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 10:50:14 -0700 Subject: [PATCH 0977/1334] linux-user/host/mips: Populate host_signal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split host_signal_pc and host_signal_write out of user-exec.c. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 52 +----------------------- linux-user/host/mips/host-signal.h | 63 +++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index f18f3b2a5c..44c83acba5 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -253,57 +253,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(__mips__) - -#if defined(__misp16) || defined(__mips_micromips) -#error "Unsupported encoding" -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - ucontext_t *uc = puc; - uintptr_t pc = uc->uc_mcontext.pc; - uint32_t insn = *(uint32_t *)pc; - int is_write = 0; - - /* Detect all store instructions at program counter. */ - switch((insn >> 26) & 077) { - case 050: /* SB */ - case 051: /* SH */ - case 052: /* SWL */ - case 053: /* SW */ - case 054: /* SDL */ - case 055: /* SDR */ - case 056: /* SWR */ - case 070: /* SC */ - case 071: /* SWC1 */ - case 074: /* SCD */ - case 075: /* SDC1 */ - case 077: /* SD */ -#if !defined(__mips_isa_rev) || __mips_isa_rev < 6 - case 072: /* SWC2 */ - case 076: /* SDC2 */ -#endif - is_write = 1; - break; - case 023: /* COP1X */ - /* Required in all versions of MIPS64 since - MIPS64r1 and subsequent versions of MIPS32r2. */ - switch (insn & 077) { - case 010: /* SWXC1 */ - case 011: /* SDXC1 */ - case 015: /* SUXC1 */ - is_write = 1; - } - break; - } - - return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); -} - -#elif defined(__riscv) +#if defined(__riscv) int cpu_signal_handler(int host_signum, void *pinfo, void *puc) diff --git a/linux-user/host/mips/host-signal.h b/linux-user/host/mips/host-signal.h index f4b4d65031..ef341f7c20 100644 --- a/linux-user/host/mips/host-signal.h +++ b/linux-user/host/mips/host-signal.h @@ -1 +1,62 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef MIPS_HOST_SIGNAL_H +#define MIPS_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.pc; +} + +#if defined(__misp16) || defined(__mips_micromips) +#error "Unsupported encoding" +#endif + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint32_t insn = *(uint32_t *)host_signal_pc(uc); + + /* Detect all store instructions at program counter. */ + switch ((insn >> 26) & 077) { + case 050: /* SB */ + case 051: /* SH */ + case 052: /* SWL */ + case 053: /* SW */ + case 054: /* SDL */ + case 055: /* SDR */ + case 056: /* SWR */ + case 070: /* SC */ + case 071: /* SWC1 */ + case 074: /* SCD */ + case 075: /* SDC1 */ + case 077: /* SD */ +#if !defined(__mips_isa_rev) || __mips_isa_rev < 6 + case 072: /* SWC2 */ + case 076: /* SDC2 */ +#endif + return true; + case 023: /* COP1X */ + /* + * Required in all versions of MIPS64 since + * MIPS64r1 and subsequent versions of MIPS32r2. + */ + switch (insn & 077) { + case 010: /* SWXC1 */ + case 011: /* SDXC1 */ + case 015: /* SUXC1 */ + return true; + } + break; + } + return false; +} + +#endif From 97be8c6a95138291ee8736690e8bc0dd6db9e27e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 10:57:06 -0700 Subject: [PATCH 0978/1334] linux-user/host/riscv: Populate host_signal.h Split host_signal_pc and host_signal_write out of user-exec.c. Reviewed-by: Warner Losh Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 134 ---------------------------- linux-user/host/riscv/host-signal.h | 86 +++++++++++++++++- 2 files changed, 85 insertions(+), 135 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 44c83acba5..a0cba61e83 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -137,64 +137,6 @@ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, } } -/* - * 'pc' is the host PC at which the exception was raised. - * 'address' is the effective address of the memory exception. - * 'is_write' is 1 if a write caused the exception and otherwise 0. - * 'old_set' is the signal set which should be restored. - */ -static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, - int is_write, sigset_t *old_set) -{ - CPUState *cpu = current_cpu; - CPUClass *cc; - unsigned long host_addr = (unsigned long)info->si_addr; - MMUAccessType access_type = adjust_signal_pc(&pc, is_write); - abi_ptr guest_addr; - - /* For synchronous signals we expect to be coming from the vCPU - * thread (so current_cpu should be valid) and either from running - * code or during translation which can fault as we cross pages. - * - * If neither is true then something has gone wrong and we should - * abort rather than try and restart the vCPU execution. - */ - if (!cpu || !cpu->running) { - printf("qemu:%s received signal outside vCPU context @ pc=0x%" - PRIxPTR "\n", __func__, pc); - abort(); - } - -#if defined(DEBUG_SIGNAL) - printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, host_addr, is_write, *(unsigned long *)old_set); -#endif - - /* Convert forcefully to guest address space, invalid addresses - are still valid segv ones */ - guest_addr = h2g_nocheck(host_addr); - - /* XXX: locking issue */ - if (is_write && - info->si_signo == SIGSEGV && - info->si_code == SEGV_ACCERR && - h2g_valid(host_addr) && - handle_sigsegv_accerr_write(cpu, old_set, pc, guest_addr)) { - return 1; - } - - /* - * There is no way the target can handle this other than raising - * an exception. Undo signal and retaddr state prior to longjmp. - */ - sigprocmask(SIG_SETMASK, old_set, NULL); - - cc = CPU_GET_CLASS(cpu); - cc->tcg_ops->tlb_fill(cpu, guest_addr, 0, access_type, - MMU_USER_IDX, false, pc); - g_assert_not_reached(); -} - static int probe_access_internal(CPUArchState *env, target_ulong addr, int fault_size, MMUAccessType access_type, bool nonfault, uintptr_t ra) @@ -253,82 +195,6 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } -#if defined(__riscv) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - ucontext_t *uc = puc; - greg_t pc = uc->uc_mcontext.__gregs[REG_PC]; - uint32_t insn = *(uint32_t *)pc; - int is_write = 0; - - /* Detect store by reading the instruction at the program - counter. Note: we currently only generate 32-bit - instructions so we thus only detect 32-bit stores */ - switch (((insn >> 0) & 0b11)) { - case 3: - switch (((insn >> 2) & 0b11111)) { - case 8: - switch (((insn >> 12) & 0b111)) { - case 0: /* sb */ - case 1: /* sh */ - case 2: /* sw */ - case 3: /* sd */ - case 4: /* sq */ - is_write = 1; - break; - default: - break; - } - break; - case 9: - switch (((insn >> 12) & 0b111)) { - case 2: /* fsw */ - case 3: /* fsd */ - case 4: /* fsq */ - is_write = 1; - break; - default: - break; - } - break; - default: - break; - } - } - - /* Check for compressed instructions */ - switch (((insn >> 13) & 0b111)) { - case 7: - switch (insn & 0b11) { - case 0: /*c.sd */ - case 2: /* c.sdsp */ - is_write = 1; - break; - default: - break; - } - break; - case 6: - switch (insn & 0b11) { - case 0: /* c.sw */ - case 3: /* c.swsp */ - is_write = 1; - break; - default: - break; - } - break; - default: - break; - } - - return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); -} -#endif - /* The softmmu versions of these helpers are in cputlb.c. */ /* diff --git a/linux-user/host/riscv/host-signal.h b/linux-user/host/riscv/host-signal.h index f4b4d65031..df145b1527 100644 --- a/linux-user/host/riscv/host-signal.h +++ b/linux-user/host/riscv/host-signal.h @@ -1 +1,85 @@ -#define HOST_SIGNAL_PLACEHOLDER +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef RISCV_HOST_SIGNAL_H +#define RISCV_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.__gregs[REG_PC]; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint32_t insn = *(uint32_t *)host_signal_pc(uc); + + /* + * Detect store by reading the instruction at the program + * counter. Note: we currently only generate 32-bit + * instructions so we thus only detect 32-bit stores + */ + switch (((insn >> 0) & 0b11)) { + case 3: + switch (((insn >> 2) & 0b11111)) { + case 8: + switch (((insn >> 12) & 0b111)) { + case 0: /* sb */ + case 1: /* sh */ + case 2: /* sw */ + case 3: /* sd */ + case 4: /* sq */ + return true; + default: + break; + } + break; + case 9: + switch (((insn >> 12) & 0b111)) { + case 2: /* fsw */ + case 3: /* fsd */ + case 4: /* fsq */ + return true; + default: + break; + } + break; + default: + break; + } + } + + /* Check for compressed instructions */ + switch (((insn >> 13) & 0b111)) { + case 7: + switch (insn & 0b11) { + case 0: /*c.sd */ + case 2: /* c.sdsp */ + return true; + default: + break; + } + break; + case 6: + switch (insn & 0b11) { + case 0: /* c.sw */ + case 3: /* c.swsp */ + return true; + default: + break; + } + break; + default: + break; + } + + return false; +} + +#endif From 7ce8e389efc5011742194fd46847fce3be4b3124 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Sep 2021 11:08:52 -0700 Subject: [PATCH 0979/1334] target/arm: Fixup comment re handle_cpu_signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The named function no longer exists. Refer to host_signal_handler instead. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/sve_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index dab5f1d1cd..07be55b7e1 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -6118,7 +6118,7 @@ DO_LDN_2(4, dd, MO_64) * linux-user/ in its get_user/put_user macros. * * TODO: Construct some helpers, written in assembly, that interact with - * handle_cpu_signal to produce memory ops which can properly report errors + * host_signal_handler to produce memory ops which can properly report errors * without racing. */ From 4f3bbd9cfb49ac8287afd32b3916edc50e8e1850 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 11:24:14 -0700 Subject: [PATCH 0980/1334] linux-user/host/riscv: Improve host_signal_write Do not read 4 bytes before we determine the size of the insn. Simplify triple switches in favor of checking major opcodes. Include the missing cases of compact fsd and fsdsp. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- linux-user/host/riscv/host-signal.h | 83 ++++++++++------------------- 1 file changed, 28 insertions(+), 55 deletions(-) diff --git a/linux-user/host/riscv/host-signal.h b/linux-user/host/riscv/host-signal.h index df145b1527..3b168cb58b 100644 --- a/linux-user/host/riscv/host-signal.h +++ b/linux-user/host/riscv/host-signal.h @@ -18,65 +18,38 @@ static inline uintptr_t host_signal_pc(ucontext_t *uc) static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { - uint32_t insn = *(uint32_t *)host_signal_pc(uc); - /* - * Detect store by reading the instruction at the program - * counter. Note: we currently only generate 32-bit - * instructions so we thus only detect 32-bit stores + * Detect store by reading the instruction at the program counter. + * Do not read more than 16 bits, because we have not yet determined + * the size of the instruction. */ - switch (((insn >> 0) & 0b11)) { - case 3: - switch (((insn >> 2) & 0b11111)) { - case 8: - switch (((insn >> 12) & 0b111)) { - case 0: /* sb */ - case 1: /* sh */ - case 2: /* sw */ - case 3: /* sd */ - case 4: /* sq */ - return true; - default: - break; - } - break; - case 9: - switch (((insn >> 12) & 0b111)) { - case 2: /* fsw */ - case 3: /* fsd */ - case 4: /* fsq */ - return true; - default: - break; - } - break; - default: - break; - } + const uint16_t *pinsn = (const uint16_t *)host_signal_pc(uc); + uint16_t insn = pinsn[0]; + + /* 16-bit instructions */ + switch (insn & 0xe003) { + case 0xa000: /* c.fsd */ + case 0xc000: /* c.sw */ + case 0xe000: /* c.sd (rv64) / c.fsw (rv32) */ + case 0xa002: /* c.fsdsp */ + case 0xc002: /* c.swsp */ + case 0xe002: /* c.sdsp (rv64) / c.fswsp (rv32) */ + return true; } - /* Check for compressed instructions */ - switch (((insn >> 13) & 0b111)) { - case 7: - switch (insn & 0b11) { - case 0: /*c.sd */ - case 2: /* c.sdsp */ - return true; - default: - break; - } - break; - case 6: - switch (insn & 0b11) { - case 0: /* c.sw */ - case 3: /* c.swsp */ - return true; - default: - break; - } - break; - default: - break; + /* 32-bit instructions, major opcodes */ + switch (insn & 0x7f) { + case 0x23: /* store */ + case 0x27: /* store-fp */ + return true; + case 0x2f: /* amo */ + /* + * The AMO function code is in bits 25-31, unread as yet. + * The AMO functions are LR (read), SC (write), and the + * rest are all read-modify-write. + */ + insn = pinsn[1]; + return (insn >> 11) != 2; /* LR */ } return false; From 04de121aaf0c896812792eb8489ae614c7f6dade Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 12:00:31 -0700 Subject: [PATCH 0981/1334] linux-user/signal: Drop HOST_SIGNAL_PLACEHOLDER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all of the linux-user hosts have been converted to host-signal.h, drop the compatibility code. Reviewed by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/exec-all.h | 12 ------------ linux-user/signal.c | 14 -------------- 2 files changed, 26 deletions(-) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 5f94d799aa..5dd663c153 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -685,18 +685,6 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, uintptr_t host_pc, abi_ptr guest_addr); -/** - * cpu_signal_handler - * @signum: host signal number - * @pinfo: host siginfo_t - * @puc: host ucontext_t - * - * To be called from the SIGBUS and SIGSEGV signal handler to inform the - * virtual cpu of exceptions. Returns true if the signal was handled by - * the virtual CPU. - */ -int cpu_signal_handler(int signum, void *pinfo, void *puc); - #else static inline void mmap_lock(void) {} static inline void mmap_unlock(void) {} diff --git a/linux-user/signal.c b/linux-user/signal.c index 6900acb122..b816678ba5 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -780,17 +780,6 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) ucontext_t *uc = puc; struct emulated_sigtable *k; int guest_sig; - -#ifdef HOST_SIGNAL_PLACEHOLDER - /* the CPU emulator uses some host signals to detect exceptions, - we forward to it some signals */ - if ((host_sig == SIGSEGV || host_sig == SIGBUS) - && info->si_code > 0) { - if (cpu_signal_handler(host_sig, info, puc)) { - return; - } - } -#else uintptr_t pc = 0; bool sync_sig = false; @@ -850,7 +839,6 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) sync_sig = true; } -#endif /* get target signal number */ guest_sig = host_to_target_signal(host_sig); @@ -865,7 +853,6 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) k->pending = guest_sig; ts->signal_pending = 1; -#ifndef HOST_SIGNAL_PLACEHOLDER /* * For synchronous signals, unwind the cpu state to the faulting * insn and then exit back to the main loop so that the signal @@ -875,7 +862,6 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) cpu->exception_index = EXCP_INTERRUPT; cpu_loop_exit_restore(cpu, pc); } -#endif rewind_if_in_safe_syscall(puc); From 09e94676ade52708cbece8fd4bd255a25b6ee475 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 17:31:33 -0700 Subject: [PATCH 0982/1334] hw/core: Add TCGCPUOps.record_sigsegv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new user-only interface for updating cpu state before raising a signal. This will replace tlb_fill for user-only and should result in less boilerplate for each guest. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/hw/core/tcg-cpu-ops.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 6cbe17f2e6..41718b695b 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -111,6 +111,32 @@ struct TCGCPUOps { */ bool (*io_recompile_replay_branch)(CPUState *cpu, const TranslationBlock *tb); +#else + /** + * record_sigsegv: + * @cpu: cpu context + * @addr: faulting guest address + * @access_type: access was read/write/execute + * @maperr: true for invalid page, false for permission fault + * @ra: host pc for unwinding + * + * We are about to raise SIGSEGV with si_code set for @maperr, + * and si_addr set for @addr. Record anything further needed + * for the signal ucontext_t. + * + * If the emulated kernel does not provide anything to the signal + * handler with anything besides the user context registers, and + * the siginfo_t, then this hook need do nothing and may be omitted. + * Otherwise, record the data and return; the caller will raise + * the signal, unwind the cpu state, and return to the main loop. + * + * If it is simpler to re-use the sysemu tlb_fill code, @ra is provided + * so that a "normal" cpu exception can be raised. In this case, + * the signal must be raised by the architecture cpu_loop. + */ + void (*record_sigsegv)(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); #endif /* CONFIG_SOFTMMU */ #endif /* NEED_CPU_H */ From 72d2bbf9ff3e1edf5d57b53149eeaa36f19fb891 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 17:32:56 -0700 Subject: [PATCH 0983/1334] linux-user: Add cpu_loop_exit_sigsegv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a new interface to be provided by the os emulator for raising SIGSEGV on fault. Use the new record_sigsegv target hook. Reviewed by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 33 ++++++++++++++++++--------------- include/exec/exec-all.h | 15 +++++++++++++++ linux-user/signal.c | 30 ++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index a0cba61e83..c4f69908e9 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -141,35 +141,38 @@ static int probe_access_internal(CPUArchState *env, target_ulong addr, int fault_size, MMUAccessType access_type, bool nonfault, uintptr_t ra) { - int flags; + int acc_flag; + bool maperr; switch (access_type) { case MMU_DATA_STORE: - flags = PAGE_WRITE; + acc_flag = PAGE_WRITE_ORG; break; case MMU_DATA_LOAD: - flags = PAGE_READ; + acc_flag = PAGE_READ; break; case MMU_INST_FETCH: - flags = PAGE_EXEC; + acc_flag = PAGE_EXEC; break; default: g_assert_not_reached(); } - if (!guest_addr_valid_untagged(addr) || - page_check_range(addr, 1, flags) < 0) { - if (nonfault) { - return TLB_INVALID_MASK; - } else { - CPUState *cpu = env_cpu(env); - CPUClass *cc = CPU_GET_CLASS(cpu); - cc->tcg_ops->tlb_fill(cpu, addr, fault_size, access_type, - MMU_USER_IDX, false, ra); - g_assert_not_reached(); + if (guest_addr_valid_untagged(addr)) { + int page_flags = page_get_flags(addr); + if (page_flags & acc_flag) { + return 0; /* success */ } + maperr = !(page_flags & PAGE_VALID); + } else { + maperr = true; } - return 0; + + if (nonfault) { + return TLB_INVALID_MASK; + } + + cpu_loop_exit_sigsegv(env_cpu(env), addr, access_type, maperr, ra); } int probe_access_flags(CPUArchState *env, target_ulong addr, diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 5dd663c153..f74578500c 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -685,6 +685,21 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, uintptr_t host_pc, abi_ptr guest_addr); +/** + * cpu_loop_exit_sigsegv: + * @cpu: the cpu context + * @addr: the guest address of the fault + * @access_type: access was read/write/execute + * @maperr: true for invalid page, false for permission fault + * @ra: host pc for unwinding + * + * Use the TCGCPUOps hook to record cpu state, do guest operating system + * specific things to raise SIGSEGV, and jump to the main cpu loop. + */ +void QEMU_NORETURN cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); + #else static inline void mmap_lock(void) {} static inline void mmap_unlock(void) {} diff --git a/linux-user/signal.c b/linux-user/signal.c index b816678ba5..135983747d 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -688,9 +688,27 @@ void force_sigsegv(int oldsig) } force_sig(TARGET_SIGSEGV); } - #endif +void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, bool maperr, uintptr_t ra) +{ + const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + + if (tcg_ops->record_sigsegv) { + tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); + } else if (tcg_ops->tlb_fill) { + tcg_ops->tlb_fill(cpu, addr, 0, access_type, MMU_USER_IDX, false, ra); + g_assert_not_reached(); + } + + force_sig_fault(TARGET_SIGSEGV, + maperr ? TARGET_SEGV_MAPERR : TARGET_SEGV_ACCERR, + addr); + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, ra); +} + /* abort execution with signal */ static void QEMU_NORETURN dump_core_and_abort(int target_sig) { @@ -806,7 +824,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) access_type = adjust_signal_pc(&pc, is_write); if (host_sig == SIGSEGV) { - const struct TCGCPUOps *tcg_ops; + bool maperr = true; if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) { /* If this was a write to a TB protected page, restart. */ @@ -821,18 +839,14 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) * which means that we may get ACCERR when we want MAPERR. */ if (page_get_flags(guest_addr) & PAGE_VALID) { - /* maperr = false; */ + maperr = false; } else { info->si_code = SEGV_MAPERR; } } sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); - - tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; - tcg_ops->tlb_fill(cpu, guest_addr, 0, access_type, - MMU_USER_IDX, false, pc); - g_assert_not_reached(); + cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); } else { sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); } From 90113883af311121e22caf505eab55e2eea1aa8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 5 Oct 2021 19:31:14 -0700 Subject: [PATCH 0984/1334] target/alpha: Implement alpha_cpu_record_sigsegv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record trap_arg{0,1,2} for the linux-user signal frame. Fill in the stores to trap_arg{1,2} that were missing from the previous user-only alpha_cpu_tlb_fill function. Use maperr to simplify computation of trap_arg1. Remove the code for EXCP_MMFAULT from cpu_loop, as that part is now handled by cpu_loop_exit_sigsegv. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/alpha/cpu_loop.c | 8 -------- target/alpha/cpu.c | 6 ++++-- target/alpha/cpu.h | 13 +++++++++---- target/alpha/helper.c | 39 ++++++++++++++++++++++++++++++++----- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 1b00a81385..4cc8e0a55c 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -54,14 +54,6 @@ void cpu_loop(CPUAlphaState *env) fprintf(stderr, "External interrupt. Exit\n"); exit(EXIT_FAILURE); break; - case EXCP_MMFAULT: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID - ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); - info._sifields._sigfault._addr = env->trap_arg0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case EXCP_UNALIGN: info.si_signo = TARGET_SIGBUS; info.si_errno = 0; diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 93e16a2ffb..69f32c3078 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -218,9 +218,11 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { static const struct TCGCPUOps alpha_tcg_ops = { .initialize = alpha_translate_init, - .tlb_fill = alpha_cpu_tlb_fill, -#ifndef CONFIG_USER_ONLY +#ifdef CONFIG_USER_ONLY + .record_sigsegv = alpha_cpu_record_sigsegv, +#else + .tlb_fill = alpha_cpu_tlb_fill, .cpu_exec_interrupt = alpha_cpu_exec_interrupt, .do_interrupt = alpha_cpu_do_interrupt, .do_transaction_failed = alpha_cpu_do_transaction_failed, diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 772828cc26..d49cc36d07 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -439,9 +439,6 @@ void alpha_translate_init(void); #define CPU_RESOLVING_TYPE TYPE_ALPHA_CPU void alpha_cpu_list(void); -bool alpha_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); void QEMU_NORETURN dynamic_excp(CPUAlphaState *, uintptr_t, int, int); void QEMU_NORETURN arith_excp(CPUAlphaState *, uintptr_t, int, uint64_t); @@ -449,7 +446,15 @@ uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env); void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val); uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg); void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val); -#ifndef CONFIG_USER_ONLY + +#ifdef CONFIG_USER_ONLY +void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address, + MMUAccessType access_type, + bool maperr, uintptr_t retaddr); +#else +bool alpha_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); void alpha_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, diff --git a/target/alpha/helper.c b/target/alpha/helper.c index 81550d9e2f..b7e7f73b15 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -120,15 +120,44 @@ void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val) } #if defined(CONFIG_USER_ONLY) -bool alpha_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) +void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address, + MMUAccessType access_type, + bool maperr, uintptr_t retaddr) { AlphaCPU *cpu = ALPHA_CPU(cs); + target_ulong mmcsr, cause; - cs->exception_index = EXCP_MMFAULT; + /* Assuming !maperr, infer the missing protection. */ + switch (access_type) { + case MMU_DATA_LOAD: + mmcsr = MM_K_FOR; + cause = 0; + break; + case MMU_DATA_STORE: + mmcsr = MM_K_FOW; + cause = 1; + break; + case MMU_INST_FETCH: + mmcsr = MM_K_FOE; + cause = -1; + break; + default: + g_assert_not_reached(); + } + if (maperr) { + if (address < BIT_ULL(TARGET_VIRT_ADDR_SPACE_BITS - 1)) { + /* Userspace address, therefore page not mapped. */ + mmcsr = MM_K_TNV; + } else { + /* Kernel or invalid address. */ + mmcsr = MM_K_ACV; + } + } + + /* Record the arguments that PALcode would give to the kernel. */ cpu->env.trap_arg0 = address; - cpu_loop_exit_restore(cs, retaddr); + cpu->env.trap_arg1 = mmcsr; + cpu->env.trap_arg2 = cause; } #else /* Returns the OSF/1 entMM failure indication, or -1 on success. */ From 5e98763c0ef55fbd8fe4fe8614825ab508543d32 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 17:49:05 -0700 Subject: [PATCH 0985/1334] target/arm: Use cpu_loop_exit_sigsegv for mte tag lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new os interface for raising the exception, rather than calling arm_cpu_tlb_fill directly. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/mte_helper.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/arm/mte_helper.c b/target/arm/mte_helper.c index 724175210b..e09b7e46a2 100644 --- a/target/arm/mte_helper.c +++ b/target/arm/mte_helper.c @@ -84,10 +84,8 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, uintptr_t index; if (!(flags & (ptr_access == MMU_DATA_STORE ? PAGE_WRITE_ORG : PAGE_READ))) { - /* SIGSEGV */ - arm_cpu_tlb_fill(env_cpu(env), ptr, ptr_size, ptr_access, - ptr_mmu_idx, false, ra); - g_assert_not_reached(); + cpu_loop_exit_sigsegv(env_cpu(env), ptr, ptr_access, + !(flags & PAGE_VALID), ra); } /* Require both MAP_ANON and PROT_MTE for the page. */ From 9b12b6b44250c23cd29161ca7007559e22beaf94 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 18:23:07 -0700 Subject: [PATCH 0986/1334] target/arm: Implement arm_cpu_record_sigsegv Because of the complexity of setting ESR, continue to use arm_deliver_fault. This means we cannot remove the code within cpu_loop that decodes EXCP_DATA_ABORT and EXCP_PREFETCH_ABORT. But using the new hook means that we don't have to do the page_get_flags check manually, and we'll be able to restrict the tlb_fill hook to sysemu later. Reviewed-by: Warner Losh Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/arm/cpu.c | 6 ++++-- target/arm/cpu_tcg.c | 6 ++++-- target/arm/internals.h | 6 ++++++ target/arm/tlb_helper.c | 36 +++++++++++++++++++----------------- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 641a8c2d3d..7a18a58ca0 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2031,10 +2031,12 @@ static const struct SysemuCPUOps arm_sysemu_ops = { static const struct TCGCPUOps arm_tcg_ops = { .initialize = arm_translate_init, .synchronize_from_tb = arm_cpu_synchronize_from_tb, - .tlb_fill = arm_cpu_tlb_fill, .debug_excp_handler = arm_debug_excp_handler, -#if !defined(CONFIG_USER_ONLY) +#ifdef CONFIG_USER_ONLY + .record_sigsegv = arm_cpu_record_sigsegv, +#else + .tlb_fill = arm_cpu_tlb_fill, .cpu_exec_interrupt = arm_cpu_exec_interrupt, .do_interrupt = arm_cpu_do_interrupt, .do_transaction_failed = arm_cpu_do_transaction_failed, diff --git a/target/arm/cpu_tcg.c b/target/arm/cpu_tcg.c index 0d5adccf1a..7b3bea2fbb 100644 --- a/target/arm/cpu_tcg.c +++ b/target/arm/cpu_tcg.c @@ -898,10 +898,12 @@ static void pxa270c5_initfn(Object *obj) static const struct TCGCPUOps arm_v7m_tcg_ops = { .initialize = arm_translate_init, .synchronize_from_tb = arm_cpu_synchronize_from_tb, - .tlb_fill = arm_cpu_tlb_fill, .debug_excp_handler = arm_debug_excp_handler, -#if !defined(CONFIG_USER_ONLY) +#ifdef CONFIG_USER_ONLY + .record_sigsegv = arm_cpu_record_sigsegv, +#else + .tlb_fill = arm_cpu_tlb_fill, .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, .do_interrupt = arm_v7m_cpu_do_interrupt, .do_transaction_failed = arm_cpu_do_transaction_failed, diff --git a/target/arm/internals.h b/target/arm/internals.h index 3612107ab2..5a7aaf0f51 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -544,9 +544,15 @@ static inline bool arm_extabort_type(MemTxResult result) return result != MEMTX_DECODE_ERROR; } +#ifdef CONFIG_USER_ONLY +void arm_cpu_record_sigsegv(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); +#else bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +#endif static inline int arm_to_core_mmu_idx(ARMMMUIdx mmu_idx) { diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c index 3107f9823e..dc5860180f 100644 --- a/target/arm/tlb_helper.c +++ b/target/arm/tlb_helper.c @@ -147,28 +147,12 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, arm_deliver_fault(cpu, addr, access_type, mmu_idx, &fi); } -#endif /* !defined(CONFIG_USER_ONLY) */ - bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { ARMCPU *cpu = ARM_CPU(cs); ARMMMUFaultInfo fi = {}; - -#ifdef CONFIG_USER_ONLY - int flags = page_get_flags(useronly_clean_ptr(address)); - if (flags & PAGE_VALID) { - fi.type = ARMFault_Permission; - } else { - fi.type = ARMFault_Translation; - } - fi.level = 3; - - /* now we have a real cpu fault */ - cpu_restore_state(cs, retaddr, true); - arm_deliver_fault(cpu, address, access_type, mmu_idx, &fi); -#else hwaddr phys_addr; target_ulong page_size; int prot, ret; @@ -210,5 +194,23 @@ bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, cpu_restore_state(cs, retaddr, true); arm_deliver_fault(cpu, address, access_type, mmu_idx, &fi); } -#endif } +#else +void arm_cpu_record_sigsegv(CPUState *cs, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra) +{ + ARMMMUFaultInfo fi = { + .type = maperr ? ARMFault_Translation : ARMFault_Permission, + .level = 3, + }; + ARMCPU *cpu = ARM_CPU(cs); + + /* + * We report both ESR and FAR to signal handlers. + * For now, it's easiest to deliver the fault normally. + */ + cpu_restore_state(cs, ra, true); + arm_deliver_fault(cpu, addr, access_type, MMU_USER_IDX, &fi); +} +#endif /* !defined(CONFIG_USER_ONLY) */ From 5753605412c194a0368b892d540b9971cd3a1907 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 16:27:10 -0700 Subject: [PATCH 0987/1334] target/cris: Make cris_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for cris linux-user. Remove the code from cpu_loop that handled the unnamed 0xaa exception. This makes all of the code in helper.c sysemu only, so remove the ifdefs and move the file to cris_softmmu_ss. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/cris/cpu_loop.c | 10 ---------- target/cris/cpu.c | 4 ++-- target/cris/cpu.h | 8 ++++---- target/cris/helper.c | 18 ------------------ target/cris/meson.build | 7 +++++-- 5 files changed, 11 insertions(+), 36 deletions(-) diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c index b9085619c4..0d5d268609 100644 --- a/linux-user/cris/cpu_loop.c +++ b/linux-user/cris/cpu_loop.c @@ -37,16 +37,6 @@ void cpu_loop(CPUCRISState *env) process_queued_cpu_work(cs); switch (trapnr) { - case 0xaa: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->pregs[PR_EDA]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; diff --git a/target/cris/cpu.c b/target/cris/cpu.c index c2e7483f5b..ed6c781342 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -205,9 +205,9 @@ static const struct SysemuCPUOps cris_sysemu_ops = { static const struct TCGCPUOps crisv10_tcg_ops = { .initialize = cris_initialize_crisv10_tcg, - .tlb_fill = cris_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = cris_cpu_tlb_fill, .cpu_exec_interrupt = cris_cpu_exec_interrupt, .do_interrupt = crisv10_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ @@ -215,9 +215,9 @@ static const struct TCGCPUOps crisv10_tcg_ops = { static const struct TCGCPUOps crisv32_tcg_ops = { .initialize = cris_initialize_tcg, - .tlb_fill = cris_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = cris_cpu_tlb_fill, .cpu_exec_interrupt = cris_cpu_exec_interrupt, .do_interrupt = cris_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ diff --git a/target/cris/cpu.h b/target/cris/cpu.h index 6603565f83..b445b194ea 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -189,6 +189,10 @@ extern const VMStateDescription vmstate_cris_cpu; void cris_cpu_do_interrupt(CPUState *cpu); void crisv10_cpu_do_interrupt(CPUState *cpu); bool cris_cpu_exec_interrupt(CPUState *cpu, int int_req); + +bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); #endif void cris_cpu_dump_state(CPUState *cs, FILE *f, int flags); @@ -251,10 +255,6 @@ static inline int cpu_mmu_index (CPUCRISState *env, bool ifetch) return !!(env->pregs[PR_CCS] & U_FLAG); } -bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); - /* Support function regs. */ #define SFR_RW_GC_CFG 0][0 #define SFR_RW_MM_CFG env->pregs[PR_SRS]][0 diff --git a/target/cris/helper.c b/target/cris/helper.c index 36926faf32..a0d6ecdcd3 100644 --- a/target/cris/helper.c +++ b/target/cris/helper.c @@ -39,22 +39,6 @@ #define D_LOG(...) do { } while (0) #endif -#if defined(CONFIG_USER_ONLY) - -bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - CRISCPU *cpu = CRIS_CPU(cs); - - cs->exception_index = 0xaa; - cpu->env.pregs[PR_EDA] = address; - cpu_loop_exit_restore(cs, retaddr); -} - -#else /* !CONFIG_USER_ONLY */ - - static void cris_shift_ccs(CPUCRISState *env) { uint32_t ccs; @@ -304,5 +288,3 @@ bool cris_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return ret; } - -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/cris/meson.build b/target/cris/meson.build index 67c3793c85..c1e326d950 100644 --- a/target/cris/meson.build +++ b/target/cris/meson.build @@ -2,13 +2,16 @@ cris_ss = ss.source_set() cris_ss.add(files( 'cpu.c', 'gdbstub.c', - 'helper.c', 'op_helper.c', 'translate.c', )) cris_softmmu_ss = ss.source_set() -cris_softmmu_ss.add(files('mmu.c', 'machine.c')) +cris_softmmu_ss.add(files( + 'helper.c', + 'machine.c', + 'mmu.c', +)) target_arch += {'cris': cris_ss} target_softmmu_arch += {'cris': cris_softmmu_ss} From 70863887a84caae2cfaf31b3ce900452d87553bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 16:32:17 -0700 Subject: [PATCH 0988/1334] target/hexagon: Remove hexagon_cpu_tlb_fill The fallback code in cpu_loop_exit_sigsegv is sufficient for hexagon linux-user. Remove the code from cpu_loop that raises SIGSEGV. Reviewed-by: Taylor Simpson Signed-off-by: Richard Henderson --- linux-user/hexagon/cpu_loop.c | 24 +----------------------- target/hexagon/cpu.c | 23 ----------------------- 2 files changed, 1 insertion(+), 46 deletions(-) diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index bee2a9e4ea..6b24cbaba9 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -28,8 +28,7 @@ void cpu_loop(CPUHexagonState *env) { CPUState *cs = env_cpu(env); - int trapnr, signum, sigcode; - target_ulong sigaddr; + int trapnr; target_ulong syscallnum; target_ulong ret; @@ -39,10 +38,6 @@ void cpu_loop(CPUHexagonState *env) cpu_exec_end(cs); process_queued_cpu_work(cs); - signum = 0; - sigcode = 0; - sigaddr = 0; - switch (trapnr) { case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ @@ -65,12 +60,6 @@ void cpu_loop(CPUHexagonState *env) env->gpr[0] = ret; } break; - case HEX_EXCP_FETCH_NO_UPAGE: - case HEX_EXCP_PRIV_NO_UREAD: - case HEX_EXCP_PRIV_NO_UWRITE: - signum = TARGET_SIGSEGV; - sigcode = TARGET_SEGV_MAPERR; - break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; @@ -79,17 +68,6 @@ void cpu_loop(CPUHexagonState *env) trapnr); exit(EXIT_FAILURE); } - - if (signum) { - target_siginfo_t info = { - .si_signo = signum, - .si_errno = 0, - .si_code = sigcode, - ._sifields._sigfault._addr = sigaddr - }; - queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); - } - process_pending_signals(env); } } diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 3338365c16..160a46a3d5 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -245,34 +245,11 @@ static void hexagon_cpu_init(Object *obj) qdev_property_add_static(DEVICE(obj), &hexagon_lldb_stack_adjust_property); } -static bool hexagon_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ -#ifdef CONFIG_USER_ONLY - switch (access_type) { - case MMU_INST_FETCH: - cs->exception_index = HEX_EXCP_FETCH_NO_UPAGE; - break; - case MMU_DATA_LOAD: - cs->exception_index = HEX_EXCP_PRIV_NO_UREAD; - break; - case MMU_DATA_STORE: - cs->exception_index = HEX_EXCP_PRIV_NO_UWRITE; - break; - } - cpu_loop_exit_restore(cs, retaddr); -#else -#error System mode not implemented for Hexagon -#endif -} - #include "hw/core/tcg-cpu-ops.h" static const struct TCGCPUOps hexagon_tcg_ops = { .initialize = hexagon_translate_init, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, - .tlb_fill = hexagon_tlb_fill, }; static void hexagon_cpu_class_init(ObjectClass *c, void *data) From 860e0b965be0ac3b8e455c3d80d4ca6f5e30a97a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 16:39:34 -0700 Subject: [PATCH 0989/1334] target/hppa: Make hppa_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for hppa linux-user. Remove the code from cpu_loop that raised SIGSEGV. This makes all of the code in mem_helper.c sysemu only, so remove the ifdefs and move the file to hppa_softmmu_ss. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/hppa/cpu_loop.c | 16 ---------------- target/hppa/cpu.c | 2 +- target/hppa/cpu.h | 2 +- target/hppa/mem_helper.c | 15 --------------- target/hppa/meson.build | 6 ++++-- 5 files changed, 6 insertions(+), 35 deletions(-) diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 81607a9b27..e0a62deeb9 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -144,22 +144,6 @@ void cpu_loop(CPUHPPAState *env) env->iaoq_f = env->gr[31]; env->iaoq_b = env->gr[31] + 4; break; - case EXCP_ITLB_MISS: - case EXCP_DTLB_MISS: - case EXCP_NA_ITLB_MISS: - case EXCP_NA_DTLB_MISS: - case EXCP_IMP: - case EXCP_DMP: - case EXCP_DMB: - case EXCP_PAGE_REF: - case EXCP_DMAR: - case EXCP_DMPI: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->cr[CR_IOR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case EXCP_UNALIGN: info.si_signo = TARGET_SIGBUS; info.si_errno = 0; diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 89cba9d7a2..23eb254228 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -145,9 +145,9 @@ static const struct SysemuCPUOps hppa_sysemu_ops = { static const struct TCGCPUOps hppa_tcg_ops = { .initialize = hppa_translate_init, .synchronize_from_tb = hppa_cpu_synchronize_from_tb, - .tlb_fill = hppa_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = hppa_cpu_tlb_fill, .cpu_exec_interrupt = hppa_cpu_exec_interrupt, .do_interrupt = hppa_cpu_do_interrupt, .do_unaligned_access = hppa_cpu_do_unaligned_access, diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index d3cb7a279f..294fd7297f 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -323,10 +323,10 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void hppa_cpu_dump_state(CPUState *cs, FILE *f, int); +#ifndef CONFIG_USER_ONLY bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -#ifndef CONFIG_USER_ONLY void hppa_cpu_do_interrupt(CPUState *cpu); bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index afc5b56c3e..bf07445cd1 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -24,20 +24,6 @@ #include "hw/core/cpu.h" #include "trace.h" -#ifdef CONFIG_USER_ONLY -bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - HPPACPU *cpu = HPPA_CPU(cs); - - /* ??? Test between data page fault and data memory protection trap, - which would affect si_code. */ - cs->exception_index = EXCP_DMP; - cpu->env.cr[CR_IOR] = address; - cpu_loop_exit_restore(cs, retaddr); -} -#else static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) { int i; @@ -392,4 +378,3 @@ int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr) hppa_tlb_entry *ent = hppa_find_tlb(env, vaddr); return ent ? ent->ar_type : -1; } -#endif /* CONFIG_USER_ONLY */ diff --git a/target/hppa/meson.build b/target/hppa/meson.build index 8a7ff82efc..021e42a2d0 100644 --- a/target/hppa/meson.build +++ b/target/hppa/meson.build @@ -7,13 +7,15 @@ hppa_ss.add(files( 'gdbstub.c', 'helper.c', 'int_helper.c', - 'mem_helper.c', 'op_helper.c', 'translate.c', )) hppa_softmmu_ss = ss.source_set() -hppa_softmmu_ss.add(files('machine.c')) +hppa_softmmu_ss.add(files( + 'machine.c', + 'mem_helper.c', +)) target_arch += {'hppa': hppa_ss} target_softmmu_arch += {'hppa': hppa_softmmu_ss} From f74bd157c62f5c064418d038f9003c9c4b3dad0e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Sep 2021 19:10:25 -0700 Subject: [PATCH 0990/1334] target/i386: Implement x86_cpu_record_sigsegv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record cr2, error_code, and exception_index. That last means that we must exit to cpu_loop ourselves, instead of letting exception_index being overwritten. Use the maperr parameter to properly set PG_ERROR_P_MASK. Reviewed by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/tcg/helper-tcg.h | 6 ++++++ target/i386/tcg/tcg-cpu.c | 3 ++- target/i386/tcg/user/excp_helper.c | 23 +++++++++++++++++------ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index 60ca09e95e..0a4401e917 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -43,9 +43,15 @@ bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req); #endif /* helper.c */ +#ifdef CONFIG_USER_ONLY +void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); +#else bool x86_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +#endif void breakpoint_handler(CPUState *cs); diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 3ecfae34cb..6fdfdf9598 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -72,10 +72,11 @@ static const struct TCGCPUOps x86_tcg_ops = { .synchronize_from_tb = x86_cpu_synchronize_from_tb, .cpu_exec_enter = x86_cpu_exec_enter, .cpu_exec_exit = x86_cpu_exec_exit, - .tlb_fill = x86_cpu_tlb_fill, #ifdef CONFIG_USER_ONLY .fake_user_interrupt = x86_cpu_do_interrupt, + .record_sigsegv = x86_cpu_record_sigsegv, #else + .tlb_fill = x86_cpu_tlb_fill, .do_interrupt = x86_cpu_do_interrupt, .cpu_exec_interrupt = x86_cpu_exec_interrupt, .debug_excp_handler = breakpoint_handler, diff --git a/target/i386/tcg/user/excp_helper.c b/target/i386/tcg/user/excp_helper.c index a89b5228fd..cd507e2a1b 100644 --- a/target/i386/tcg/user/excp_helper.c +++ b/target/i386/tcg/user/excp_helper.c @@ -22,18 +22,29 @@ #include "exec/exec-all.h" #include "tcg/helper-tcg.h" -bool x86_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) +void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; + /* + * The error_code that hw reports as part of the exception frame + * is copied to linux sigcontext.err. The exception_index is + * copied to linux sigcontext.trapno. Short of inventing a new + * place to store the trapno, we cannot let our caller raise the + * signal and set exception_index to EXCP_INTERRUPT. + */ env->cr[2] = addr; - env->error_code = (access_type == MMU_DATA_STORE) << PG_ERROR_W_BIT; - env->error_code |= PG_ERROR_U_MASK; + env->error_code = ((access_type == MMU_DATA_STORE) << PG_ERROR_W_BIT) + | (maperr ? 0 : PG_ERROR_P_MASK) + | PG_ERROR_U_MASK; cs->exception_index = EXCP0E_PAGE; + + /* Disable do_interrupt_user. */ env->exception_is_int = 0; env->exception_next_eip = -1; - cpu_loop_exit_restore(cs, retaddr); + + cpu_loop_exit_restore(cs, ra); } From 028772c45cfdd21870f0f9056d270f03bf990fae Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 16:56:32 -0700 Subject: [PATCH 0991/1334] target/m68k: Make m68k_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for m68k linux-user. Remove the code from cpu_loop that handled EXCP_ACCESS. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/m68k/cpu_loop.c | 10 ---------- target/m68k/cpu.c | 2 +- target/m68k/helper.c | 6 +----- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index ebf32be78f..790bd558c3 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -90,16 +90,6 @@ void cpu_loop(CPUM68KState *env) case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; - case EXCP_ACCESS: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->mmu.ar; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; case EXCP_DEBUG: info.si_signo = TARGET_SIGTRAP; info.si_errno = 0; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 66d22d1189..c7aeb7da9c 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -515,9 +515,9 @@ static const struct SysemuCPUOps m68k_sysemu_ops = { static const struct TCGCPUOps m68k_tcg_ops = { .initialize = m68k_tcg_init, - .tlb_fill = m68k_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = m68k_cpu_tlb_fill, .cpu_exec_interrupt = m68k_cpu_exec_interrupt, .do_interrupt = m68k_cpu_do_interrupt, .do_transaction_failed = m68k_cpu_transaction_failed, diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 137a3e1a3d..5728e48585 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -978,16 +978,12 @@ void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector) } } -#endif - bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType qemu_access_type, int mmu_idx, bool probe, uintptr_t retaddr) { M68kCPU *cpu = M68K_CPU(cs); CPUM68KState *env = &cpu->env; - -#ifndef CONFIG_USER_ONLY hwaddr physical; int prot; int access_type; @@ -1051,12 +1047,12 @@ bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (!(access_type & ACCESS_STORE)) { env->mmu.ssw |= M68K_RW_040; } -#endif cs->exception_index = EXCP_ACCESS; env->mmu.ar = address; cpu_loop_exit_restore(cs, retaddr); } +#endif /* !CONFIG_USER_ONLY */ uint32_t HELPER(bitrev)(uint32_t x) { From fd297732a2c27aae8407a0c96660345af10575df Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 17:17:38 -0700 Subject: [PATCH 0992/1334] target/microblaze: Make mb_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for microblaze linux-user. Remove the code from cpu_loop that handled the unnamed 0xaa exception. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- linux-user/microblaze/cpu_loop.c | 10 ---------- target/microblaze/cpu.c | 2 +- target/microblaze/cpu.h | 8 ++++---- target/microblaze/helper.c | 13 +------------ 4 files changed, 6 insertions(+), 27 deletions(-) diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 52222eb93f..a94467dd2d 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -37,16 +37,6 @@ void cpu_loop(CPUMBState *env) process_queued_cpu_work(cs); switch (trapnr) { - case 0xaa: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 15db277925..b9c888b87e 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -365,9 +365,9 @@ static const struct SysemuCPUOps mb_sysemu_ops = { static const struct TCGCPUOps mb_tcg_ops = { .initialize = mb_tcg_init, .synchronize_from_tb = mb_cpu_synchronize_from_tb, - .tlb_fill = mb_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = mb_cpu_tlb_fill, .cpu_exec_interrupt = mb_cpu_exec_interrupt, .do_interrupt = mb_cpu_do_interrupt, .do_transaction_failed = mb_cpu_transaction_failed, diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index b7a848bbae..e9cd0b88de 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -394,10 +394,6 @@ void mb_tcg_init(void); #define MMU_USER_IDX 2 /* See NB_MMU_MODES further up the file. */ -bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); - typedef CPUMBState CPUArchState; typedef MicroBlazeCPU ArchCPU; @@ -415,6 +411,10 @@ static inline void cpu_get_tb_cpu_state(CPUMBState *env, target_ulong *pc, } #if !defined(CONFIG_USER_ONLY) +bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); + void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index dd2aecd1d5..a607fe68e5 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -24,18 +24,7 @@ #include "qemu/host-utils.h" #include "exec/log.h" -#if defined(CONFIG_USER_ONLY) - -bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - cs->exception_index = 0xaa; - cpu_loop_exit_restore(cs, retaddr); -} - -#else /* !CONFIG_USER_ONLY */ - +#ifndef CONFIG_USER_ONLY static bool mb_cpu_access_is_secure(MicroBlazeCPU *cpu, MMUAccessType access_type) { From 52d4899bf3b876065269cfd353ea3b98f66df91a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 17:26:02 -0700 Subject: [PATCH 0993/1334] target/mips: Make mips_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for mips linux-user. This means we can remove tcg/user/tlb_helper.c entirely. Remove the code from cpu_loop that raised SIGSEGV. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/mips/cpu_loop.c | 11 ------ target/mips/cpu.c | 2 +- target/mips/tcg/meson.build | 3 -- target/mips/tcg/tcg-internal.h | 7 ++-- target/mips/tcg/user/meson.build | 3 -- target/mips/tcg/user/tlb_helper.c | 59 ------------------------------- 6 files changed, 5 insertions(+), 80 deletions(-) delete mode 100644 target/mips/tcg/user/meson.build delete mode 100644 target/mips/tcg/user/tlb_helper.c diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index cb03fb066b..b735c99a24 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -158,17 +158,6 @@ done_syscall: } env->active_tc.gpr[2] = ret; break; - case EXCP_TLBL: - case EXCP_TLBS: - case EXCP_AdEL: - case EXCP_AdES: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->CP0_BadVAddr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case EXCP_CpU: case EXCP_RI: info.si_signo = TARGET_SIGILL; diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 00e0c55d0e..4aae23934b 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -539,9 +539,9 @@ static const struct SysemuCPUOps mips_sysemu_ops = { static const struct TCGCPUOps mips_tcg_ops = { .initialize = mips_tcg_init, .synchronize_from_tb = mips_cpu_synchronize_from_tb, - .tlb_fill = mips_cpu_tlb_fill, #if !defined(CONFIG_USER_ONLY) + .tlb_fill = mips_cpu_tlb_fill, .cpu_exec_interrupt = mips_cpu_exec_interrupt, .do_interrupt = mips_cpu_do_interrupt, .do_transaction_failed = mips_cpu_do_transaction_failed, diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build index 8f6f7508b6..98003779ae 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -28,9 +28,6 @@ mips_ss.add(when: 'TARGET_MIPS64', if_true: files( 'mxu_translate.c', )) -if have_user - subdir('user') -endif if have_system subdir('sysemu') endif diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index bad3deb611..466768aec4 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -18,9 +18,6 @@ void mips_tcg_init(void); void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); -bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) QEMU_NORETURN; @@ -60,6 +57,10 @@ void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MemTxResult response, uintptr_t retaddr); void cpu_mips_tlb_flush(CPUMIPSState *env); +bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); + #endif /* !CONFIG_USER_ONLY */ #endif diff --git a/target/mips/tcg/user/meson.build b/target/mips/tcg/user/meson.build deleted file mode 100644 index 79badcd321..0000000000 --- a/target/mips/tcg/user/meson.build +++ /dev/null @@ -1,3 +0,0 @@ -mips_user_ss.add(files( - 'tlb_helper.c', -)) diff --git a/target/mips/tcg/user/tlb_helper.c b/target/mips/tcg/user/tlb_helper.c deleted file mode 100644 index 210c6d529e..0000000000 --- a/target/mips/tcg/user/tlb_helper.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * MIPS TLB (Translation lookaside buffer) helpers. - * - * Copyright (c) 2004-2005 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#include "qemu/osdep.h" - -#include "cpu.h" -#include "exec/exec-all.h" -#include "internal.h" - -static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, - MMUAccessType access_type) -{ - CPUState *cs = env_cpu(env); - - env->error_code = 0; - if (access_type == MMU_INST_FETCH) { - env->error_code |= EXCP_INST_NOTAVAIL; - } - - /* Reference to kernel address from user mode or supervisor mode */ - /* Reference to supervisor address from user mode */ - if (access_type == MMU_DATA_STORE) { - cs->exception_index = EXCP_AdES; - } else { - cs->exception_index = EXCP_AdEL; - } - - /* Raise exception */ - if (!(env->hflags & MIPS_HFLAG_DM)) { - env->CP0_BadVAddr = address; - } -} - -bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; - - /* data access */ - raise_mmu_exception(env, address, access_type); - do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr); -} From fac94cb36daacd202f718865ea82f5ff5a2b5a93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Sep 2021 13:41:43 -0400 Subject: [PATCH 0994/1334] target/nios2: Implement nios2_cpu_record_sigsegv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the linux-user kuser page handling is currently implemented by detecting magic addresses in the unnamed 0xaa trap, we cannot simply remove nios2_cpu_tlb_fill and rely on the fallback code. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/nios2/cpu.c | 6 ++++-- target/nios2/cpu.h | 6 ++++++ target/nios2/helper.c | 7 ++++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 58ecd27d75..4cade61e93 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -216,9 +216,11 @@ static const struct SysemuCPUOps nios2_sysemu_ops = { static const struct TCGCPUOps nios2_tcg_ops = { .initialize = nios2_tcg_init, - .tlb_fill = nios2_cpu_tlb_fill, -#ifndef CONFIG_USER_ONLY +#ifdef CONFIG_USER_ONLY + .record_sigsegv = nios2_cpu_record_sigsegv, +#else + .tlb_fill = nios2_cpu_tlb_fill, .cpu_exec_interrupt = nios2_cpu_exec_interrupt, .do_interrupt = nios2_cpu_do_interrupt, .do_unaligned_access = nios2_cpu_do_unaligned_access, diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index a80587338a..1a69ed7a49 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -218,9 +218,15 @@ static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch) MMU_SUPERVISOR_IDX; } +#ifdef CONFIG_USER_ONLY +void nios2_cpu_record_sigsegv(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); +#else bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +#endif static inline int cpu_interrupts_enabled(CPUNios2State *env) { diff --git a/target/nios2/helper.c b/target/nios2/helper.c index 53be8398e9..e5c98650e1 100644 --- a/target/nios2/helper.c +++ b/target/nios2/helper.c @@ -38,10 +38,11 @@ void nios2_cpu_do_interrupt(CPUState *cs) env->regs[R_EA] = env->regs[R_PC] + 4; } -bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) +void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t retaddr) { + /* FIXME: Disentangle kuser page from linux-user sigsegv handling. */ cs->exception_index = 0xaa; cpu_loop_exit_restore(cs, retaddr); } From d315712b69fa34c27d09d425ca5951a08b07ade8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 20:27:58 -0700 Subject: [PATCH 0995/1334] linux-user/openrisc: Abort for EXCP_RANGE, EXCP_FPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU does not allow the system control bits for either exception to be enabled in linux-user, therefore both exceptions are dead code. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/openrisc/cpu_loop.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index f6360db47c..10b7147f68 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -56,7 +56,6 @@ void cpu_loop(CPUOpenRISCState *env) break; case EXCP_DPF: case EXCP_IPF: - case EXCP_RANGE: info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; @@ -77,13 +76,6 @@ void cpu_loop(CPUOpenRISCState *env) info._sifields._sigfault._addr = env->pc; queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; - case EXCP_FPE: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case EXCP_INTERRUPT: /* We processed the pending cpu work above. */ break; @@ -96,6 +88,15 @@ void cpu_loop(CPUOpenRISCState *env) case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; + case EXCP_RANGE: + /* Requires SR.OVE set, which linux-user won't do. */ + cpu_abort(cs, "Unexpected RANGE exception"); + case EXCP_FPE: + /* + * Requires FPSCR.FPEE set. Writes to FPSCR from usermode not + * yet enabled in kernel ABI, so linux-user does not either. + */ + cpu_abort(cs, "Unexpected FPE exception"); default: g_assert_not_reached(); } From 12f0bc55791bd6e0864a430a0c3c9518ae7622e8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 20:33:23 -0700 Subject: [PATCH 0996/1334] target/openrisc: Make openrisc_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for openrisc linux-user. This makes all of the code in mmu.c sysemu only, so remove the ifdefs and move the file to openrisc_softmmu_ss. Remove the code from cpu_loop that handled EXCP_DPF. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/openrisc/cpu_loop.c | 8 -------- target/openrisc/cpu.c | 2 +- target/openrisc/cpu.h | 7 ++++--- target/openrisc/meson.build | 2 +- target/openrisc/mmu.c | 9 --------- 5 files changed, 6 insertions(+), 22 deletions(-) diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 10b7147f68..3cfdbbf037 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -54,14 +54,6 @@ void cpu_loop(CPUOpenRISCState *env) cpu_set_gpr(env, 11, ret); } break; - case EXCP_DPF: - case EXCP_IPF: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case EXCP_ALIGN: info.si_signo = TARGET_SIGBUS; info.si_errno = 0; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 27cb04152f..dfbafc5236 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -186,9 +186,9 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = { static const struct TCGCPUOps openrisc_tcg_ops = { .initialize = openrisc_translate_init, - .tlb_fill = openrisc_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = openrisc_cpu_tlb_fill, .cpu_exec_interrupt = openrisc_cpu_exec_interrupt, .do_interrupt = openrisc_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 187a4a114e..ee069b080c 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -317,14 +317,15 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int openrisc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void openrisc_translate_init(void); -bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); int print_insn_or1k(bfd_vma addr, disassemble_info *info); #define cpu_list cpu_openrisc_list #ifndef CONFIG_USER_ONLY +bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); + extern const VMStateDescription vmstate_openrisc_cpu; void openrisc_cpu_do_interrupt(CPUState *cpu); diff --git a/target/openrisc/meson.build b/target/openrisc/meson.build index e445dec4a0..84322086ec 100644 --- a/target/openrisc/meson.build +++ b/target/openrisc/meson.build @@ -10,7 +10,6 @@ openrisc_ss.add(files( 'fpu_helper.c', 'gdbstub.c', 'interrupt_helper.c', - 'mmu.c', 'sys_helper.c', 'translate.c', )) @@ -19,6 +18,7 @@ openrisc_softmmu_ss = ss.source_set() openrisc_softmmu_ss.add(files( 'interrupt.c', 'machine.c', + 'mmu.c', )) target_arch += {'openrisc': openrisc_ss} diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 94df8c7bef..e561ef245b 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -23,11 +23,8 @@ #include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu/host-utils.h" -#ifndef CONFIG_USER_ONLY #include "hw/loader.h" -#endif -#ifndef CONFIG_USER_ONLY static inline void get_phys_nommu(hwaddr *phys_addr, int *prot, target_ulong address) { @@ -94,7 +91,6 @@ static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot, return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS; } } -#endif static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address, int exception) @@ -112,8 +108,6 @@ bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, { OpenRISCCPU *cpu = OPENRISC_CPU(cs); int excp = EXCP_DPF; - -#ifndef CONFIG_USER_ONLY int prot; hwaddr phys_addr; @@ -138,13 +132,11 @@ bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, if (probe) { return false; } -#endif raise_mmu_exception(cpu, addr, excp); cpu_loop_exit_restore(cs, retaddr); } -#ifndef CONFIG_USER_ONLY hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); @@ -177,4 +169,3 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return phys_addr; } } -#endif From 1db8af5c87ef5c89ecdb9e2d2620cd38cfbca940 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Sep 2021 06:37:19 -0700 Subject: [PATCH 0997/1334] target/ppc: Implement ppc_cpu_record_sigsegv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record DAR, DSISR, and exception_index. That last means that we must exit to cpu_loop ourselves, instead of letting exception_index being overwritten. This is exactly what the user-mode ppc_cpu_tlb_fill does, so simply rename it as ppc_cpu_record_sigsegv. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/ppc/cpu.h | 3 --- target/ppc/cpu_init.c | 6 ++++-- target/ppc/internal.h | 9 +++++++++ target/ppc/user_only_helper.c | 15 +++++++++++---- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 0472ec9154..e946da5f3a 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1302,9 +1302,6 @@ extern const VMStateDescription vmstate_ppc_cpu; /*****************************************************************************/ void ppc_translate_init(void); -bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1(CPUPPCState *env, target_ulong value); diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 65545ba9ca..1c7a7b4b38 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -9014,9 +9014,11 @@ static const struct SysemuCPUOps ppc_sysemu_ops = { static const struct TCGCPUOps ppc_tcg_ops = { .initialize = ppc_translate_init, - .tlb_fill = ppc_cpu_tlb_fill, -#ifndef CONFIG_USER_ONLY +#ifdef CONFIG_USER_ONLY + .record_sigsegv = ppc_cpu_record_sigsegv, +#else + .tlb_fill = ppc_cpu_tlb_fill, .cpu_exec_interrupt = ppc_cpu_exec_interrupt, .do_interrupt = ppc_cpu_do_interrupt, .cpu_exec_enter = ppc_cpu_exec_enter, diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 55284369f5..339974b7d8 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -283,5 +283,14 @@ static inline void pte_invalidate(target_ulong *pte0) #define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) +#ifdef CONFIG_USER_ONLY +void ppc_cpu_record_sigsegv(CPUState *cs, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); +#else +bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); +#endif #endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/user_only_helper.c b/target/ppc/user_only_helper.c index aa3f867596..7ff76f7a06 100644 --- a/target/ppc/user_only_helper.c +++ b/target/ppc/user_only_helper.c @@ -21,16 +21,23 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "internal.h" - -bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) +void ppc_cpu_record_sigsegv(CPUState *cs, vaddr address, + MMUAccessType access_type, + bool maperr, uintptr_t retaddr) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; int exception, error_code; + /* + * Both DSISR and the "trap number" (exception vector offset, + * looked up from exception_index) are present in the linux-user + * signal frame. + * FIXME: we don't actually populate the trap number properly. + * It would be easiest to fill in an env->trap value now. + */ if (access_type == MMU_INST_FETCH) { exception = POWERPC_EXCP_ISI; error_code = 0x40000000; From 263e2ab20ca40580483233a62e7a7996b28b02fc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Sep 2021 20:46:38 -0700 Subject: [PATCH 0998/1334] target/riscv: Make riscv_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for riscv linux-user. Remove the code from cpu_loop that raised SIGSEGV. Reviewed-by: Warner Losh Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/riscv/cpu_loop.c | 7 ------- target/riscv/cpu.c | 2 +- target/riscv/cpu_helper.c | 21 +-------------------- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index e5bb6d908a..b301dac802 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -87,13 +87,6 @@ void cpu_loop(CPURISCVState *env) sigcode = TARGET_TRAP_BRKPT; sigaddr = env->pc; break; - case RISCV_EXCP_INST_PAGE_FAULT: - case RISCV_EXCP_LOAD_PAGE_FAULT: - case RISCV_EXCP_STORE_PAGE_FAULT: - signum = TARGET_SIGSEGV; - sigcode = TARGET_SEGV_MAPERR; - sigaddr = env->badaddr; - break; case RISCV_EXCP_SEMIHOST: env->gpr[xA0] = do_common_semihosting(cs); env->pc += 4; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 7d53125dbc..f812998123 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -694,9 +694,9 @@ static const struct SysemuCPUOps riscv_sysemu_ops = { static const struct TCGCPUOps riscv_tcg_ops = { .initialize = riscv_translate_init, .synchronize_from_tb = riscv_cpu_synchronize_from_tb, - .tlb_fill = riscv_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = riscv_cpu_tlb_fill, .cpu_exec_interrupt = riscv_cpu_exec_interrupt, .do_interrupt = riscv_cpu_do_interrupt, .do_transaction_failed = riscv_cpu_do_transaction_failed, diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f30ff672f8..9eeed38c7e 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -814,7 +814,6 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, riscv_cpu_two_stage_lookup(mmu_idx); riscv_raise_exception(env, cs->exception_index, retaddr); } -#endif /* !CONFIG_USER_ONLY */ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, @@ -822,7 +821,6 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; -#ifndef CONFIG_USER_ONLY vaddr im_address; hwaddr pa = 0; int prot, prot2, prot_pmp; @@ -954,25 +952,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } return true; - -#else - switch (access_type) { - case MMU_INST_FETCH: - cs->exception_index = RISCV_EXCP_INST_PAGE_FAULT; - break; - case MMU_DATA_LOAD: - cs->exception_index = RISCV_EXCP_LOAD_PAGE_FAULT; - break; - case MMU_DATA_STORE: - cs->exception_index = RISCV_EXCP_STORE_PAGE_FAULT; - break; - default: - g_assert_not_reached(); - } - env->badaddr = address; - cpu_loop_exit_restore(cs, retaddr); -#endif } +#endif /* !CONFIG_USER_ONLY */ /* * Handle Traps From db9aab5783a2fb62250e12f0c4cfed5e1778c189 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Sep 2021 10:24:18 -0700 Subject: [PATCH 0999/1334] target/s390x: Use probe_access_flags in s390_probe_access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not sure why the user-only code wasn't rewritten to use probe_access_flags at the same time that the sysemu code was converted. For the purpose of user-only, this is an exact replacement. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/tcg/mem_helper.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 17e3f83641..362a30d99e 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -141,20 +141,12 @@ static int s390_probe_access(CPUArchState *env, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, bool nonfault, void **phost, uintptr_t ra) { +#if defined(CONFIG_USER_ONLY) + return probe_access_flags(env, addr, access_type, mmu_idx, + nonfault, phost, ra); +#else int flags; -#if defined(CONFIG_USER_ONLY) - flags = page_get_flags(addr); - if (!(flags & (access_type == MMU_DATA_LOAD ? PAGE_READ : PAGE_WRITE_ORG))) { - env->__excp_addr = addr; - flags = (flags & PAGE_VALID) ? PGM_PROTECTION : PGM_ADDRESSING; - if (nonfault) { - return flags; - } - tcg_s390_program_interrupt(env, flags, ra); - } - *phost = g2h(env_cpu(env), addr); -#else /* * For !CONFIG_USER_ONLY, we cannot rely on TLB_INVALID_MASK or haddr==NULL * to detect if there was an exception during tlb_fill(). @@ -173,8 +165,8 @@ static int s390_probe_access(CPUArchState *env, target_ulong addr, int size, (access_type == MMU_DATA_STORE ? BP_MEM_WRITE : BP_MEM_READ), ra); } -#endif return 0; +#endif } static int access_prepare_nf(S390Access *access, CPUS390XState *env, From c8e7fef102058c3554b26a381e0a89ae05b9677b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Sep 2021 10:34:30 -0700 Subject: [PATCH 1000/1334] target/s390x: Implement s390_cpu_record_sigsegv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the masking of the address from cpu_loop into s390_cpu_record_sigsegv -- this is governed by hw, not linux. This does mean we have to raise our own exception, rather than return to the fallback. Use maperr to choose between PGM_PROTECTION and PGM_ADDRESSING. Use the appropriate si_code for each in cpu_loop. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/s390x/cpu_loop.c | 13 ++++++------- target/s390x/cpu.c | 6 ++++-- target/s390x/s390x-internal.h | 13 ++++++++++--- target/s390x/tcg/excp_helper.c | 18 +++++++++++------- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 69b69981f6..d089c8417e 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -24,8 +24,6 @@ #include "cpu_loop-common.h" #include "signal-common.h" -/* s390x masks the fault address it reports in si_addr for SIGSEGV and SIGBUS */ -#define S390X_FAIL_ADDR_MASK -4096LL static int get_pgm_data_si_code(int dxc_code) { @@ -111,12 +109,13 @@ void cpu_loop(CPUS390XState *env) n = TARGET_ILL_ILLOPC; goto do_signal_pc; case PGM_PROTECTION: + force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_ACCERR, + env->__excp_addr); + break; case PGM_ADDRESSING: - sig = TARGET_SIGSEGV; - /* XXX: check env->error_code */ - n = TARGET_SEGV_MAPERR; - addr = env->__excp_addr & S390X_FAIL_ADDR_MASK; - goto do_signal; + force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, + env->__excp_addr); + break; case PGM_EXECUTE: case PGM_SPECIFICATION: case PGM_SPECIAL_OP: diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 7b7b05f1d3..593dda75c4 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -266,9 +266,11 @@ static void s390_cpu_reset_full(DeviceState *dev) static const struct TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, - .tlb_fill = s390_cpu_tlb_fill, -#if !defined(CONFIG_USER_ONLY) +#ifdef CONFIG_USER_ONLY + .record_sigsegv = s390_cpu_record_sigsegv, +#else + .tlb_fill = s390_cpu_tlb_fill, .cpu_exec_interrupt = s390_cpu_exec_interrupt, .do_interrupt = s390_cpu_do_interrupt, .debug_excp_handler = s390x_cpu_debug_excp_handler, diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 27d4a03ca1..163aa4f94a 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -270,13 +270,20 @@ ObjectClass *s390_cpu_class_by_name(const char *name); void s390x_cpu_debug_excp_handler(CPUState *cs); void s390_cpu_do_interrupt(CPUState *cpu); bool s390_cpu_exec_interrupt(CPUState *cpu, int int_req); -bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) QEMU_NORETURN; +#ifdef CONFIG_USER_ONLY +void s390_cpu_record_sigsegv(CPUState *cs, vaddr address, + MMUAccessType access_type, + bool maperr, uintptr_t retaddr); +#else +bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); +#endif + /* fpu_helper.c */ uint32_t set_cc_nz_f32(float32 v); diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index 3d6662a53c..b923d080fc 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -89,16 +89,20 @@ void s390_cpu_do_interrupt(CPUState *cs) cs->exception_index = -1; } -bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) +void s390_cpu_record_sigsegv(CPUState *cs, vaddr address, + MMUAccessType access_type, + bool maperr, uintptr_t retaddr) { S390CPU *cpu = S390_CPU(cs); - trigger_pgm_exception(&cpu->env, PGM_ADDRESSING); - /* On real machines this value is dropped into LowMem. Since this - is userland, simply put this someplace that cpu_loop can find it. */ - cpu->env.__excp_addr = address; + trigger_pgm_exception(&cpu->env, maperr ? PGM_ADDRESSING : PGM_PROTECTION); + /* + * On real machines this value is dropped into LowMem. Since this + * is userland, simply put this someplace that cpu_loop can find it. + * S390 only gives the page of the fault, not the exact address. + * C.f. the construction of TEC in mmu_translate(). + */ + cpu->env.__excp_addr = address & TARGET_PAGE_MASK; cpu_loop_exit_restore(cs, retaddr); } From cac720ec5466312a6f7f3f81fa3f11f05c022375 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Sep 2021 07:59:07 -0700 Subject: [PATCH 1001/1334] target/sh4: Make sh4_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for sh4 linux-user. Remove the code from cpu_loop that raised SIGSEGV. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/sh4/cpu_loop.c | 8 -------- target/sh4/cpu.c | 2 +- target/sh4/cpu.h | 6 +++--- target/sh4/helper.c | 9 +-------- 4 files changed, 5 insertions(+), 20 deletions(-) diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index 65b8972e3c..ac9b01840c 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -65,14 +65,6 @@ void cpu_loop(CPUSH4State *env) info.si_code = TARGET_TRAP_BRKPT; queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; - case 0xa0: - case 0xc0: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->tea; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); arch_interrupt = false; diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 2047742d03..06b2691dc4 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -236,9 +236,9 @@ static const struct SysemuCPUOps sh4_sysemu_ops = { static const struct TCGCPUOps superh_tcg_ops = { .initialize = sh4_translate_init, .synchronize_from_tb = superh_cpu_synchronize_from_tb, - .tlb_fill = superh_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = superh_cpu_tlb_fill, .cpu_exec_interrupt = superh_cpu_exec_interrupt, .do_interrupt = superh_cpu_do_interrupt, .do_unaligned_access = superh_cpu_do_unaligned_access, diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index dc81406646..4cfb109f56 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -213,12 +213,12 @@ void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, uintptr_t retaddr) QEMU_NORETURN; void sh4_translate_init(void); +void sh4_cpu_list(void); + +#if !defined(CONFIG_USER_ONLY) bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); - -void sh4_cpu_list(void); -#if !defined(CONFIG_USER_ONLY) void superh_cpu_do_interrupt(CPUState *cpu); bool superh_cpu_exec_interrupt(CPUState *cpu, int int_req); void cpu_sh4_invalidate_tlb(CPUSH4State *s); diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 53cb9c3b63..6a620e36fc 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -796,8 +796,6 @@ bool superh_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return false; } -#endif /* !CONFIG_USER_ONLY */ - bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) @@ -806,11 +804,6 @@ bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPUSH4State *env = &cpu->env; int ret; -#ifdef CONFIG_USER_ONLY - ret = (access_type == MMU_DATA_STORE ? MMU_DTLB_VIOLATION_WRITE : - access_type == MMU_INST_FETCH ? MMU_ITLB_VIOLATION : - MMU_DTLB_VIOLATION_READ); -#else target_ulong physical; int prot; @@ -829,7 +822,6 @@ bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret != MMU_DTLB_MULTIPLE && ret != MMU_ITLB_MULTIPLE) { env->pteh = (env->pteh & PTEH_ASID_MASK) | (address & PTEH_VPN_MASK); } -#endif env->tea = address; switch (ret) { @@ -868,3 +860,4 @@ bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } cpu_loop_exit_restore(cs, retaddr); } +#endif /* !CONFIG_USER_ONLY */ From caac44a52aa71d5ff83607cad861d02ecbbfcdc0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Sep 2021 08:05:53 -0700 Subject: [PATCH 1002/1334] target/sparc: Make sparc_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for sparc linux-user. This makes all of the code in mmu_helper.c sysemu only, so remove the ifdefs and move the file to sparc_softmmu_ss. Remove the code from cpu_loop that handled TT_DFAULT and TT_TFAULT. Cc: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/sparc/cpu_loop.c | 25 ------------------------- target/sparc/cpu.c | 2 +- target/sparc/meson.build | 2 +- target/sparc/mmu_helper.c | 25 ------------------------- 4 files changed, 2 insertions(+), 52 deletions(-) diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index ad29b4eb6a..0ba65e431c 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -219,17 +219,6 @@ void cpu_loop (CPUSPARCState *env) case TT_WIN_UNF: /* window underflow */ restore_window(env); break; - case TT_TFAULT: - case TT_DFAULT: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->mmuregs[4]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; #else case TT_SPILL: /* window overflow */ save_window(env); @@ -237,20 +226,6 @@ void cpu_loop (CPUSPARCState *env) case TT_FILL: /* window underflow */ restore_window(env); break; - case TT_TFAULT: - case TT_DFAULT: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - if (trapnr == TT_DFAULT) - info._sifields._sigfault._addr = env->dmmu.mmuregs[4]; - else - info._sifields._sigfault._addr = cpu_tsptr(env)->tpc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; #ifndef TARGET_ABI32 case 0x16e: flush_windows(env); diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 21dd27796d..55268ed2a1 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -865,9 +865,9 @@ static const struct SysemuCPUOps sparc_sysemu_ops = { static const struct TCGCPUOps sparc_tcg_ops = { .initialize = sparc_tcg_init, .synchronize_from_tb = sparc_cpu_synchronize_from_tb, - .tlb_fill = sparc_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY + .tlb_fill = sparc_cpu_tlb_fill, .cpu_exec_interrupt = sparc_cpu_exec_interrupt, .do_interrupt = sparc_cpu_do_interrupt, .do_transaction_failed = sparc_cpu_do_transaction_failed, diff --git a/target/sparc/meson.build b/target/sparc/meson.build index a3638b9503..a801802ee2 100644 --- a/target/sparc/meson.build +++ b/target/sparc/meson.build @@ -6,7 +6,6 @@ sparc_ss.add(files( 'gdbstub.c', 'helper.c', 'ldst_helper.c', - 'mmu_helper.c', 'translate.c', 'win_helper.c', )) @@ -16,6 +15,7 @@ sparc_ss.add(when: 'TARGET_SPARC64', if_true: files('int64_helper.c', 'vis_helpe sparc_softmmu_ss = ss.source_set() sparc_softmmu_ss.add(files( 'machine.c', + 'mmu_helper.c', 'monitor.c', )) diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index a44473a1c7..2ad47391d0 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -25,30 +25,6 @@ /* Sparc MMU emulation */ -#if defined(CONFIG_USER_ONLY) - -bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; - - if (access_type == MMU_INST_FETCH) { - cs->exception_index = TT_TFAULT; - } else { - cs->exception_index = TT_DFAULT; -#ifdef TARGET_SPARC64 - env->dmmu.mmuregs[4] = address; -#else - env->mmuregs[4] = address; -#endif - } - cpu_loop_exit_restore(cs, retaddr); -} - -#else - #ifndef TARGET_SPARC64 /* * Sparc V8 Reference MMU (SRMMU) @@ -926,4 +902,3 @@ hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } return phys_addr; } -#endif From 6407f64fcf0990f9353ec8b3c2a86aed92ef4aa1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Sep 2021 08:09:38 -0700 Subject: [PATCH 1003/1334] target/xtensa: Make xtensa_cpu_tlb_fill sysemu only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback code in cpu_loop_exit_sigsegv is sufficient for xtensa linux-user. Remove the code from cpu_loop that raised SIGSEGV. Acked-by: Max Filippov Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/xtensa/cpu_loop.c | 9 --------- target/xtensa/cpu.c | 2 +- target/xtensa/cpu.h | 2 +- target/xtensa/helper.c | 22 +--------------------- 4 files changed, 3 insertions(+), 32 deletions(-) diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index 622afbcd34..a83490ab35 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -226,15 +226,6 @@ void cpu_loop(CPUXtensaState *env) queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; - case LOAD_PROHIBITED_CAUSE: - case STORE_PROHIBITED_CAUSE: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->sregs[EXCVADDR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - default: fprintf(stderr, "exccause = %d\n", env->sregs[EXCCAUSE]); g_assert_not_reached(); diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index c1cbd03595..224f723236 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -192,10 +192,10 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = { static const struct TCGCPUOps xtensa_tcg_ops = { .initialize = xtensa_translate_init, - .tlb_fill = xtensa_cpu_tlb_fill, .debug_excp_handler = xtensa_breakpoint_handler, #ifndef CONFIG_USER_ONLY + .tlb_fill = xtensa_cpu_tlb_fill, .cpu_exec_interrupt = xtensa_cpu_exec_interrupt, .do_interrupt = xtensa_cpu_do_interrupt, .do_transaction_failed = xtensa_cpu_do_transaction_failed, diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index f9a510ca46..02143f2f77 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -563,10 +563,10 @@ struct XtensaCPU { }; +#ifndef CONFIG_USER_ONLY bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -#ifndef CONFIG_USER_ONLY void xtensa_cpu_do_interrupt(CPUState *cpu); bool xtensa_cpu_exec_interrupt(CPUState *cpu, int interrupt_request); void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index f18ab383fd..29d216ec1b 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -242,27 +242,7 @@ void xtensa_cpu_list(void) } } -#ifdef CONFIG_USER_ONLY - -bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; - - qemu_log_mask(CPU_LOG_INT, - "%s: rw = %d, address = 0x%08" VADDR_PRIx ", size = %d\n", - __func__, access_type, address, size); - env->sregs[EXCVADDR] = address; - env->sregs[EXCCAUSE] = (access_type == MMU_DATA_STORE ? - STORE_PROHIBITED_CAUSE : LOAD_PROHIBITED_CAUSE); - cs->exception_index = EXC_USER; - cpu_loop_exit_restore(cs, retaddr); -} - -#else /* !CONFIG_USER_ONLY */ - +#ifndef CONFIG_USER_ONLY void xtensa_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) From eeca7dc566076d6130d986e17508372bc7916281 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Sep 2021 08:13:38 -0700 Subject: [PATCH 1004/1334] accel/tcg: Restrict TCGCPUOps::tlb_fill() to sysemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have replaced tlb_fill with record_sigsegv for user mode. Move the declaration to restrict it to system emulation. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/hw/core/tcg-cpu-ops.h | 22 ++++++++++------------ linux-user/signal.c | 3 --- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 41718b695b..8eadd404c8 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -35,18 +35,6 @@ struct TCGCPUOps { void (*cpu_exec_enter)(CPUState *cpu); /** @cpu_exec_exit: Callback for cpu_exec cleanup */ void (*cpu_exec_exit)(CPUState *cpu); - /** - * @tlb_fill: Handle a softmmu tlb miss or user-only address fault - * - * For system mode, if the access is valid, call tlb_set_page - * and return true; if the access is invalid, and probe is - * true, return false; otherwise raise an exception and do - * not return. For user-only mode, always raise an exception - * and do not return. - */ - bool (*tlb_fill)(CPUState *cpu, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); /** @debug_excp_handler: Callback for handling debug exceptions */ void (*debug_excp_handler)(CPUState *cpu); @@ -68,6 +56,16 @@ struct TCGCPUOps { #ifdef CONFIG_SOFTMMU /** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */ bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); + /** + * @tlb_fill: Handle a softmmu tlb miss + * + * If the access is valid, call tlb_set_page and return true; + * if the access is invalid and probe is true, return false; + * otherwise raise an exception and do not return. + */ + bool (*tlb_fill)(CPUState *cpu, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); /** * @do_transaction_failed: Callback for handling failed memory transactions * (ie bus faults or external aborts; not MMU faults) diff --git a/linux-user/signal.c b/linux-user/signal.c index 135983747d..9d60abc038 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -697,9 +697,6 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, if (tcg_ops->record_sigsegv) { tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); - } else if (tcg_ops->tlb_fill) { - tcg_ops->tlb_fill(cpu, addr, 0, access_type, MMU_USER_IDX, false, ra); - g_assert_not_reached(); } force_sig_fault(TARGET_SIGSEGV, From 644a9fece426d52cf8fb51e24e003dd4c590c5cc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 4 Oct 2021 10:05:20 -0700 Subject: [PATCH 1005/1334] hw/core: Add TCGCPUOps.record_sigbus Add a new user-only interface for updating cpu state before raising a signal. This will take the place of do_unaligned_access for user-only and should result in less boilerplate for each guest. Reviewed-by: Warner Losh Signed-off-by: Richard Henderson --- include/hw/core/tcg-cpu-ops.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 8eadd404c8..e13898553a 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -135,6 +135,29 @@ struct TCGCPUOps { void (*record_sigsegv)(CPUState *cpu, vaddr addr, MMUAccessType access_type, bool maperr, uintptr_t ra); + /** + * record_sigbus: + * @cpu: cpu context + * @addr: misaligned guest address + * @access_type: access was read/write/execute + * @ra: host pc for unwinding + * + * We are about to raise SIGBUS with si_code BUS_ADRALN, + * and si_addr set for @addr. Record anything further needed + * for the signal ucontext_t. + * + * If the emulated kernel does not provide the signal handler with + * anything besides the user context registers, and the siginfo_t, + * then this hook need do nothing and may be omitted. + * Otherwise, record the data and return; the caller will raise + * the signal, unwind the cpu state, and return to the main loop. + * + * If it is simpler to re-use the sysemu do_unaligned_access code, + * @ra is provided so that a "normal" cpu exception can be raised. + * In this case, the signal must be raised by the architecture cpu_loop. + */ + void (*record_sigbus)(CPUState *cpu, vaddr addr, + MMUAccessType access_type, uintptr_t ra); #endif /* CONFIG_SOFTMMU */ #endif /* NEED_CPU_H */ From 12ed56407e60371d45ffa3b7f2fd00c4d7efa580 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 4 Oct 2021 10:06:10 -0700 Subject: [PATCH 1006/1334] linux-user: Add cpu_loop_exit_sigbus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a new interface to be provided by the os emulator for raising SIGBUS on fault. Use the new record_sigbus target hook. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/exec-all.h | 14 ++++++++++++++ linux-user/signal.c | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index f74578500c..6bb2a0f7ec 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -700,6 +700,20 @@ void QEMU_NORETURN cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, MMUAccessType access_type, bool maperr, uintptr_t ra); +/** + * cpu_loop_exit_sigbus: + * @cpu: the cpu context + * @addr: the guest address of the alignment fault + * @access_type: access was read/write/execute + * @ra: host pc for unwinding + * + * Use the TCGCPUOps hook to record cpu state, do guest operating system + * specific things to raise SIGBUS, and jump to the main cpu loop. + */ +void QEMU_NORETURN cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, + uintptr_t ra); + #else static inline void mmap_lock(void) {} static inline void mmap_unlock(void) {} diff --git a/linux-user/signal.c b/linux-user/signal.c index 9d60abc038..df2c8678d0 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -706,6 +706,20 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, cpu_loop_exit_restore(cpu, ra); } +void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, uintptr_t ra) +{ + const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + + if (tcg_ops->record_sigbus) { + tcg_ops->record_sigbus(cpu, addr, access_type, ra); + } + + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, addr); + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, ra); +} + /* abort execution with signal */ static void QEMU_NORETURN dump_core_and_abort(int target_sig) { From e7424abc201ea06ff91a15fd86a533a22cd8dff4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 23 Jul 2021 12:20:55 -1000 Subject: [PATCH 1007/1334] target/alpha: Implement alpha_cpu_record_sigbus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record trap_arg{0,1,2} for the linux-user signal frame. Raise SIGBUS directly from cpu_loop_exit_sigbus, which means we can remove the code for EXCP_UNALIGN in cpu_loop. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/alpha/cpu_loop.c | 7 ------- target/alpha/cpu.c | 1 + target/alpha/cpu.h | 8 +++++--- target/alpha/mem_helper.c | 30 ++++++++++++++++++++++-------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 4cc8e0a55c..4029849d5c 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -54,13 +54,6 @@ void cpu_loop(CPUAlphaState *env) fprintf(stderr, "External interrupt. Exit\n"); exit(EXIT_FAILURE); break; - case EXCP_UNALIGN: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->trap_arg0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case EXCP_OPCDEC: do_sigill: info.si_signo = TARGET_SIGILL; diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 69f32c3078..a8990d401b 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -221,6 +221,7 @@ static const struct TCGCPUOps alpha_tcg_ops = { #ifdef CONFIG_USER_ONLY .record_sigsegv = alpha_cpu_record_sigsegv, + .record_sigbus = alpha_cpu_record_sigbus, #else .tlb_fill = alpha_cpu_tlb_fill, .cpu_exec_interrupt = alpha_cpu_exec_interrupt, diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index d49cc36d07..afd975c878 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -282,9 +282,6 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int alpha_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; #define cpu_list alpha_cpu_list @@ -451,10 +448,15 @@ void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val); void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address, MMUAccessType access_type, bool maperr, uintptr_t retaddr); +void alpha_cpu_record_sigbus(CPUState *cs, vaddr address, + MMUAccessType access_type, uintptr_t retaddr); #else bool alpha_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr) QEMU_NORETURN; void alpha_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, diff --git a/target/alpha/mem_helper.c b/target/alpha/mem_helper.c index 75e72bc337..47283a0612 100644 --- a/target/alpha/mem_helper.c +++ b/target/alpha/mem_helper.c @@ -23,18 +23,12 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" -/* Softmmu support */ -#ifndef CONFIG_USER_ONLY -void alpha_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) +static void do_unaligned_access(CPUAlphaState *env, vaddr addr, uintptr_t retaddr) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; uint64_t pc; uint32_t insn; - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(env_cpu(env), retaddr, true); pc = env->pc; insn = cpu_ldl_code(env, pc); @@ -42,6 +36,26 @@ void alpha_cpu_do_unaligned_access(CPUState *cs, vaddr addr, env->trap_arg0 = addr; env->trap_arg1 = insn >> 26; /* opcode */ env->trap_arg2 = (insn >> 21) & 31; /* dest regno */ +} + +#ifdef CONFIG_USER_ONLY +void alpha_cpu_record_sigbus(CPUState *cs, vaddr addr, + MMUAccessType access_type, uintptr_t retaddr) +{ + AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = &cpu->env; + + do_unaligned_access(env, addr, retaddr); +} +#else +void alpha_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = &cpu->env; + + do_unaligned_access(env, addr, retaddr); cs->exception_index = EXCP_UNALIGN; env->error_code = 0; cpu_loop_exit(cs); From 39a099ca25670b9fed838a09887fe8a1fdd803d3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 23 Jul 2021 12:22:54 -1000 Subject: [PATCH 1008/1334] target/arm: Implement arm_cpu_record_sigbus Because of the complexity of setting ESR, re-use the existing arm_cpu_do_unaligned_access function. This means we have to handle the exception ourselves in cpu_loop, transforming it to the appropriate signal. Reviewed-by: Warner Losh Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 12 +++++++++--- linux-user/arm/cpu_loop.c | 30 ++++++++++++++++++++++++++---- target/arm/cpu.c | 1 + target/arm/cpu_tcg.c | 1 + target/arm/internals.h | 2 ++ target/arm/tlb_helper.c | 6 ++++++ 6 files changed, 45 insertions(+), 7 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 034b737435..97e0728b67 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -79,7 +79,7 @@ void cpu_loop(CPUARMState *env) { CPUState *cs = env_cpu(env); - int trapnr, ec, fsc, si_code; + int trapnr, ec, fsc, si_code, si_signo; abi_long ret; for (;;) { @@ -121,20 +121,26 @@ void cpu_loop(CPUARMState *env) fsc = extract32(env->exception.syndrome, 0, 6); switch (fsc) { case 0x04 ... 0x07: /* Translation fault, level {0-3} */ + si_signo = TARGET_SIGSEGV; si_code = TARGET_SEGV_MAPERR; break; case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */ case 0x0d ... 0x0f: /* Permission fault, level {1-3} */ + si_signo = TARGET_SIGSEGV; si_code = TARGET_SEGV_ACCERR; break; case 0x11: /* Synchronous Tag Check Fault */ + si_signo = TARGET_SIGSEGV; si_code = TARGET_SEGV_MTESERR; break; + case 0x21: /* Alignment fault */ + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; default: g_assert_not_reached(); } - - force_sig_fault(TARGET_SIGSEGV, si_code, env->exception.vaddress); + force_sig_fault(si_signo, si_code, env->exception.vaddress); break; case EXCP_DEBUG: case EXCP_BKPT: diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index ae09adcb95..01cb6eb534 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -25,6 +25,7 @@ #include "cpu_loop-common.h" #include "signal-common.h" #include "semihosting/common-semi.h" +#include "target/arm/syndrome.h" #define get_user_code_u32(x, gaddr, env) \ ({ abi_long __r = get_user_u32((x), (gaddr)); \ @@ -280,7 +281,7 @@ static bool emulate_arm_fpa11(CPUARMState *env, uint32_t opcode) void cpu_loop(CPUARMState *env) { CPUState *cs = env_cpu(env); - int trapnr; + int trapnr, si_signo, si_code; unsigned int n, insn; abi_ulong ret; @@ -423,9 +424,30 @@ void cpu_loop(CPUARMState *env) break; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - /* XXX: check env->error_code */ - force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, - env->exception.vaddress); + /* For user-only we don't set TTBCR_EAE, so look at the FSR. */ + switch (env->exception.fsr & 0x1f) { + case 0x1: /* Alignment */ + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + case 0x3: /* Access flag fault, level 1 */ + case 0x6: /* Access flag fault, level 2 */ + case 0x9: /* Domain fault, level 1 */ + case 0xb: /* Domain fault, level 2 */ + case 0xd: /* Permision fault, level 1 */ + case 0xf: /* Permision fault, level 2 */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_ACCERR; + break; + case 0x5: /* Translation fault, level 1 */ + case 0x7: /* Translation fault, level 2 */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MAPERR; + break; + default: + g_assert_not_reached(); + } + force_sig_fault(si_signo, si_code, env->exception.vaddress); break; case EXCP_DEBUG: case EXCP_BKPT: diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 7a18a58ca0..a211804fd3 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2035,6 +2035,7 @@ static const struct TCGCPUOps arm_tcg_ops = { #ifdef CONFIG_USER_ONLY .record_sigsegv = arm_cpu_record_sigsegv, + .record_sigbus = arm_cpu_record_sigbus, #else .tlb_fill = arm_cpu_tlb_fill, .cpu_exec_interrupt = arm_cpu_exec_interrupt, diff --git a/target/arm/cpu_tcg.c b/target/arm/cpu_tcg.c index 7b3bea2fbb..13d0e9b195 100644 --- a/target/arm/cpu_tcg.c +++ b/target/arm/cpu_tcg.c @@ -902,6 +902,7 @@ static const struct TCGCPUOps arm_v7m_tcg_ops = { #ifdef CONFIG_USER_ONLY .record_sigsegv = arm_cpu_record_sigsegv, + .record_sigbus = arm_cpu_record_sigbus, #else .tlb_fill = arm_cpu_tlb_fill, .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, diff --git a/target/arm/internals.h b/target/arm/internals.h index 5a7aaf0f51..89f7610ebc 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -548,6 +548,8 @@ static inline bool arm_extabort_type(MemTxResult result) void arm_cpu_record_sigsegv(CPUState *cpu, vaddr addr, MMUAccessType access_type, bool maperr, uintptr_t ra); +void arm_cpu_record_sigbus(CPUState *cpu, vaddr addr, + MMUAccessType access_type, uintptr_t ra); #else bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c index dc5860180f..12a934e924 100644 --- a/target/arm/tlb_helper.c +++ b/target/arm/tlb_helper.c @@ -213,4 +213,10 @@ void arm_cpu_record_sigsegv(CPUState *cs, vaddr addr, cpu_restore_state(cs, ra, true); arm_deliver_fault(cpu, addr, access_type, MMU_USER_IDX, &fi); } + +void arm_cpu_record_sigbus(CPUState *cs, vaddr addr, + MMUAccessType access_type, uintptr_t ra) +{ + arm_cpu_do_unaligned_access(cs, addr, access_type, MMU_USER_IDX, ra); +} #endif /* !defined(CONFIG_USER_ONLY) */ From ee8e0807de6f0ec70454ffbbb778c1246c45af2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 23 Jul 2021 12:24:52 -1000 Subject: [PATCH 1009/1334] linux-user/hppa: Remove EXCP_UNALIGN handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will raise SIGBUS directly from cpu_loop_exit_sigbus. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/hppa/cpu_loop.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index e0a62deeb9..375576c8f0 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -144,13 +144,6 @@ void cpu_loop(CPUHPPAState *env) env->iaoq_f = env->gr[31]; env->iaoq_b = env->gr[31] + 4; break; - case EXCP_UNALIGN: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->cr[CR_IOR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case EXCP_ILL: case EXCP_PRIV_OPR: case EXCP_PRIV_REG: From b414df757d73d0a1d37f14a866ff1338b93a4a27 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 29 Jul 2021 11:56:00 -1000 Subject: [PATCH 1010/1334] target/microblaze: Do not set MO_ALIGN for user-only The kernel will fix up unaligned accesses, so emulate that by allowing unaligned accesses to succeed. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 437bbed6d6..2561b904b9 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -722,6 +722,7 @@ static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) } #endif +#ifndef CONFIG_USER_ONLY static void record_unaligned_ess(DisasContext *dc, int rd, MemOp size, bool store) { @@ -734,6 +735,7 @@ static void record_unaligned_ess(DisasContext *dc, int rd, tcg_set_insn_start_param(dc->insn_start, 1, iflags); } +#endif static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, int mem_index, bool rev) @@ -755,12 +757,19 @@ static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, } } + /* + * For system mode, enforce alignment if the cpu configuration + * requires it. For user-mode, the Linux kernel will have fixed up + * any unaligned access, so emulate that by *not* setting MO_ALIGN. + */ +#ifndef CONFIG_USER_ONLY if (size > MO_8 && (dc->tb_flags & MSR_EE) && dc->cfg->unaligned_exceptions) { record_unaligned_ess(dc, rd, size, false); mop |= MO_ALIGN; } +#endif tcg_gen_qemu_ld_i32(reg_for_write(dc, rd), addr, mem_index, mop); @@ -901,12 +910,19 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, } } + /* + * For system mode, enforce alignment if the cpu configuration + * requires it. For user-mode, the Linux kernel will have fixed up + * any unaligned access, so emulate that by *not* setting MO_ALIGN. + */ +#ifndef CONFIG_USER_ONLY if (size > MO_8 && (dc->tb_flags & MSR_EE) && dc->cfg->unaligned_exceptions) { record_unaligned_ess(dc, rd, size, true); mop |= MO_ALIGN; } +#endif tcg_gen_qemu_st_i32(reg_for_read(dc, rd), addr, mem_index, mop); From 336e91f85332dda0ede4c1d15b87a19a0fb898a2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 29 Jul 2021 13:17:35 -1000 Subject: [PATCH 1011/1334] target/ppc: Move SPR_DSISR setting to powerpc_excp By doing this while sending the exception, we will have already done the unwinding, which makes the ppc_cpu_do_unaligned_access code a bit cleaner. Update the comment about the expected instruction format. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/ppc/excp_helper.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index b7d1767920..88a8de4b80 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -454,13 +454,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) break; } case POWERPC_EXCP_ALIGN: /* Alignment exception */ - /* Get rS/rD and rA from faulting opcode */ /* - * Note: the opcode fields will not be set properly for a - * direct store load/store, but nobody cares as nobody - * actually uses direct store segments. + * Get rS/rD and rA from faulting opcode. + * Note: We will only invoke ALIGN for atomic operations, + * so all instructions are X-form. */ - env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + { + uint32_t insn = cpu_ldl_code(env, env->nip); + env->spr[SPR_DSISR] |= (insn & 0x03FF0000) >> 16; + } break; case POWERPC_EXCP_PROGRAM: /* Program exception */ switch (env->error_code & ~0xF) { @@ -1462,14 +1464,9 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int mmu_idx, uintptr_t retaddr) { CPUPPCState *env = cs->env_ptr; - uint32_t insn; - - /* Restore state and reload the insn we executed, for filling in DSISR. */ - cpu_restore_state(cs, retaddr, true); - insn = cpu_ldl_code(env, env->nip); cs->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = insn & 0x03FF0000; - cpu_loop_exit(cs); + env->error_code = 0; + cpu_loop_exit_restore(cs, retaddr); } #endif From a7e3af132568453d9b4e31ec1ac647ef494f8765 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 29 Jul 2021 13:25:00 -1000 Subject: [PATCH 1012/1334] target/ppc: Set fault address in ppc_cpu_do_unaligned_access We ought to have been recording the virtual address for reporting to the guest trap handler. Cc: qemu-ppc@nongnu.org Reviewed-by: Warner Losh Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/ppc/excp_helper.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 88a8de4b80..e568a54536 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1465,6 +1465,20 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, { CPUPPCState *env = cs->env_ptr; + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + env->spr[SPR_40x_DEAR] = vaddr; + break; + case POWERPC_MMU_BOOKE: + case POWERPC_MMU_BOOKE206: + env->spr[SPR_BOOKE_DEAR] = vaddr; + break; + default: + env->spr[SPR_DAR] = vaddr; + break; + } + cs->exception_index = POWERPC_EXCP_ALIGN; env->error_code = 0; cpu_loop_exit_restore(cs, retaddr); From 996473e4a93d6a6fe0b324afacf398a5a97955d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 4 Oct 2021 15:07:48 -0700 Subject: [PATCH 1013/1334] target/ppc: Restrict ppc_cpu_do_unaligned_access to sysemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is not used by, nor required by, user-only. Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/ppc/excp_helper.c | 8 +++----- target/ppc/internal.h | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index e568a54536..17607adbe4 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1454,11 +1454,8 @@ void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL); } -#endif -#endif /* CONFIG_TCG */ -#endif +#endif /* TARGET_PPC64 */ -#ifdef CONFIG_TCG void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) @@ -1483,4 +1480,5 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, env->error_code = 0; cpu_loop_exit_restore(cs, retaddr); } -#endif +#endif /* CONFIG_TCG */ +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 339974b7d8..6aa9484f34 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -211,11 +211,6 @@ void helper_compute_fprf_float16(CPUPPCState *env, float16 arg); void helper_compute_fprf_float32(CPUPPCState *env, float32 arg); void helper_compute_fprf_float128(CPUPPCState *env, float128 arg); -/* Raise a data fault alignment exception for the specified virtual address */ -void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; - /* translate.c */ int ppc_fixup_cpu(PowerPCCPU *cpu); @@ -291,6 +286,9 @@ void ppc_cpu_record_sigsegv(CPUState *cs, vaddr addr, bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr) QEMU_NORETURN; #endif #endif /* PPC_INTERNAL_H */ From 5057ae5636cbdaea3f61a5800c2ee1961d986adf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 4 Oct 2021 15:08:04 -0700 Subject: [PATCH 1014/1334] linux-user/ppc: Remove POWERPC_EXCP_ALIGN handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will raise SIGBUS directly from cpu_loop_exit_sigbus. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/ppc/cpu_loop.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 840b23736b..483e669300 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -162,14 +162,6 @@ void cpu_loop(CPUPPCState *env) cpu_abort(cs, "External interrupt while in user mode. " "Aborting\n"); break; - case POWERPC_EXCP_ALIGN: /* Alignment exception */ - /* XXX: check this */ - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; case POWERPC_EXCP_PROGRAM: /* Program exception */ case POWERPC_EXCP_HV_EMU: /* HV emulation */ /* XXX: check this */ From 5bcbf3561f63a9a0660fe40b68374258fe592c23 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 4 Oct 2021 10:40:57 -0700 Subject: [PATCH 1015/1334] target/s390x: Implement s390x_cpu_record_sigbus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For s390x, the only unaligned accesses that are signaled are atomic, and we don't actually want to raise SIGBUS for those, but instead raise a SPECIFICATION error, which the kernel will report as SIGILL. Split out a do_unaligned_access function to share between the user-only s390x_cpu_record_sigbus and the sysemu s390x_do_unaligned_access. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/cpu.c | 1 + target/s390x/s390x-internal.h | 8 +++++--- target/s390x/tcg/excp_helper.c | 27 ++++++++++++++++++++------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 593dda75c4..ccdbaf84d5 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -269,6 +269,7 @@ static const struct TCGCPUOps s390_tcg_ops = { #ifdef CONFIG_USER_ONLY .record_sigsegv = s390_cpu_record_sigsegv, + .record_sigbus = s390_cpu_record_sigbus, #else .tlb_fill = s390_cpu_tlb_fill, .cpu_exec_interrupt = s390_cpu_exec_interrupt, diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 163aa4f94a..1a178aed41 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -270,18 +270,20 @@ ObjectClass *s390_cpu_class_by_name(const char *name); void s390x_cpu_debug_excp_handler(CPUState *cs); void s390_cpu_do_interrupt(CPUState *cpu); bool s390_cpu_exec_interrupt(CPUState *cpu, int int_req); -void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; #ifdef CONFIG_USER_ONLY void s390_cpu_record_sigsegv(CPUState *cs, vaddr address, MMUAccessType access_type, bool maperr, uintptr_t retaddr); +void s390_cpu_record_sigbus(CPUState *cs, vaddr address, + MMUAccessType access_type, uintptr_t retaddr); #else bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr) QEMU_NORETURN; #endif diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index b923d080fc..4e7648f301 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -82,6 +82,19 @@ void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc) tcg_s390_data_exception(env, dxc, GETPC()); } +/* + * Unaligned accesses are only diagnosed with MO_ALIGN. At the moment, + * this is only for the atomic operations, for which we want to raise a + * specification exception. + */ +static void QEMU_NORETURN do_unaligned_access(CPUState *cs, uintptr_t retaddr) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, retaddr); +} + #if defined(CONFIG_USER_ONLY) void s390_cpu_do_interrupt(CPUState *cs) @@ -106,6 +119,12 @@ void s390_cpu_record_sigsegv(CPUState *cs, vaddr address, cpu_loop_exit_restore(cs, retaddr); } +void s390_cpu_record_sigbus(CPUState *cs, vaddr address, + MMUAccessType access_type, uintptr_t retaddr) +{ + do_unaligned_access(cs, retaddr); +} + #else /* !CONFIG_USER_ONLY */ static inline uint64_t cpu_mmu_idx_to_asc(int mmu_idx) @@ -593,17 +612,11 @@ void s390x_cpu_debug_excp_handler(CPUState *cs) } } -/* Unaligned accesses are only diagnosed with MO_ALIGN. At the moment, - this is only for the atomic operations, for which we want to raise a - specification exception. */ void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; - - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, retaddr); + do_unaligned_access(cs, retaddr); } static void QEMU_NORETURN monitor_event(CPUS390XState *env, From 0ee0942a7815ac275eba580d7fef812d9a7d2759 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 28 Jul 2021 14:16:56 -1000 Subject: [PATCH 1016/1334] target/sh4: Set fault address in superh_cpu_do_unaligned_access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We ought to have been recording the virtual address for reporting to the guest trap handler. Cc: Yoshinori Sato Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sh4/op_helper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index c996dce7df..752669825f 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -29,6 +29,9 @@ void superh_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { + CPUSH4State *env = cs->env_ptr; + + env->tea = addr; switch (access_type) { case MMU_INST_FETCH: case MMU_DATA_LOAD: @@ -37,6 +40,8 @@ void superh_cpu_do_unaligned_access(CPUState *cs, vaddr addr, case MMU_DATA_STORE: cs->exception_index = 0x100; break; + default: + g_assert_not_reached(); } cpu_loop_exit_restore(cs, retaddr); } From 9852112ee44fbf4cdfcd8e911d3d9aac1cb59b30 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 23 Jul 2021 12:47:17 -1000 Subject: [PATCH 1017/1334] target/sparc: Remove DEBUG_UNALIGNED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The printf should have been qemu_log_mask, the parameters themselves no longer compile, and because this is placed before unwinding the PC is actively wrong. We get better (and correct) logging on the other side of raising the exception, in sparc_cpu_do_interrupt. Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/ldst_helper.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index bbf3601cb1..0549b6adf1 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -27,7 +27,6 @@ //#define DEBUG_MMU //#define DEBUG_MXCC -//#define DEBUG_UNALIGNED //#define DEBUG_UNASSIGNED //#define DEBUG_ASI //#define DEBUG_CACHE_CONTROL @@ -364,10 +363,6 @@ static void do_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align, uintptr_t ra) { if (addr & align) { -#ifdef DEBUG_UNALIGNED - printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx - "\n", addr, env->pc); -#endif cpu_raise_exception_ra(env, TT_UNALIGNED, ra); } } @@ -1968,10 +1963,6 @@ void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; -#ifdef DEBUG_UNALIGNED - printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx - "\n", addr, env->pc); -#endif cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr); } #endif From c0e0c6fe01e8f2a27b241c226fafb578fd896cb2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 30 Jul 2021 07:55:06 -1000 Subject: [PATCH 1018/1334] target/sparc: Split out build_sfsr Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/mmu_helper.c | 72 +++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 2ad47391d0..014601e701 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -502,16 +502,60 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, return 0; } +static uint64_t build_sfsr(CPUSPARCState *env, int mmu_idx, int rw) +{ + uint64_t sfsr = SFSR_VALID_BIT; + + switch (mmu_idx) { + case MMU_PHYS_IDX: + sfsr |= SFSR_CT_NOTRANS; + break; + case MMU_USER_IDX: + case MMU_KERNEL_IDX: + sfsr |= SFSR_CT_PRIMARY; + break; + case MMU_USER_SECONDARY_IDX: + case MMU_KERNEL_SECONDARY_IDX: + sfsr |= SFSR_CT_SECONDARY; + break; + case MMU_NUCLEUS_IDX: + sfsr |= SFSR_CT_NUCLEUS; + break; + default: + g_assert_not_reached(); + } + + if (rw == 1) { + sfsr |= SFSR_WRITE_BIT; + } else if (rw == 4) { + sfsr |= SFSR_NF_BIT; + } + + if (env->pstate & PS_PRIV) { + sfsr |= SFSR_PR_BIT; + } + + if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */ + sfsr |= SFSR_OW_BIT; /* overflow (not read before another fault) */ + } + + /* FIXME: ASI field in SFSR must be set */ + + return sfsr; +} + static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical, int *prot, MemTxAttrs *attrs, target_ulong address, int rw, int mmu_idx) { CPUState *cs = env_cpu(env); unsigned int i; + uint64_t sfsr; uint64_t context; - uint64_t sfsr = 0; bool is_user = false; + sfsr = build_sfsr(env, mmu_idx, rw); + switch (mmu_idx) { case MMU_PHYS_IDX: g_assert_not_reached(); @@ -520,29 +564,18 @@ static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical, /* fallthru */ case MMU_KERNEL_IDX: context = env->dmmu.mmu_primary_context & 0x1fff; - sfsr |= SFSR_CT_PRIMARY; break; case MMU_USER_SECONDARY_IDX: is_user = true; /* fallthru */ case MMU_KERNEL_SECONDARY_IDX: context = env->dmmu.mmu_secondary_context & 0x1fff; - sfsr |= SFSR_CT_SECONDARY; break; - case MMU_NUCLEUS_IDX: - sfsr |= SFSR_CT_NUCLEUS; - /* FALLTHRU */ default: context = 0; break; } - if (rw == 1) { - sfsr |= SFSR_WRITE_BIT; - } else if (rw == 4) { - sfsr |= SFSR_NF_BIT; - } - for (i = 0; i < 64; i++) { /* ctx match, vaddr match, valid? */ if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) { @@ -592,22 +625,9 @@ static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical, return 0; } - if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */ - sfsr |= SFSR_OW_BIT; /* overflow (not read before - another fault) */ - } - - if (env->pstate & PS_PRIV) { - sfsr |= SFSR_PR_BIT; - } - - /* FIXME: ASI field in SFSR must be set */ - env->dmmu.sfsr = sfsr | SFSR_VALID_BIT; - + env->dmmu.sfsr = sfsr; env->dmmu.sfar = address; /* Fault address register */ - env->dmmu.tag_access = (address & ~0x1fffULL) | context; - return 1; } } From aebe51538b90f76a9085dbdeccd87f295316a1dd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 23 Jul 2021 13:55:05 -1000 Subject: [PATCH 1019/1334] target/sparc: Set fault address in sparc_cpu_do_unaligned_access We ought to have been recording the virtual address for reporting to the guest trap handler. Move the function to mmu_helper.c, so that we can re-use code shared with get_physical_address_data. Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/ldst_helper.c | 13 ------------- target/sparc/mmu_helper.c | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 0549b6adf1..a3e1cf9b6e 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -1953,16 +1953,3 @@ void sparc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, is_asi, size, retaddr); } #endif - -#if !defined(CONFIG_USER_ONLY) -void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, - int mmu_idx, - uintptr_t retaddr) -{ - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; - - cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr); -} -#endif diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 014601e701..f2668389b0 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -922,3 +922,23 @@ hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } return phys_addr; } + +#ifndef CONFIG_USER_ONLY +void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, + uintptr_t retaddr) +{ + SPARCCPU *cpu = SPARC_CPU(cs); + CPUSPARCState *env = &cpu->env; + +#ifdef TARGET_SPARC64 + env->dmmu.sfsr = build_sfsr(env, mmu_idx, access_type); + env->dmmu.sfar = addr; +#else + env->mmuregs[4] = addr; +#endif + + cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr); +} +#endif /* !CONFIG_USER_ONLY */ From fce3f474301a104a0d3a02d7d7b42891dea6338b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 25 Jul 2021 08:25:21 -1000 Subject: [PATCH 1020/1334] accel/tcg: Report unaligned atomics for user-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new cpu_loop_exit_sigbus for atomic_mmu_lookup, which has access to complete alignment info from the TCGMemOpIdx arg. Reviewed-by: Warner Losh Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index c4f69908e9..1ee64f01fc 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -474,11 +474,22 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, MemOpIdx oi, int size, int prot, uintptr_t retaddr) { + MemOp mop = get_memop(oi); + int a_bits = get_alignment_bits(mop); + void *ret; + + /* Enforce guest required alignment. */ + if (unlikely(addr & ((1 << a_bits) - 1))) { + MMUAccessType t = prot == PAGE_READ ? MMU_DATA_LOAD : MMU_DATA_STORE; + cpu_loop_exit_sigbus(env_cpu(env), addr, t, retaddr); + } + /* Enforce qemu required alignment. */ if (unlikely(addr & (size - 1))) { cpu_loop_exit_atomic(env_cpu(env), retaddr); } - void *ret = g2h(env_cpu(env), addr); + + ret = g2h(env_cpu(env), addr); set_helper_retaddr(retaddr); return ret; } From 9395cd0a38bfec68d150443f59aa50e44877a9c1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 13 Oct 2021 15:55:24 -0700 Subject: [PATCH 1021/1334] accel/tcg: Report unaligned load/store for user-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new cpu_loop_exit_sigbus for cpu_mmu_lookup. Reviewed-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 1ee64f01fc..aaf47d89d2 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -218,9 +218,14 @@ static void validate_memop(MemOpIdx oi, MemOp expected) static void *cpu_mmu_lookup(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t ra, MMUAccessType type) { + MemOp mop = get_memop(oi); + int a_bits = get_alignment_bits(mop); void *ret; - /* TODO: Enforce guest required alignment. */ + /* Enforce guest required alignment. */ + if (unlikely(addr & ((1 << a_bits) - 1))) { + cpu_loop_exit_sigbus(env_cpu(env), addr, type, ra); + } ret = g2h(env_cpu(env), addr); set_helper_retaddr(ra); From 37e891e38fe1e81fc468d8a000912f8e7ee61336 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 19:41:04 -1000 Subject: [PATCH 1022/1334] tcg: Add helper_unaligned_{ld,st} for user-only sigbus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be called from tcg generated code on hosts that support unaligned accesses natively, in response to an access that is supposed to be aligned. Reviewed-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 11 +++++++++++ include/tcg/tcg-ldst.h | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index aaf47d89d2..1528a21fad 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -27,6 +27,7 @@ #include "exec/helper-proto.h" #include "qemu/atomic128.h" #include "trace/trace-root.h" +#include "tcg/tcg-ldst.h" #include "internal.h" __thread uintptr_t helper_retaddr; @@ -215,6 +216,16 @@ static void validate_memop(MemOpIdx oi, MemOp expected) #endif } +void helper_unaligned_ld(CPUArchState *env, target_ulong addr) +{ + cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_LOAD, GETPC()); +} + +void helper_unaligned_st(CPUArchState *env, target_ulong addr) +{ + cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_STORE, GETPC()); +} + static void *cpu_mmu_lookup(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t ra, MMUAccessType type) { diff --git a/include/tcg/tcg-ldst.h b/include/tcg/tcg-ldst.h index 8c86365611..bf40942de4 100644 --- a/include/tcg/tcg-ldst.h +++ b/include/tcg/tcg-ldst.h @@ -70,5 +70,10 @@ void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, MemOpIdx oi, uintptr_t retaddr); +#else + +void QEMU_NORETURN helper_unaligned_ld(CPUArchState *env, target_ulong addr); +void QEMU_NORETURN helper_unaligned_st(CPUArchState *env, target_ulong addr); + #endif /* CONFIG_SOFTMMU */ #endif /* TCG_LDST_H */ From 742f07628c0a0bd847b47ee0a0b20c44531e0ba5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 4 Oct 2021 19:39:29 -0700 Subject: [PATCH 1023/1334] linux-user: Handle BUS_ADRALN in host_signal_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle BUS_ADRALN via cpu_loop_exit_sigbus, but allow other SIGBUS si_codes to continue into the host-to-guest signal conversion code. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/signal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/signal.c b/linux-user/signal.c index df2c8678d0..81c45bfce9 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -860,6 +860,9 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); } else { sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + if (info->si_code == BUS_ADRALN) { + cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc); + } } sync_sig = true; From 9a5d1352678440656c09fe4f6adc1020beebc0fa Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 21 Sep 2021 09:28:12 -0500 Subject: [PATCH 1024/1334] qemu-img: Consistent docs for convert -F Use consistent capitalization, and fix a missed line (we duplicate the qemu-img synopses in too many places). Fixes: 1899bf4737 (qemu-img: Add -F shorthand to convert) Signed-off-by: Eric Blake Message-Id: <20210921142812.2631605-1-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz --- docs/tools/qemu-img.rst | 2 +- qemu-img-cmds.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index d58980aef8..c0a4443146 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -415,7 +415,7 @@ Command description: 4 Error on reading data -.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps [--skip-broken-bitmaps]] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE [-F backing_fmt]] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps [--skip-broken-bitmaps]] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE [-F BACKING_FMT]] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME Convert the disk image *FILENAME* or a snapshot *SNAPSHOT_PARAM* to disk image *OUTPUT_FILENAME* using format *OUTPUT_FMT*. It can diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 4c4d94ab22..72bcdcfbfa 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -48,7 +48,7 @@ ERST DEF("convert", img_convert, "convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file [-F backing_fmt]] [-o options] [-l snapshot_param] [-S sparse_size] [-r rate_limit] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") SRST -.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE [-F BACKING_FMT]] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME ERST DEF("create", img_create, From 87e4d4a205505cb21970773bbf830df699207587 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Fri, 8 Oct 2021 02:28:20 -0400 Subject: [PATCH 1025/1334] pylint: fix errors and warnings generated by tests/qemu-iotests/297 Test 297 in tests/qemu-iotests currently fails: pylint has learned new things to check, or we simply missed them. All fixes in this patch are related to additional spaces used or wrong indentation. No functional change intended. Signed-off-by: Emanuele Giuseppe Esposito Message-Id: <20211008062821.1010967-2-eesposit@redhat.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/129 | 18 +++++++++--------- tests/qemu-iotests/310 | 16 ++++++++-------- tests/qemu-iotests/check | 11 ++++++----- tests/qemu-iotests/iotests.py | 7 ++++--- tests/qemu-iotests/tests/image-fleecing | 4 ++-- 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129 index 5251e2669e..c75ec62ecf 100755 --- a/tests/qemu-iotests/129 +++ b/tests/qemu-iotests/129 @@ -77,8 +77,8 @@ class TestStopWithBlockJob(iotests.QMPTestCase): self.do_test_stop("drive-backup", device="drive0", target=self.target_img, format=iotests.imgfmt, sync="full", - x_perf={ 'max-chunk': 65536, - 'max-workers': 8 }) + x_perf={'max-chunk': 65536, + 'max-workers': 8}) def test_block_commit(self): # Add overlay above the source node so that we actually use a @@ -88,13 +88,13 @@ class TestStopWithBlockJob(iotests.QMPTestCase): '1G') result = self.vm.qmp('blockdev-add', **{ - 'node-name': 'overlay', - 'driver': iotests.imgfmt, - 'file': { - 'driver': 'file', - 'filename': self.overlay_img - } - }) + 'node-name': 'overlay', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': self.overlay_img + } + }) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('blockdev-snapshot', diff --git a/tests/qemu-iotests/310 b/tests/qemu-iotests/310 index 9d9c928c4b..33c3411869 100755 --- a/tests/qemu-iotests/310 +++ b/tests/qemu-iotests/310 @@ -48,11 +48,11 @@ with iotests.FilePath('base.img') as base_img_path, \ assert qemu_io_silent(base_img_path, '-c', 'write -P 1 3M 1M') == 0 assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, '-F', iotests.imgfmt, mid_img_path) == 0 - assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 2M 1M') == 0 - assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 4M 1M') == 0 + assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 2M 1M') == 0 + assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 4M 1M') == 0 assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path, '-F', iotests.imgfmt, top_img_path) == 0 - assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0 # 0 1 2 3 4 # top 2 @@ -108,10 +108,10 @@ with iotests.FilePath('base.img') as base_img_path, \ assert qemu_img('rebase', '-u', '-b', '', '-f', iotests.imgfmt, top_img_path) == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 0 0 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 3 2M 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 0 3M 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 3 4M 1M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 0 0 1M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 3 2M 1M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 0 3M 1M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 3 4M 1M') == 0 log('Done') diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index da1bfb839e..43a4b694cc 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -37,13 +37,14 @@ def make_argparser() -> argparse.ArgumentParser: p.add_argument('-d', dest='debug', action='store_true', help='debug') p.add_argument('-p', dest='print', action='store_true', - help='redirects qemu\'s stdout and stderr to the test output') + help='redirects qemu\'s stdout and stderr to ' + 'the test output') p.add_argument('-gdb', action='store_true', - help="start gdbserver with $GDB_OPTIONS options \ - ('localhost:12345' if $GDB_OPTIONS is empty)") + help="start gdbserver with $GDB_OPTIONS options " + "('localhost:12345' if $GDB_OPTIONS is empty)") p.add_argument('-valgrind', action='store_true', - help='use valgrind, sets VALGRIND_QEMU environment ' - 'variable') + help='use valgrind, sets VALGRIND_QEMU environment ' + 'variable') p.add_argument('-misalign', action='store_true', help='misalign memory allocations') diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index e2f9d873ad..83bfedb902 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -621,7 +621,7 @@ class VM(qtest.QEMUQtestMachine): super()._post_shutdown() if not qemu_valgrind or not self._popen: return - valgrind_filename = f"{test_dir}/{self._popen.pid}.valgrind" + valgrind_filename = f"{test_dir}/{self._popen.pid}.valgrind" if self.exitcode() == 99: with open(valgrind_filename, encoding='utf-8') as f: print(f.read()) @@ -1363,8 +1363,9 @@ class ReproducibleStreamWrapper: class ReproducibleTestRunner(unittest.TextTestRunner): def __init__(self, stream: Optional[TextIO] = None, - resultclass: Type[unittest.TestResult] = ReproducibleTestResult, - **kwargs: Any) -> None: + resultclass: Type[unittest.TestResult] = + ReproducibleTestResult, + **kwargs: Any) -> None: rstream = ReproducibleStreamWrapper(stream or sys.stdout) super().__init__(stream=rstream, # type: ignore descriptions=True, diff --git a/tests/qemu-iotests/tests/image-fleecing b/tests/qemu-iotests/tests/image-fleecing index 35164e9036..a58b5a1781 100755 --- a/tests/qemu-iotests/tests/image-fleecing +++ b/tests/qemu-iotests/tests/image-fleecing @@ -115,8 +115,8 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): nbd_uri = 'nbd+unix:///%s?socket=%s' % (tmp_node, nbd_sock_path) log(vm.qmp('nbd-server-start', - {'addr': { 'type': 'unix', - 'data': { 'path': nbd_sock_path } } })) + {'addr': {'type': 'unix', + 'data': {'path': nbd_sock_path}}})) log(vm.qmp('nbd-server-add', device=tmp_node)) From f3d43dfd9a8a870160f0aaf7bd692b9ce4827ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 13 Sep 2021 15:04:19 +0200 Subject: [PATCH 1026/1334] vmdk: allow specification of tools version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VMDK files support an attribute that represents the version of the guest tools that are installed on the disk. This attribute is used by vSphere before a machine has been started to determine if the VM has the guest tools installed. This is important when configuring "Operating system customizations" in vSphere, as it checks for the presence of the guest tools before allowing those customizations. Thus when the VM has not yet booted normally it would be impossible to customize it, therefore preventing a customized first-boot. The attribute should not hurt on disks that do not have the guest tools installed and indeed the VMware tools also unconditionally add this attribute. (Defaulting to the value "2147483647", as is done in this patch) Signed-off-by: Thomas Weißschuh Message-Id: <20210913130419.13241-1-thomas.weissschuh.ext@zeiss.com> [hreitz: Added missing '#' in block-core.json] Signed-off-by: Hanna Reitz --- block/vmdk.c | 24 ++++++++++++++++++++---- qapi/block-core.json | 3 +++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index fb4cc9da90..0dfab6e941 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -60,6 +60,7 @@ #define VMDK_ZEROED (-3) #define BLOCK_OPT_ZEROED_GRAIN "zeroed_grain" +#define BLOCK_OPT_TOOLSVERSION "toolsversion" typedef struct { uint32_t version; @@ -2344,6 +2345,7 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, BlockdevVmdkAdapterType adapter_type, const char *backing_file, const char *hw_version, + const char *toolsversion, bool compat6, bool zeroed_grain, vmdk_create_extent_fn extent_fn, @@ -2384,7 +2386,8 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, "ddb.geometry.cylinders = \"%" PRId64 "\"\n" "ddb.geometry.heads = \"%" PRIu32 "\"\n" "ddb.geometry.sectors = \"63\"\n" - "ddb.adapterType = \"%s\"\n"; + "ddb.adapterType = \"%s\"\n" + "ddb.toolsVersion = \"%s\"\n"; ext_desc_lines = g_string_new(NULL); @@ -2401,6 +2404,9 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, if (!hw_version) { hw_version = "4"; } + if (!toolsversion) { + toolsversion = "2147483647"; + } if (adapter_type != BLOCKDEV_VMDK_ADAPTER_TYPE_IDE) { /* that's the number of heads with which vmware operates when @@ -2525,7 +2531,8 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, size / (int64_t)(63 * number_heads * BDRV_SECTOR_SIZE), number_heads, - BlockdevVmdkAdapterType_str(adapter_type)); + BlockdevVmdkAdapterType_str(adapter_type), + toolsversion); desc_len = strlen(desc); /* the descriptor offset = 0x200 */ if (!split && !flat) { @@ -2617,6 +2624,7 @@ static int coroutine_fn vmdk_co_create_opts(BlockDriver *drv, BlockdevVmdkAdapterType adapter_type_enum; char *backing_file = NULL; char *hw_version = NULL; + char *toolsversion = NULL; char *fmt = NULL; BlockdevVmdkSubformat subformat; int ret = 0; @@ -2649,6 +2657,7 @@ static int coroutine_fn vmdk_co_create_opts(BlockDriver *drv, adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE); backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION); + toolsversion = qemu_opt_get_del(opts, BLOCK_OPT_TOOLSVERSION); compat6 = qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false); if (strcmp(hw_version, "undefined") == 0) { g_free(hw_version); @@ -2692,14 +2701,15 @@ static int coroutine_fn vmdk_co_create_opts(BlockDriver *drv, .opts = opts, }; ret = vmdk_co_do_create(total_size, subformat, adapter_type_enum, - backing_file, hw_version, compat6, zeroed_grain, - vmdk_co_create_opts_cb, &data, errp); + backing_file, hw_version, toolsversion, compat6, + zeroed_grain, vmdk_co_create_opts_cb, &data, errp); exit: g_free(backing_fmt); g_free(adapter_type); g_free(backing_file); g_free(hw_version); + g_free(toolsversion); g_free(fmt); g_free(desc); g_free(path); @@ -2782,6 +2792,7 @@ static int coroutine_fn vmdk_co_create(BlockdevCreateOptions *create_options, opts->adapter_type, opts->backing_file, opts->hwversion, + opts->toolsversion, false, opts->zeroed_grain, vmdk_co_create_cb, @@ -3031,6 +3042,11 @@ static QemuOptsList vmdk_create_opts = { .help = "VMDK hardware version", .def_value_str = "undefined" }, + { + .name = BLOCK_OPT_TOOLSVERSION, + .type = QEMU_OPT_STRING, + .help = "VMware guest tools version", + }, { .name = BLOCK_OPT_SUBFMT, .type = QEMU_OPT_STRING, diff --git a/qapi/block-core.json b/qapi/block-core.json index ce2c1352cb..69698a4ffe 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4691,6 +4691,8 @@ # @adapter-type: The adapter type used to fill in the descriptor. Default: ide. # @hwversion: Hardware version. The meaningful options are "4" or "6". # Default: "4". +# @toolsversion: VMware guest tools version. +# Default: "2147483647" (Since 6.2) # @zeroed-grain: Whether to enable zeroed-grain feature for sparse subformats. # Default: false. # @@ -4704,6 +4706,7 @@ '*backing-file': 'str', '*adapter-type': 'BlockdevVmdkAdapterType', '*hwversion': 'str', + '*toolsversion': 'str', '*zeroed-grain': 'bool' } } From 7da9623cc078229caf07c290e16401ccdb9408d2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 12 Oct 2021 10:27:02 +0200 Subject: [PATCH 1027/1334] block/vpc: Add a sanity check that fixed-size images have the right type The code in vpc.c uses BDRVVPCState->footer.type in various places to decide whether the image is a fixed-size (VHD_FIXED) or a dynamic (VHD_DYNAMIC) image. However, we never check that this field really contains VHD_FIXED if we detected a fixed size image in vpc_open(), so a wrong value here could cause quite some trouble during runtime. Suggested-by: Kevin Wolf Signed-off-by: Thomas Huth Message-Id: <20211012082702.792259-1-thuth@redhat.com> Signed-off-by: Hanna Reitz --- block/vpc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/vpc.c b/block/vpc.c index 1b4c7333af..297a26262a 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -276,7 +276,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, if (ret < 0) { goto fail; } - if (strncmp(footer->creator, "conectix", 8)) { + if (strncmp(footer->creator, "conectix", 8) || + be32_to_cpu(footer->type) != VHD_FIXED) { error_setg(errp, "invalid VPC image"); ret = -EINVAL; goto fail; From 13a028336f2c05e7ff47dfdaf30dfac7f4883e80 Mon Sep 17 00:00:00 2001 From: Ari Sundholm Date: Tue, 19 Oct 2021 14:09:55 +0300 Subject: [PATCH 1028/1334] block/file-posix: Fix return value translation for AIO discards AIO discards regressed as a result of the following commit: 0dfc7af2 block/file-posix: Optimize for macOS When trying to run blkdiscard within a Linux guest, the request would fail, with some errors in dmesg: ---- [ snip ] ---- [ 4.010070] sd 2:0:0:0: [sda] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE [ 4.011061] sd 2:0:0:0: [sda] tag#0 Sense Key : Aborted Command [current] [ 4.011061] sd 2:0:0:0: [sda] tag#0 Add. Sense: I/O process terminated [ 4.011061] sd 2:0:0:0: [sda] tag#0 CDB: Unmap/Read sub-channel 42 00 00 00 00 00 00 00 18 00 [ 4.011061] blk_update_request: I/O error, dev sda, sector 0 ---- [ snip ] ---- This turns out to be a result of a flaw in changes to the error value translation logic in handle_aiocb_discard(). The default return value may be left untranslated in some configurations, and the wrong variable is used in one translation. Fix both issues. Fixes: 0dfc7af2b28 ("block/file-posix: Optimize for macOS") Cc: qemu-stable@nongnu.org Signed-off-by: Ari Sundholm Signed-off-by: Emil Karlson Reviewed-by: Akihiko Odaki Reviewed-by: Stefan Hajnoczi Message-Id: <20211019110954.4170931-1-ari@tuxera.com> Signed-off-by: Kevin Wolf --- block/file-posix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 53be0bdc1b..6def2a4cba 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1807,7 +1807,7 @@ static int handle_aiocb_copy_range(void *opaque) static int handle_aiocb_discard(void *opaque) { RawPosixAIOData *aiocb = opaque; - int ret = -EOPNOTSUPP; + int ret = -ENOTSUP; BDRVRawState *s = aiocb->bs->opaque; if (!s->has_discard) { @@ -1829,7 +1829,7 @@ static int handle_aiocb_discard(void *opaque) #ifdef CONFIG_FALLOCATE_PUNCH_HOLE ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, aiocb->aio_offset, aiocb->aio_nbytes); - ret = translate_err(-errno); + ret = translate_err(ret); #elif defined(__APPLE__) && (__MACH__) fpunchhole_t fpunchhole; fpunchhole.fp_flags = 0; From bfb8aa6d583b09378dcdb85d40c7951e44acd09f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 18 Oct 2021 15:47:14 +0200 Subject: [PATCH 1029/1334] block: Fail gracefully when blockdev-snapshot creates loops Using blockdev-snapshot to append a node as an overlay to itself, or to any of its parents, causes crashes. Catch the condition and return an error for these cases instead. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1824363 Signed-off-by: Kevin Wolf Message-Id: <20211018134714.48438-1-kwolf@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Kevin Wolf --- block.c | 10 ++++++++++ tests/qemu-iotests/085 | 31 ++++++++++++++++++++++++++++++- tests/qemu-iotests/085.out | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index 45f653a88b..580cb77a70 100644 --- a/block.c +++ b/block.c @@ -84,6 +84,9 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, BdrvChildRole child_role, Error **errp); +static bool bdrv_recurse_has_child(BlockDriverState *bs, + BlockDriverState *child); + static void bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs); static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, @@ -2673,6 +2676,7 @@ static void bdrv_replace_child_noperm(BdrvChild *child, int drain_saldo; assert(!child->frozen); + assert(old_bs != new_bs); if (old_bs && new_bs) { assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); @@ -2892,6 +2896,12 @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, assert(parent_bs->drv); + if (bdrv_recurse_has_child(child_bs, parent_bs)) { + error_setg(errp, "Making '%s' a %s child of '%s' would create a cycle", + child_bs->node_name, child_name, parent_bs->node_name); + return -EINVAL; + } + bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL, perm, shared_perm, &perm, &shared_perm); diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 index d557522943..de74262a26 100755 --- a/tests/qemu-iotests/085 +++ b/tests/qemu-iotests/085 @@ -103,11 +103,18 @@ do_blockdev_add() } # ${1}: unique identifier for the snapshot filename -add_snapshot_image() +create_snapshot_image() { base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}" snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}" TEST_IMG=$snapshot_file _make_test_img -u -b "${base_image}" -F $IMGFMT "$size" +} + +# ${1}: unique identifier for the snapshot filename +add_snapshot_image() +{ + snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}" + create_snapshot_image "$1" do_blockdev_add "$1" "'backing': null, " "${snapshot_file}" } @@ -230,6 +237,28 @@ _make_test_img -b "${TEST_IMG}.base" -F $IMGFMT "$size" do_blockdev_add ${SNAPSHOTS} "" "${TEST_IMG}" blockdev_snapshot ${SNAPSHOTS} error +echo +echo === Invalid command - creating loops === +echo + +SNAPSHOTS=$((${SNAPSHOTS}+1)) +add_snapshot_image ${SNAPSHOTS} + +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node':'snap_${SNAPSHOTS}', + 'overlay':'snap_${SNAPSHOTS}' } + }" "error" + +SNAPSHOTS=$((${SNAPSHOTS}+1)) +create_snapshot_image ${SNAPSHOTS} +do_blockdev_add ${SNAPSHOTS} "'backing': 'snap_$((${SNAPSHOTS}-1))', " \ + "${TEST_DIR}/${SNAPSHOTS}-${snapshot_virt0}" + +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node':'snap_${SNAPSHOTS}', + 'overlay':'snap_$((${SNAPSHOTS}-1))' } + }" "error" + echo echo === Invalid command - The node does not exist === echo diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out index 1d4c565b6d..b543b992ff 100644 --- a/tests/qemu-iotests/085.out +++ b/tests/qemu-iotests/085.out @@ -217,15 +217,42 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ 'overlay':'snap_13' } } {"error": {"class": "GenericError", "desc": "The overlay already has a backing image"}} +=== Invalid command - creating loops === + +Formatting 'TEST_DIR/14-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/13-snapshot-v0.IMGFMT backing_fmt=IMGFMT +{ 'execute': 'blockdev-add', 'arguments': + { 'driver': 'IMGFMT', 'node-name': 'snap_14', 'backing': null, + 'file': + { 'driver': 'file', 'filename': 'TEST_DIR/14-snapshot-v0.IMGFMT', + 'node-name': 'file_14' } } } +{"return": {}} +{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node':'snap_14', + 'overlay':'snap_14' } + } +{"error": {"class": "GenericError", "desc": "Making 'snap_14' a backing child of 'snap_14' would create a cycle"}} +Formatting 'TEST_DIR/15-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/14-snapshot-v0.IMGFMT backing_fmt=IMGFMT +{ 'execute': 'blockdev-add', 'arguments': + { 'driver': 'IMGFMT', 'node-name': 'snap_15', 'backing': 'snap_14', + 'file': + { 'driver': 'file', 'filename': 'TEST_DIR/15-snapshot-v0.IMGFMT', + 'node-name': 'file_15' } } } +{"return": {}} +{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node':'snap_15', + 'overlay':'snap_14' } + } +{"error": {"class": "GenericError", "desc": "Making 'snap_15' a backing child of 'snap_14' would create a cycle"}} + === Invalid command - The node does not exist === { 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', - 'overlay':'snap_14' } } -{"error": {"class": "GenericError", "desc": "Cannot find device='snap_14' nor node-name='snap_14'"}} + 'overlay':'snap_16' } } +{"error": {"class": "GenericError", "desc": "Cannot find device='snap_16' nor node-name='snap_16'"}} { 'execute': 'blockdev-snapshot', 'arguments': { 'node':'nodevice', - 'overlay':'snap_13' } + 'overlay':'snap_15' } } {"error": {"class": "GenericError", "desc": "Cannot find device='nodevice' nor node-name='nodevice'"}} *** done From 0347a8fd4c3faaedf119be04c197804be40a384b Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 12 Oct 2021 17:22:31 +0200 Subject: [PATCH 1030/1334] block/rbd: implement bdrv_co_block_status the qemu rbd driver currently lacks support for bdrv_co_block_status. This results mainly in incorrect progress during block operations (e.g. qemu-img convert with an rbd image as source). This patch utilizes the rbd_diff_iterate2 call from librbd to detect allocated and unallocated (all zero areas). To avoid querying the ceph OSDs for the answer this is only done if the image has the fast-diff feature which depends on the object-map and exclusive-lock features. In this case it is guaranteed that the information is present in memory in the librbd client and thus very fast. If fast-diff is not available all areas are reported to be allocated which is the current behaviour if bdrv_co_block_status is not implemented. Signed-off-by: Peter Lieven Message-Id: <20211012152231.24868-1-pl@kamp.de> Reviewed-by: Ilya Dryomov Signed-off-by: Kevin Wolf --- block/rbd.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/block/rbd.c b/block/rbd.c index 701fbf2b0c..def96292e0 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -97,6 +97,12 @@ typedef struct RBDTask { int64_t ret; } RBDTask; +typedef struct RBDDiffIterateReq { + uint64_t offs; + uint64_t bytes; + bool exists; +} RBDDiffIterateReq; + static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, BlockdevOptionsRbd *opts, bool cache, const char *keypairs, const char *secretid, @@ -1259,6 +1265,111 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, return spec_info; } +/* + * rbd_diff_iterate2 allows to interrupt the exection by returning a negative + * value in the callback routine. Choose a value that does not conflict with + * an existing exitcode and return it if we want to prematurely stop the + * execution because we detected a change in the allocation status. + */ +#define QEMU_RBD_EXIT_DIFF_ITERATE2 -9000 + +static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len, + int exists, void *opaque) +{ + RBDDiffIterateReq *req = opaque; + + assert(req->offs + req->bytes <= offs); + /* + * we do not diff against a snapshot so we should never receive a callback + * for a hole. + */ + assert(exists); + + if (!req->exists && offs > req->offs) { + /* + * we started in an unallocated area and hit the first allocated + * block. req->bytes must be set to the length of the unallocated area + * before the allocated area. stop further processing. + */ + req->bytes = offs - req->offs; + return QEMU_RBD_EXIT_DIFF_ITERATE2; + } + + if (req->exists && offs > req->offs + req->bytes) { + /* + * we started in an allocated area and jumped over an unallocated area, + * req->bytes contains the length of the allocated area before the + * unallocated area. stop further processing. + */ + return QEMU_RBD_EXIT_DIFF_ITERATE2; + } + + req->bytes += len; + req->exists = true; + + return 0; +} + +static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs, + bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, + int64_t *map, + BlockDriverState **file) +{ + BDRVRBDState *s = bs->opaque; + int status, r; + RBDDiffIterateReq req = { .offs = offset }; + uint64_t features, flags; + + assert(offset + bytes <= s->image_size); + + /* default to all sectors allocated */ + status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; + *map = offset; + *file = bs; + *pnum = bytes; + + /* check if RBD image supports fast-diff */ + r = rbd_get_features(s->image, &features); + if (r < 0) { + return status; + } + if (!(features & RBD_FEATURE_FAST_DIFF)) { + return status; + } + + /* check if RBD fast-diff result is valid */ + r = rbd_get_flags(s->image, &flags); + if (r < 0) { + return status; + } + if (flags & RBD_FLAG_FAST_DIFF_INVALID) { + return status; + } + + r = rbd_diff_iterate2(s->image, NULL, offset, bytes, true, true, + qemu_rbd_diff_iterate_cb, &req); + if (r < 0 && r != QEMU_RBD_EXIT_DIFF_ITERATE2) { + return status; + } + assert(req.bytes <= bytes); + if (!req.exists) { + if (r == 0) { + /* + * rbd_diff_iterate2 does not invoke callbacks for unallocated + * areas. This here catches the case where no callback was + * invoked at all (req.bytes == 0). + */ + assert(req.bytes == 0); + req.bytes = bytes; + } + status = BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID; + } + + *pnum = req.bytes; + return status; +} + static int64_t qemu_rbd_getlength(BlockDriverState *bs) { BDRVRBDState *s = bs->opaque; @@ -1494,6 +1605,7 @@ static BlockDriver bdrv_rbd = { #ifdef LIBRBD_SUPPORTS_WRITE_ZEROES .bdrv_co_pwrite_zeroes = qemu_rbd_co_pwrite_zeroes, #endif + .bdrv_co_block_status = qemu_rbd_co_block_status, .bdrv_snapshot_create = qemu_rbd_snap_create, .bdrv_snapshot_delete = qemu_rbd_snap_remove, From 46e018e9b741731842b93ce23a86fad60445969b Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 24 Aug 2021 12:43:44 +0200 Subject: [PATCH 1031/1334] ide: Cap LBA28 capacity announcement to 2^28-1 The LBA28 capacity (at offsets 60/61 of identification) is supposed to express the maximum size supported by LBA28 commands. If the device is larger than this, we have to cap it to 2^28-1. At least NetBSD happens to be using this value to determine whether to use LBA28 or LBA48 for its commands, using LBA28 for sectors that don't need LBA48. This commit thus fixes NetBSD access to disks larger than 128GiB. Signed-off-by: Samuel Thibault Message-Id: <20210824104344.3878849-1-samuel.thibault@ens-lyon.org> Signed-off-by: Kevin Wolf --- hw/ide/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index fd69ca3167..e28f8aad61 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -98,8 +98,12 @@ static void put_le16(uint16_t *p, unsigned int v) static void ide_identify_size(IDEState *s) { uint16_t *p = (uint16_t *)s->identify_data; - put_le16(p + 60, s->nb_sectors); - put_le16(p + 61, s->nb_sectors >> 16); + int64_t nb_sectors_lba28 = s->nb_sectors; + if (nb_sectors_lba28 >= 1 << 28) { + nb_sectors_lba28 = (1 << 28) - 1; + } + put_le16(p + 60, nb_sectors_lba28); + put_le16(p + 61, nb_sectors_lba28 >> 16); put_le16(p + 100, s->nb_sectors); put_le16(p + 101, s->nb_sectors >> 16); put_le16(p + 102, s->nb_sectors >> 32); From 304332039014679b809f606e2f227ee0fc43a451 Mon Sep 17 00:00:00 2001 From: Fabrice Fontaine Date: Fri, 22 Oct 2021 11:52:09 +0200 Subject: [PATCH 1032/1334] block/export/fuse.c: fix musl build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include linux/falloc.h if CONFIG_FALLOCATE_ZERO_RANGE is defined to fix https://gitlab.com/qemu-project/qemu/-/commit/50482fda98bd62e072c30b7ea73c985c4e9d9bbb and avoid the following build failure on musl: ../block/export/fuse.c: In function 'fuse_fallocate': ../block/export/fuse.c:643:21: error: 'FALLOC_FL_ZERO_RANGE' undeclared (first use in this function) 643 | else if (mode & FALLOC_FL_ZERO_RANGE) { | ^~~~~~~~~~~~~~~~~~~~ Fixes: - http://autobuild.buildroot.org/results/be24433a429fda681fb66698160132c1c99bc53b Fixes: 50482fda98b ("block/export/fuse.c: fix musl build") Signed-off-by: Fabrice Fontaine Message-Id: <20211022095209.1319671-1-fontaine.fabrice@gmail.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Kevin Wolf --- block/export/fuse.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block/export/fuse.c b/block/export/fuse.c index 2e3bf8270b..823c126d23 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -31,6 +31,10 @@ #include #include +#if defined(CONFIG_FALLOCATE_ZERO_RANGE) +#include +#endif + #ifdef __linux__ #include #endif From 684960d46267d6e6443b39ee98b3a95632ba8dc6 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 26 Oct 2021 18:23:44 +0200 Subject: [PATCH 1033/1334] file-posix: add `aio-max-batch` option Commit d7ddd0a161 ("linux-aio: limit the batch size using `aio-max-batch` parameter") added a way to limit the batch size of Linux AIO backend for the entire AIO context. The same AIO context can be shared by multiple devices, so latency-sensitive devices may want to limit the batch size even more to avoid increasing latency. For this reason we add the `aio-max-batch` option to the file backend, which will be used by the next commits to limit the size of batches including requests generated by this device. Suggested-by: Kevin Wolf Reviewed-by: Kevin Wolf Signed-off-by: Stefano Garzarella Message-Id: <20211026162346.253081-2-sgarzare@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/file-posix.c | 9 +++++++++ qapi/block-core.json | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/block/file-posix.c b/block/file-posix.c index 6def2a4cba..7a289a9481 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -150,6 +150,8 @@ typedef struct BDRVRawState { uint64_t locked_perm; uint64_t locked_shared_perm; + uint64_t aio_max_batch; + int perm_change_fd; int perm_change_flags; BDRVReopenState *reopen_state; @@ -530,6 +532,11 @@ static QemuOptsList raw_runtime_opts = { .type = QEMU_OPT_STRING, .help = "host AIO implementation (threads, native, io_uring)", }, + { + .name = "aio-max-batch", + .type = QEMU_OPT_NUMBER, + .help = "AIO max batch size (0 = auto handled by AIO backend, default: 0)", + }, { .name = "locking", .type = QEMU_OPT_STRING, @@ -609,6 +616,8 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, s->use_linux_io_uring = (aio == BLOCKDEV_AIO_OPTIONS_IO_URING); #endif + s->aio_max_batch = qemu_opt_get_number(opts, "aio-max-batch", 0); + locking = qapi_enum_parse(&OnOffAuto_lookup, qemu_opt_get(opts, "locking"), ON_OFF_AUTO_AUTO, &local_err); diff --git a/qapi/block-core.json b/qapi/block-core.json index ce2c1352cb..ea36e0038c 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2939,6 +2939,12 @@ # for this device (default: none, forward the commands via SG_IO; # since 2.11) # @aio: AIO backend (default: threads) (since: 2.8) +# @aio-max-batch: maximum number of requests to batch together into a single +# submission in the AIO backend. The smallest value between +# this and the aio-max-batch value of the IOThread object is +# chosen. +# 0 means that the AIO backend will handle it automatically. +# (default: 0, since 6.2) # @locking: whether to enable file locking. If set to 'auto', only enable # when Open File Descriptor (OFD) locking API is available # (default: auto, since 2.10) @@ -2968,6 +2974,7 @@ '*pr-manager': 'str', '*locking': 'OnOffAuto', '*aio': 'BlockdevAioOptions', + '*aio-max-batch': 'int', '*drop-cache': {'type': 'bool', 'if': 'CONFIG_LINUX'}, '*x-check-cache-dropped': { 'type': 'bool', From 512da211010700cdbfaab45c8980ca88958a4ab8 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 26 Oct 2021 18:23:45 +0200 Subject: [PATCH 1034/1334] linux-aio: add `dev_max_batch` parameter to laio_co_submit() This new parameter can be used by block devices to limit the Linux AIO batch size more than the limit set by the AIO context. file-posix backend supports this, passing its `aio-max-batch` option previously added. Add an helper function to calculate the maximum batch size. Reviewed-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Signed-off-by: Stefano Garzarella Message-Id: <20211026162346.253081-3-sgarzare@redhat.com> Signed-off-by: Kevin Wolf --- block/file-posix.c | 3 ++- block/linux-aio.c | 30 ++++++++++++++++++++++-------- include/block/raw-aio.h | 3 ++- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 7a289a9481..c2c94fca66 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2066,7 +2066,8 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, } else if (s->use_linux_aio) { LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs)); assert(qiov->size == bytes); - return laio_co_submit(bs, aio, s->fd, offset, qiov, type); + return laio_co_submit(bs, aio, s->fd, offset, qiov, type, + s->aio_max_batch); #endif } diff --git a/block/linux-aio.c b/block/linux-aio.c index 0dab507b71..88b44fee72 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -334,6 +334,23 @@ static void ioq_submit(LinuxAioState *s) } } +static uint64_t laio_max_batch(LinuxAioState *s, uint64_t dev_max_batch) +{ + uint64_t max_batch = s->aio_context->aio_max_batch ?: DEFAULT_MAX_BATCH; + + /* + * AIO context can be shared between multiple block devices, so + * `dev_max_batch` allows reducing the batch size for latency-sensitive + * devices. + */ + max_batch = MIN_NON_ZERO(dev_max_batch, max_batch); + + /* limit the batch with the number of available events */ + max_batch = MIN_NON_ZERO(MAX_EVENTS - s->io_q.in_flight, max_batch); + + return max_batch; +} + void laio_io_plug(BlockDriverState *bs, LinuxAioState *s) { s->io_q.plugged++; @@ -349,15 +366,11 @@ void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s) } static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, - int type) + int type, uint64_t dev_max_batch) { LinuxAioState *s = laiocb->ctx; struct iocb *iocbs = &laiocb->iocb; QEMUIOVector *qiov = laiocb->qiov; - int64_t max_batch = s->aio_context->aio_max_batch ?: DEFAULT_MAX_BATCH; - - /* limit the batch with the number of available events */ - max_batch = MIN_NON_ZERO(MAX_EVENTS - s->io_q.in_flight, max_batch); switch (type) { case QEMU_AIO_WRITE: @@ -378,7 +391,7 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, s->io_q.in_queue++; if (!s->io_q.blocked && (!s->io_q.plugged || - s->io_q.in_queue >= max_batch)) { + s->io_q.in_queue >= laio_max_batch(s, dev_max_batch))) { ioq_submit(s); } @@ -386,7 +399,8 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, } int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, - uint64_t offset, QEMUIOVector *qiov, int type) + uint64_t offset, QEMUIOVector *qiov, int type, + uint64_t dev_max_batch) { int ret; struct qemu_laiocb laiocb = { @@ -398,7 +412,7 @@ int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, .qiov = qiov, }; - ret = laio_do_submit(fd, &laiocb, offset, type); + ret = laio_do_submit(fd, &laiocb, offset, type, dev_max_batch); if (ret < 0) { return ret; } diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 251b10d273..ebd042fa27 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -51,7 +51,8 @@ typedef struct LinuxAioState LinuxAioState; LinuxAioState *laio_init(Error **errp); void laio_cleanup(LinuxAioState *s); int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, - uint64_t offset, QEMUIOVector *qiov, int type); + uint64_t offset, QEMUIOVector *qiov, int type, + uint64_t dev_max_batch); void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); void laio_io_plug(BlockDriverState *bs, LinuxAioState *s); From 68d7946648a5c364a4df187804d37f09a318b50f Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 26 Oct 2021 18:23:46 +0200 Subject: [PATCH 1035/1334] linux-aio: add `dev_max_batch` parameter to laio_io_unplug() Between the submission of a request and the unplug, other devices with larger limits may have been queued new requests without flushing the batch. Using the new `dev_max_batch` parameter, laio_io_unplug() can check if the batch exceeds the device limit to flush the current batch. Reviewed-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf Signed-off-by: Stefano Garzarella Message-Id: <20211026162346.253081-4-sgarzare@redhat.com> Signed-off-by: Kevin Wolf --- block/file-posix.c | 2 +- block/linux-aio.c | 8 +++++--- include/block/raw-aio.h | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index c2c94fca66..7a27c83060 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2125,7 +2125,7 @@ static void raw_aio_unplug(BlockDriverState *bs) #ifdef CONFIG_LINUX_AIO if (s->use_linux_aio) { LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs)); - laio_io_unplug(bs, aio); + laio_io_unplug(bs, aio, s->aio_max_batch); } #endif #ifdef CONFIG_LINUX_IO_URING diff --git a/block/linux-aio.c b/block/linux-aio.c index 88b44fee72..f53ae72e21 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -356,11 +356,13 @@ void laio_io_plug(BlockDriverState *bs, LinuxAioState *s) s->io_q.plugged++; } -void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s) +void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s, + uint64_t dev_max_batch) { assert(s->io_q.plugged); - if (--s->io_q.plugged == 0 && - !s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { + if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch) || + (--s->io_q.plugged == 0 && + !s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending))) { ioq_submit(s); } } diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index ebd042fa27..21fc10c4c9 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -56,7 +56,8 @@ int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); void laio_io_plug(BlockDriverState *bs, LinuxAioState *s); -void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s); +void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s, + uint64_t dev_max_batch); #endif /* io_uring.c - Linux io_uring implementation */ #ifdef CONFIG_LINUX_IO_URING From 73d4a11300ee87e04f93fab4380b80379e792a06 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Tue, 26 Oct 2021 11:07:45 +0200 Subject: [PATCH 1036/1334] block-backend: Silence clang -m32 compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to e7e588d432d31ecebc26358e47201dd108db964c, there is a warning in block/block-backend.c that qiov->size <= INT64_MAX is always true on machines where size_t is narrower than a uint64_t. In said commit, we silenced this warning by casting to uint64_t. The commit introducing this warning here (a93d81c84afa717b0a1a6947524d8d1fbfd6bbf5) anticipated it and so tried to address it the same way. However, it only did so in one of two places where this comparison occurs, and so we still need to fix up the other one. Fixes: a93d81c84afa717b0a1a6947524d8d1fbfd6bbf5 ("block-backend: convert blk_aio_ functions to int64_t bytes paramter") Signed-off-by: Hanna Reitz Message-Id: <20211026090745.30800-1-hreitz@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Kevin Wolf --- block/block-backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/block-backend.c b/block/block-backend.c index 39cd99df2b..12ef80ea17 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1540,7 +1540,7 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { - assert(qiov->size <= INT64_MAX); + assert((uint64_t)qiov->size <= INT64_MAX); return blk_aio_prwv(blk, offset, qiov->size, qiov, blk_aio_write_entry, flags, cb, opaque); } From e955acd91dc3c1dfe13e09d2b63242932d54ae14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Oct 2021 10:31:57 +0200 Subject: [PATCH 1037/1334] MAINTAINERS: Add MIPS general architecture support entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The architecture is covered in TCG (frontend and backend) and hardware models. Add a generic section matching the 'mips' word in patch subjects. Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211004092515.3819836-2-f4bug@amsat.org> Reviewed-by: Richard Henderson --- MAINTAINERS | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 310a9512ea..a156c4bffc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -109,6 +109,12 @@ K: ^Subject:.*(?i)s390x? T: git https://gitlab.com/cohuck/qemu.git s390-next L: qemu-s390x@nongnu.org +MIPS general architecture support +M: Philippe Mathieu-Daudé +R: Jiaxun Yang +S: Odd Fixes +K: ^Subject:.*(?i)mips + Guest CPU cores (TCG) --------------------- Overall TCG CPUs @@ -242,7 +248,6 @@ F: include/hw/mips/ F: include/hw/misc/mips_* F: include/hw/timer/mips_gictimer.h F: tests/tcg/mips/ -K: ^Subject:.*(?i)mips MIPS TCG CPUs (nanoMIPS ISA) S: Orphan From 6cee54794dc8d27f5cf4a7fac3d7b8e3146988d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Oct 2021 10:26:23 +0200 Subject: [PATCH 1038/1334] MAINTAINERS: Add entries to cover MIPS CPS / GIC hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIPS CPS and GIC models are unrelated to the TCG frontend. Move them as new sections under the 'Devices' group. Cc: Paul Burton Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211027041416.1237433-3-f4bug@amsat.org> --- MAINTAINERS | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index a156c4bffc..684990b63d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -239,14 +239,8 @@ F: target/mips/ F: configs/devices/mips*/* F: disas/mips.c F: docs/system/cpu-models-mips.rst.inc -F: hw/intc/mips_gic.c F: hw/mips/ -F: hw/misc/mips_* -F: hw/timer/mips_gictimer.c -F: include/hw/intc/mips_gic.h F: include/hw/mips/ -F: include/hw/misc/mips_* -F: include/hw/timer/mips_gictimer.h F: tests/tcg/mips/ MIPS TCG CPUs (nanoMIPS ISA) @@ -2272,6 +2266,20 @@ S: Odd Fixes F: hw/intc/openpic.c F: include/hw/ppc/openpic.h +MIPS CPS +M: Philippe Mathieu-Daudé +S: Odd Fixes +F: hw/misc/mips_* +F: include/hw/misc/mips_* + +MIPS GIC +M: Philippe Mathieu-Daudé +S: Odd Fixes +F: hw/intc/mips_gic.c +F: hw/timer/mips_gictimer.c +F: include/hw/intc/mips_gic.h +F: include/hw/timer/mips_gictimer.h + Subsystems ---------- Overall Audio backends From f44d1d4ed9feb50f4d791b0b6827c1e0dba20555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Oct 2021 10:26:38 +0200 Subject: [PATCH 1039/1334] MAINTAINERS: Split MIPS TCG frontend vs MIPS machines/hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hardware emulated models don't belong to the TCG MAINTAINERS section. Move them to a new 'Overall MIPS Machines' section in the 'MIPS Machines' group. Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211004092515.3819836-4-f4bug@amsat.org> Reviewed-by: Richard Henderson --- MAINTAINERS | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 684990b63d..d58885d9b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -236,11 +236,8 @@ R: Jiaxun Yang R: Aleksandar Rikalo S: Odd Fixes F: target/mips/ -F: configs/devices/mips*/* F: disas/mips.c F: docs/system/cpu-models-mips.rst.inc -F: hw/mips/ -F: include/hw/mips/ F: tests/tcg/mips/ MIPS TCG CPUs (nanoMIPS ISA) @@ -1169,6 +1166,13 @@ F: hw/microblaze/petalogix_ml605_mmu.c MIPS Machines ------------- +Overall MIPS Machines +M: Philippe Mathieu-Daudé +S: Odd Fixes +F: configs/devices/mips*/* +F: hw/mips/ +F: include/hw/mips/ + Jazz M: Hervé Poussineau R: Aleksandar Rikalo From 06df015b69ba2812a9cff6858d32491d6a7b9c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Oct 2021 11:33:42 +0200 Subject: [PATCH 1040/1334] target/mips: Fix MSA MADDV.B opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The result of the 'Vector Multiply and Add' opcode is incorrect with Byte vectors. Probably due to a copy/paste error, commit 7a7a162adde mistakenly used the $wt (target register) instead of $wd (destination register) as first operand. Fix that. Cc: Aleksandar Rikalo Fixes: 7a7a162adde ("target/mips: msa: Split helpers for MADDV.") Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-2-f4bug@amsat.org> --- target/mips/tcg/msa_helper.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index e40c1b7057..d978909527 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -3231,22 +3231,22 @@ void helper_msa_maddv_b(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - pwd->b[0] = msa_maddv_df(DF_BYTE, pwt->b[0], pws->b[0], pwt->b[0]); - pwd->b[1] = msa_maddv_df(DF_BYTE, pwt->b[1], pws->b[1], pwt->b[1]); - pwd->b[2] = msa_maddv_df(DF_BYTE, pwt->b[2], pws->b[2], pwt->b[2]); - pwd->b[3] = msa_maddv_df(DF_BYTE, pwt->b[3], pws->b[3], pwt->b[3]); - pwd->b[4] = msa_maddv_df(DF_BYTE, pwt->b[4], pws->b[4], pwt->b[4]); - pwd->b[5] = msa_maddv_df(DF_BYTE, pwt->b[5], pws->b[5], pwt->b[5]); - pwd->b[6] = msa_maddv_df(DF_BYTE, pwt->b[6], pws->b[6], pwt->b[6]); - pwd->b[7] = msa_maddv_df(DF_BYTE, pwt->b[7], pws->b[7], pwt->b[7]); - pwd->b[8] = msa_maddv_df(DF_BYTE, pwt->b[8], pws->b[8], pwt->b[8]); - pwd->b[9] = msa_maddv_df(DF_BYTE, pwt->b[9], pws->b[9], pwt->b[9]); - pwd->b[10] = msa_maddv_df(DF_BYTE, pwt->b[10], pws->b[10], pwt->b[10]); - pwd->b[11] = msa_maddv_df(DF_BYTE, pwt->b[11], pws->b[11], pwt->b[11]); - pwd->b[12] = msa_maddv_df(DF_BYTE, pwt->b[12], pws->b[12], pwt->b[12]); - pwd->b[13] = msa_maddv_df(DF_BYTE, pwt->b[13], pws->b[13], pwt->b[13]); - pwd->b[14] = msa_maddv_df(DF_BYTE, pwt->b[14], pws->b[14], pwt->b[14]); - pwd->b[15] = msa_maddv_df(DF_BYTE, pwt->b[15], pws->b[15], pwt->b[15]); + pwd->b[0] = msa_maddv_df(DF_BYTE, pwd->b[0], pws->b[0], pwt->b[0]); + pwd->b[1] = msa_maddv_df(DF_BYTE, pwd->b[1], pws->b[1], pwt->b[1]); + pwd->b[2] = msa_maddv_df(DF_BYTE, pwd->b[2], pws->b[2], pwt->b[2]); + pwd->b[3] = msa_maddv_df(DF_BYTE, pwd->b[3], pws->b[3], pwt->b[3]); + pwd->b[4] = msa_maddv_df(DF_BYTE, pwd->b[4], pws->b[4], pwt->b[4]); + pwd->b[5] = msa_maddv_df(DF_BYTE, pwd->b[5], pws->b[5], pwt->b[5]); + pwd->b[6] = msa_maddv_df(DF_BYTE, pwd->b[6], pws->b[6], pwt->b[6]); + pwd->b[7] = msa_maddv_df(DF_BYTE, pwd->b[7], pws->b[7], pwt->b[7]); + pwd->b[8] = msa_maddv_df(DF_BYTE, pwd->b[8], pws->b[8], pwt->b[8]); + pwd->b[9] = msa_maddv_df(DF_BYTE, pwd->b[9], pws->b[9], pwt->b[9]); + pwd->b[10] = msa_maddv_df(DF_BYTE, pwd->b[10], pws->b[10], pwt->b[10]); + pwd->b[11] = msa_maddv_df(DF_BYTE, pwd->b[11], pws->b[11], pwt->b[11]); + pwd->b[12] = msa_maddv_df(DF_BYTE, pwd->b[12], pws->b[12], pwt->b[12]); + pwd->b[13] = msa_maddv_df(DF_BYTE, pwd->b[13], pws->b[13], pwt->b[13]); + pwd->b[14] = msa_maddv_df(DF_BYTE, pwd->b[14], pws->b[14], pwt->b[14]); + pwd->b[15] = msa_maddv_df(DF_BYTE, pwd->b[15], pws->b[15], pwt->b[15]); } void helper_msa_maddv_h(CPUMIPSState *env, From 36b39a69b2e7649d317a08dd81da39a7c9bc14f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Oct 2021 11:33:49 +0200 Subject: [PATCH 1041/1334] target/mips: Fix MSA MSUBV.B opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The result of the 'Vector Multiply and Subtract' opcode is incorrect with Byte vectors. Probably due to a copy/paste error, commit 5f148a02327 mistakenly used the $wt (target register) instead of $wd (destination register) as first operand. Fix that. Cc: Aleksandar Rikalo Fixes: 5f148a02327 ("target/mips: msa: Split helpers for MSUBV.") Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-3-f4bug@amsat.org> --- target/mips/tcg/msa_helper.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index d978909527..5667b1f0a1 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -3303,22 +3303,22 @@ void helper_msa_msubv_b(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); - pwd->b[0] = msa_msubv_df(DF_BYTE, pwt->b[0], pws->b[0], pwt->b[0]); - pwd->b[1] = msa_msubv_df(DF_BYTE, pwt->b[1], pws->b[1], pwt->b[1]); - pwd->b[2] = msa_msubv_df(DF_BYTE, pwt->b[2], pws->b[2], pwt->b[2]); - pwd->b[3] = msa_msubv_df(DF_BYTE, pwt->b[3], pws->b[3], pwt->b[3]); - pwd->b[4] = msa_msubv_df(DF_BYTE, pwt->b[4], pws->b[4], pwt->b[4]); - pwd->b[5] = msa_msubv_df(DF_BYTE, pwt->b[5], pws->b[5], pwt->b[5]); - pwd->b[6] = msa_msubv_df(DF_BYTE, pwt->b[6], pws->b[6], pwt->b[6]); - pwd->b[7] = msa_msubv_df(DF_BYTE, pwt->b[7], pws->b[7], pwt->b[7]); - pwd->b[8] = msa_msubv_df(DF_BYTE, pwt->b[8], pws->b[8], pwt->b[8]); - pwd->b[9] = msa_msubv_df(DF_BYTE, pwt->b[9], pws->b[9], pwt->b[9]); - pwd->b[10] = msa_msubv_df(DF_BYTE, pwt->b[10], pws->b[10], pwt->b[10]); - pwd->b[11] = msa_msubv_df(DF_BYTE, pwt->b[11], pws->b[11], pwt->b[11]); - pwd->b[12] = msa_msubv_df(DF_BYTE, pwt->b[12], pws->b[12], pwt->b[12]); - pwd->b[13] = msa_msubv_df(DF_BYTE, pwt->b[13], pws->b[13], pwt->b[13]); - pwd->b[14] = msa_msubv_df(DF_BYTE, pwt->b[14], pws->b[14], pwt->b[14]); - pwd->b[15] = msa_msubv_df(DF_BYTE, pwt->b[15], pws->b[15], pwt->b[15]); + pwd->b[0] = msa_msubv_df(DF_BYTE, pwd->b[0], pws->b[0], pwt->b[0]); + pwd->b[1] = msa_msubv_df(DF_BYTE, pwd->b[1], pws->b[1], pwt->b[1]); + pwd->b[2] = msa_msubv_df(DF_BYTE, pwd->b[2], pws->b[2], pwt->b[2]); + pwd->b[3] = msa_msubv_df(DF_BYTE, pwd->b[3], pws->b[3], pwt->b[3]); + pwd->b[4] = msa_msubv_df(DF_BYTE, pwd->b[4], pws->b[4], pwt->b[4]); + pwd->b[5] = msa_msubv_df(DF_BYTE, pwd->b[5], pws->b[5], pwt->b[5]); + pwd->b[6] = msa_msubv_df(DF_BYTE, pwd->b[6], pws->b[6], pwt->b[6]); + pwd->b[7] = msa_msubv_df(DF_BYTE, pwd->b[7], pws->b[7], pwt->b[7]); + pwd->b[8] = msa_msubv_df(DF_BYTE, pwd->b[8], pws->b[8], pwt->b[8]); + pwd->b[9] = msa_msubv_df(DF_BYTE, pwd->b[9], pws->b[9], pwt->b[9]); + pwd->b[10] = msa_msubv_df(DF_BYTE, pwd->b[10], pws->b[10], pwt->b[10]); + pwd->b[11] = msa_msubv_df(DF_BYTE, pwd->b[11], pws->b[11], pwt->b[11]); + pwd->b[12] = msa_msubv_df(DF_BYTE, pwd->b[12], pws->b[12], pwt->b[12]); + pwd->b[13] = msa_msubv_df(DF_BYTE, pwd->b[13], pws->b[13], pwt->b[13]); + pwd->b[14] = msa_msubv_df(DF_BYTE, pwd->b[14], pws->b[14], pwt->b[14]); + pwd->b[15] = msa_msubv_df(DF_BYTE, pwd->b[15], pws->b[15], pwt->b[15]); } void helper_msa_msubv_h(CPUMIPSState *env, From bbc213b37c1366cf64701d37a21b709c97714a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 23 Oct 2021 09:54:20 +0200 Subject: [PATCH 1042/1334] target/mips: Adjust style in msa_translate_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the first 'off' variable assignment is unused, it helps to better understand the code logic. Move the assignation where it would have been used so it is easier to compare the MSA registers based on FPU ones versus the MSA specific registers. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211023214803.522078-34-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 3ef912da6b..3aa15e147c 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -280,13 +280,15 @@ void msa_translate_init(void) int i; for (i = 0; i < 32; i++) { - int off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[0]); + int off; /* * The MSA vector registers are mapped on the * scalar floating-point unit (FPU) registers. */ + off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[0]); msa_wr_d[i * 2] = fpu_f64[i]; + off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[1]); msa_wr_d[i * 2 + 1] = tcg_global_mem_new_i64(cpu_env, off, msaregnames[i * 2 + 1]); From 40f75c02d4c796cb54826b65edd2e2530e5129f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 24 Oct 2021 20:10:08 +0200 Subject: [PATCH 1043/1334] target/mips: Use dup_const() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dup_const() helper makes the code easier to follow, use it. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211028210843.2120802-5-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 3aa15e147c..b135c58fd4 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -315,28 +315,11 @@ static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, { /* generates tcg ops to check if any element is 0 */ /* Note this function only works with MSA_WRLEN = 128 */ - uint64_t eval_zero_or_big = 0; - uint64_t eval_big = 0; + uint64_t eval_zero_or_big = dup_const(df, 1); + uint64_t eval_big = eval_zero_or_big << ((8 << df) - 1); TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); - switch (df) { - case DF_BYTE: - eval_zero_or_big = 0x0101010101010101ULL; - eval_big = 0x8080808080808080ULL; - break; - case DF_HALF: - eval_zero_or_big = 0x0001000100010001ULL; - eval_big = 0x8000800080008000ULL; - break; - case DF_WORD: - eval_zero_or_big = 0x0000000100000001ULL; - eval_big = 0x8000000080000000ULL; - break; - case DF_DOUBLE: - eval_zero_or_big = 0x0000000000000001ULL; - eval_big = 0x8000000000000000ULL; - break; - } + tcg_gen_subi_i64(t0, msa_wr_d[wt << 1], eval_zero_or_big); tcg_gen_andc_i64(t0, t0, msa_wr_d[wt << 1]); tcg_gen_andi_i64(t0, t0, eval_big); From 340ee8b3f1872c7f8969a5eb48fc3b5a9284e27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 23 Oct 2021 09:57:16 +0200 Subject: [PATCH 1044/1334] target/mips: Have check_msa_access() return a boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have check_msa_access() return a boolean value so we can return early if MSA is not enabled (the instruction got decoded properly, but we raised an exception). Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-6-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index b135c58fd4..e0ccd8c1cb 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -295,19 +295,24 @@ void msa_translate_init(void) } } -static inline int check_msa_access(DisasContext *ctx) +/* + * Check if MSA is enabled. + * This function is always called with MSA available. + * If MSA is disabled, raise an exception. + */ +static inline bool check_msa_enabled(DisasContext *ctx) { if (unlikely((ctx->hflags & MIPS_HFLAG_FPU) && !(ctx->hflags & MIPS_HFLAG_F64))) { gen_reserved_instruction(ctx); - return 0; + return false; } if (unlikely(!(ctx->hflags & MIPS_HFLAG_MSA))) { generate_exception_end(ctx, EXCP_MSADIS); - return 0; + return false; } - return 1; + return true; } static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, @@ -339,7 +344,9 @@ static bool gen_msa_BxZ_V(DisasContext *ctx, int wt, int s16, TCGCond cond) { TCGv_i64 t0; - check_msa_access(ctx); + if (!check_msa_enabled(ctx)) { + return true; + } if (ctx->hflags & MIPS_HFLAG_BMASK) { gen_reserved_instruction(ctx); @@ -371,7 +378,9 @@ static bool trans_BNZ_V(DisasContext *ctx, arg_msa_bz *a) static bool gen_msa_BxZ(DisasContext *ctx, int df, int wt, int s16, bool if_not) { - check_msa_access(ctx); + if (!check_msa_enabled(ctx)) { + return true; + } if (ctx->hflags & MIPS_HFLAG_BMASK) { gen_reserved_instruction(ctx); @@ -2143,7 +2152,9 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) { uint32_t opcode = ctx->opcode; - check_msa_access(ctx); + if (!check_msa_enabled(ctx)) { + return true; + } switch (MASK_MSA_MINOR(opcode)) { case OPC_MSA_I8_00: From 7e9db46d645dca27f28ec28e0fc479778e410d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 09:38:11 +0200 Subject: [PATCH 1045/1334] target/mips: Use enum definitions from CPUMIPSMSADataFormat enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace magic DataFormat value by the corresponding enum from CPUMIPSMSADataFormat. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-7-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index e0ccd8c1cb..56a0148fec 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -1791,10 +1791,10 @@ static void gen_msa_3rf(DisasContext *ctx) case OPC_MULR_Q_df: case OPC_MADDR_Q_df: case OPC_MSUBR_Q_df: - tdf = tcg_constant_i32(df + 1); + tdf = tcg_constant_i32(DF_HALF + df); break; default: - tdf = tcg_constant_i32(df + 2); + tdf = tcg_constant_i32(DF_WORD + df); break; } @@ -2023,7 +2023,7 @@ static void gen_msa_2rf(DisasContext *ctx) TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tws = tcg_const_i32(ws); /* adjust df value for floating-point instruction */ - TCGv_i32 tdf = tcg_constant_i32(df + 2); + TCGv_i32 tdf = tcg_constant_i32(DF_WORD + df); switch (MASK_MSA_2RF(ctx->opcode)) { case OPC_FCLASS_df: From d61566cf78d97952ecf6a00a64b168c61fadd4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 08:18:25 +0200 Subject: [PATCH 1046/1334] target/mips: Rename sa16 -> sa, bz_df -> bz -> bz_v MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This 'shift amount' format is not always 16-bit, so name it generically as 'sa'. This will help to unify the various arg_msa decodetree generated structures. Rename the @bz format -> @bz_v (specific @bz with df=3) and @bz_df -> @bz (generic @bz). Since we modify &msa_bz, re-align its arguments, so the other structures added in the following commits stay visually aligned. Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-8-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 15 +++++++-------- target/mips/tcg/msa_translate.c | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 74d99f6862..56419a24eb 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -13,19 +13,18 @@ &r rs rt rd sa -&msa_bz df wt s16 +&msa_bz df wt sa @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r -@bz ...... ... .. wt:5 s16:16 &msa_bz df=3 -@bz_df ...... ... df:2 wt:5 s16:16 &msa_bz +@bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 +@bz ...... ... df:2 wt:5 sa:16 &msa_bz LSA 000000 ..... ..... ..... 000 .. 000101 @lsa DLSA 000000 ..... ..... ..... 000 .. 010101 @lsa -BZ_V 010001 01011 ..... ................ @bz -BNZ_V 010001 01111 ..... ................ @bz - -BZ_x 010001 110 .. ..... ................ @bz_df -BNZ_x 010001 111 .. ..... ................ @bz_df +BZ_V 010001 01011 ..... ................ @bz_v +BNZ_V 010001 01111 ..... ................ @bz_v +BZ 010001 110 .. ..... ................ @bz +BNZ 010001 111 .. ..... ................ @bz MSA 011110 -------------------------- diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 56a0148fec..8311730f0a 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -340,7 +340,7 @@ static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, tcg_temp_free_i64(t1); } -static bool gen_msa_BxZ_V(DisasContext *ctx, int wt, int s16, TCGCond cond) +static bool gen_msa_BxZ_V(DisasContext *ctx, int wt, int sa, TCGCond cond) { TCGv_i64 t0; @@ -358,7 +358,7 @@ static bool gen_msa_BxZ_V(DisasContext *ctx, int wt, int s16, TCGCond cond) tcg_gen_trunc_i64_tl(bcond, t0); tcg_temp_free_i64(t0); - ctx->btarget = ctx->base.pc_next + (s16 << 2) + 4; + ctx->btarget = ctx->base.pc_next + (sa << 2) + 4; ctx->hflags |= MIPS_HFLAG_BC; ctx->hflags |= MIPS_HFLAG_BDS32; @@ -368,15 +368,15 @@ static bool gen_msa_BxZ_V(DisasContext *ctx, int wt, int s16, TCGCond cond) static bool trans_BZ_V(DisasContext *ctx, arg_msa_bz *a) { - return gen_msa_BxZ_V(ctx, a->wt, a->s16, TCG_COND_EQ); + return gen_msa_BxZ_V(ctx, a->wt, a->sa, TCG_COND_EQ); } static bool trans_BNZ_V(DisasContext *ctx, arg_msa_bz *a) { - return gen_msa_BxZ_V(ctx, a->wt, a->s16, TCG_COND_NE); + return gen_msa_BxZ_V(ctx, a->wt, a->sa, TCG_COND_NE); } -static bool gen_msa_BxZ(DisasContext *ctx, int df, int wt, int s16, bool if_not) +static bool gen_msa_BxZ(DisasContext *ctx, int df, int wt, int sa, bool if_not) { if (!check_msa_enabled(ctx)) { return true; @@ -389,21 +389,21 @@ static bool gen_msa_BxZ(DisasContext *ctx, int df, int wt, int s16, bool if_not) gen_check_zero_element(bcond, df, wt, if_not ? TCG_COND_EQ : TCG_COND_NE); - ctx->btarget = ctx->base.pc_next + (s16 << 2) + 4; + ctx->btarget = ctx->base.pc_next + (sa << 2) + 4; ctx->hflags |= MIPS_HFLAG_BC; ctx->hflags |= MIPS_HFLAG_BDS32; return true; } -static bool trans_BZ_x(DisasContext *ctx, arg_msa_bz *a) +static bool trans_BZ(DisasContext *ctx, arg_msa_bz *a) { - return gen_msa_BxZ(ctx, a->df, a->wt, a->s16, false); + return gen_msa_BxZ(ctx, a->df, a->wt, a->sa, false); } -static bool trans_BNZ_x(DisasContext *ctx, arg_msa_bz *a) +static bool trans_BNZ(DisasContext *ctx, arg_msa_bz *a) { - return gen_msa_BxZ(ctx, a->df, a->wt, a->s16, true); + return gen_msa_BxZ(ctx, a->df, a->wt, a->sa, true); } static void gen_msa_i8(DisasContext *ctx) From 75094c334e4c2f6d4646274cd73006d8931a31de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 08:22:31 +0200 Subject: [PATCH 1047/1334] target/mips: Convert MSA LDI opcode to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the LDI opcode (Immediate Load) to decodetree. Since it overlaps with the generic MSA handler, use a decodetree overlap group. Since the 'data format' field is a constant value, use tcg_constant_i32() instead of a TCG temporary. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-9-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 8 +++++++- target/mips/tcg/msa_translate.c | 22 ++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 56419a24eb..bdfe5a24cb 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -14,10 +14,12 @@ &r rs rt rd sa &msa_bz df wt sa +&msa_ldi df wd sa @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @bz ...... ... df:2 wt:5 sa:16 &msa_bz +@ldi ...... ... df:2 sa:s10 wd:5 ...... &msa_ldi LSA 000000 ..... ..... ..... 000 .. 000101 @lsa DLSA 000000 ..... ..... ..... 000 .. 010101 @lsa @@ -27,4 +29,8 @@ BNZ_V 010001 01111 ..... ................ @bz_v BZ 010001 110 .. ..... ................ @bz BNZ 010001 111 .. ..... ................ @bz -MSA 011110 -------------------------- +{ + LDI 011110 110 .. .......... ..... 000111 @ldi + + MSA 011110 -------------------------- +} diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 8311730f0a..94c69a668d 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -70,7 +70,6 @@ enum { OPC_CLEI_S_df = (0x4 << 23) | OPC_MSA_I5_07, OPC_MINI_U_df = (0x5 << 23) | OPC_MSA_I5_06, OPC_CLEI_U_df = (0x5 << 23) | OPC_MSA_I5_07, - OPC_LDI_df = (0x6 << 23) | OPC_MSA_I5_07, /* I8 instruction */ OPC_ANDI_B = (0x0 << 24) | OPC_MSA_I8_00, @@ -515,13 +514,6 @@ static void gen_msa_i5(DisasContext *ctx) case OPC_CLEI_U_df: gen_helper_msa_clei_u_df(cpu_env, tdf, twd, tws, timm); break; - case OPC_LDI_df: - { - int32_t s10 = sextract32(ctx->opcode, 11, 10); - tcg_gen_movi_i32(timm, s10); - gen_helper_msa_ldi_df(cpu_env, tdf, twd, timm); - } - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); @@ -534,6 +526,20 @@ static void gen_msa_i5(DisasContext *ctx) tcg_temp_free_i32(timm); } +static bool trans_LDI(DisasContext *ctx, arg_msa_ldi *a) +{ + if (!check_msa_enabled(ctx)) { + return true; + } + + gen_helper_msa_ldi_df(cpu_env, + tcg_constant_i32(a->df), + tcg_constant_i32(a->wd), + tcg_constant_i32(a->sa)); + + return true; +} + static void gen_msa_bit(DisasContext *ctx) { #define MASK_MSA_BIT(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) From b8e74816ec8ee7efdce6aa290cd18c937b317133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 08:27:58 +0200 Subject: [PATCH 1048/1334] target/mips: Convert MSA I5 instruction format to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert instructions with a 5-bit immediate value to decodetree. Since the 'data format' field is a constant value, use tcg_constant_i32() instead of a TCG temporary. Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-10-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 16 +++++ target/mips/tcg/msa_translate.c | 102 ++++++++------------------------ 2 files changed, 41 insertions(+), 77 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index bdfe5a24cb..cd2b618684 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -15,10 +15,13 @@ &msa_bz df wt sa &msa_ldi df wd sa +&msa_i df wd ws sa @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @bz ...... ... df:2 wt:5 sa:16 &msa_bz +@u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i +@s5 ...... ... df:2 sa:s5 ws:5 wd:5 ...... &msa_i @ldi ...... ... df:2 sa:s10 wd:5 ...... &msa_ldi LSA 000000 ..... ..... ..... 000 .. 000101 @lsa @@ -30,6 +33,19 @@ BZ 010001 110 .. ..... ................ @bz BNZ 010001 111 .. ..... ................ @bz { + ADDVI 011110 000 .. ..... ..... ..... 000110 @u5 + SUBVI 011110 001 .. ..... ..... ..... 000110 @u5 + MAXI_S 011110 010 .. ..... ..... ..... 000110 @s5 + MAXI_U 011110 011 .. ..... ..... ..... 000110 @u5 + MINI_S 011110 100 .. ..... ..... ..... 000110 @s5 + MINI_U 011110 101 .. ..... ..... ..... 000110 @u5 + + CEQI 011110 000 .. ..... ..... ..... 000111 @s5 + CLTI_S 011110 010 .. ..... ..... ..... 000111 @s5 + CLTI_U 011110 011 .. ..... ..... ..... 000111 @u5 + CLEI_S 011110 100 .. ..... ..... ..... 000111 @s5 + CLEI_U 011110 101 .. ..... ..... ..... 000111 @u5 + LDI 011110 110 .. .......... ..... 000111 @ldi MSA 011110 -------------------------- diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 94c69a668d..c5211c4e05 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -27,8 +27,6 @@ enum { OPC_MSA_I8_00 = 0x00 | OPC_MSA, OPC_MSA_I8_01 = 0x01 | OPC_MSA, OPC_MSA_I8_02 = 0x02 | OPC_MSA, - OPC_MSA_I5_06 = 0x06 | OPC_MSA, - OPC_MSA_I5_07 = 0x07 | OPC_MSA, OPC_MSA_BIT_09 = 0x09 | OPC_MSA, OPC_MSA_BIT_0A = 0x0A | OPC_MSA, OPC_MSA_3R_0D = 0x0D | OPC_MSA, @@ -58,19 +56,6 @@ enum { }; enum { - /* I5 instruction df(bits 22..21) = _b, _h, _w, _d */ - OPC_ADDVI_df = (0x0 << 23) | OPC_MSA_I5_06, - OPC_CEQI_df = (0x0 << 23) | OPC_MSA_I5_07, - OPC_SUBVI_df = (0x1 << 23) | OPC_MSA_I5_06, - OPC_MAXI_S_df = (0x2 << 23) | OPC_MSA_I5_06, - OPC_CLTI_S_df = (0x2 << 23) | OPC_MSA_I5_07, - OPC_MAXI_U_df = (0x3 << 23) | OPC_MSA_I5_06, - OPC_CLTI_U_df = (0x3 << 23) | OPC_MSA_I5_07, - OPC_MINI_S_df = (0x4 << 23) | OPC_MSA_I5_06, - OPC_CLEI_S_df = (0x4 << 23) | OPC_MSA_I5_07, - OPC_MINI_U_df = (0x5 << 23) | OPC_MSA_I5_06, - OPC_CLEI_U_df = (0x5 << 23) | OPC_MSA_I5_07, - /* I8 instruction */ OPC_ANDI_B = (0x0 << 24) | OPC_MSA_I8_00, OPC_BMNZI_B = (0x0 << 24) | OPC_MSA_I8_01, @@ -314,6 +299,8 @@ static inline bool check_msa_enabled(DisasContext *ctx) return true; } +typedef void gen_helper_piiii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); + static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, TCGCond cond) { @@ -463,69 +450,34 @@ static void gen_msa_i8(DisasContext *ctx) tcg_temp_free_i32(ti8); } -static void gen_msa_i5(DisasContext *ctx) +static bool trans_msa_i5(DisasContext *ctx, arg_msa_i *a, + gen_helper_piiii *gen_msa_i5) { -#define MASK_MSA_I5(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) - int8_t s5 = (int8_t) sextract32(ctx->opcode, 16, 5); - uint8_t u5 = extract32(ctx->opcode, 16, 5); - - TCGv_i32 tdf = tcg_const_i32(extract32(ctx->opcode, 21, 2)); - TCGv_i32 twd = tcg_const_i32(extract32(ctx->opcode, 11, 5)); - TCGv_i32 tws = tcg_const_i32(extract32(ctx->opcode, 6, 5)); - TCGv_i32 timm = tcg_temp_new_i32(); - tcg_gen_movi_i32(timm, u5); - - switch (MASK_MSA_I5(ctx->opcode)) { - case OPC_ADDVI_df: - gen_helper_msa_addvi_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_SUBVI_df: - gen_helper_msa_subvi_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_MAXI_S_df: - tcg_gen_movi_i32(timm, s5); - gen_helper_msa_maxi_s_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_MAXI_U_df: - gen_helper_msa_maxi_u_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_MINI_S_df: - tcg_gen_movi_i32(timm, s5); - gen_helper_msa_mini_s_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_MINI_U_df: - gen_helper_msa_mini_u_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_CEQI_df: - tcg_gen_movi_i32(timm, s5); - gen_helper_msa_ceqi_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_CLTI_S_df: - tcg_gen_movi_i32(timm, s5); - gen_helper_msa_clti_s_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_CLTI_U_df: - gen_helper_msa_clti_u_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_CLEI_S_df: - tcg_gen_movi_i32(timm, s5); - gen_helper_msa_clei_s_df(cpu_env, tdf, twd, tws, timm); - break; - case OPC_CLEI_U_df: - gen_helper_msa_clei_u_df(cpu_env, tdf, twd, tws, timm); - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; + if (!check_msa_enabled(ctx)) { + return true; } - tcg_temp_free_i32(tdf); - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); - tcg_temp_free_i32(timm); + gen_msa_i5(cpu_env, + tcg_constant_i32(a->df), + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws), + tcg_constant_i32(a->sa)); + + return true; } +TRANS(ADDVI, trans_msa_i5, gen_helper_msa_addvi_df); +TRANS(SUBVI, trans_msa_i5, gen_helper_msa_subvi_df); +TRANS(MAXI_S, trans_msa_i5, gen_helper_msa_maxi_s_df); +TRANS(MAXI_U, trans_msa_i5, gen_helper_msa_maxi_u_df); +TRANS(MINI_S, trans_msa_i5, gen_helper_msa_mini_s_df); +TRANS(MINI_U, trans_msa_i5, gen_helper_msa_mini_u_df); +TRANS(CLTI_S, trans_msa_i5, gen_helper_msa_clti_s_df); +TRANS(CLTI_U, trans_msa_i5, gen_helper_msa_clti_u_df); +TRANS(CLEI_S, trans_msa_i5, gen_helper_msa_clei_s_df); +TRANS(CLEI_U, trans_msa_i5, gen_helper_msa_clei_u_df); +TRANS(CEQI, trans_msa_i5, gen_helper_msa_ceqi_df); + static bool trans_LDI(DisasContext *ctx, arg_msa_ldi *a) { if (!check_msa_enabled(ctx)) { @@ -2168,10 +2120,6 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) case OPC_MSA_I8_02: gen_msa_i8(ctx); break; - case OPC_MSA_I5_06: - case OPC_MSA_I5_07: - gen_msa_i5(ctx); - break; case OPC_MSA_BIT_09: case OPC_MSA_BIT_0A: gen_msa_bit(ctx); From 4701d23aef1d096da20b46ce817e30f81bd01b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 08:47:29 +0200 Subject: [PATCH 1049/1334] target/mips: Convert MSA BIT instruction format to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert instructions with an immediate bit index and data format df/m to decodetree. Since the 'data format' field is a constant value, use tcg_constant_i32() instead of a TCG temporary. Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211028210843.2120802-11-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 19 ++++ target/mips/tcg/msa_translate.c | 179 +++++++++++++++----------------- 2 files changed, 101 insertions(+), 97 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index cd2b618684..3d6c6faf68 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -16,6 +16,10 @@ &msa_bz df wt sa &msa_ldi df wd sa &msa_i df wd ws sa +&msa_bit df wd ws m + +%bit_df 16:7 !function=bit_df +%bit_m 16:7 !function=bit_m @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @@ -23,6 +27,7 @@ @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @s5 ...... ... df:2 sa:s5 ws:5 wd:5 ...... &msa_i @ldi ...... ... df:2 sa:s10 wd:5 ...... &msa_ldi +@bit ...... ... ....... ws:5 wd:5 ...... &msa_bit df=%bit_df m=%bit_m LSA 000000 ..... ..... ..... 000 .. 000101 @lsa DLSA 000000 ..... ..... ..... 000 .. 010101 @lsa @@ -48,5 +53,19 @@ BNZ 010001 111 .. ..... ................ @bz LDI 011110 110 .. .......... ..... 000111 @ldi + SLLI 011110 000 ....... ..... ..... 001001 @bit + SRAI 011110 001 ....... ..... ..... 001001 @bit + SRLI 011110 010 ....... ..... ..... 001001 @bit + BCLRI 011110 011 ....... ..... ..... 001001 @bit + BSETI 011110 100 ....... ..... ..... 001001 @bit + BNEGI 011110 101 ....... ..... ..... 001001 @bit + BINSLI 011110 110 ....... ..... ..... 001001 @bit + BINSRI 011110 111 ....... ..... ..... 001001 @bit + + SAT_S 011110 000 ....... ..... ..... 001010 @bit + SAT_U 011110 001 ....... ..... ..... 001010 @bit + SRARI 011110 010 ....... ..... ..... 001010 @bit + SRLRI 011110 011 ....... ..... ..... 001010 @bit + MSA 011110 -------------------------- } diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index c5211c4e05..9c1a24eb25 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -17,6 +17,9 @@ #include "fpu_helper.h" #include "internal.h" +static int bit_m(DisasContext *ctx, int x); +static int bit_df(DisasContext *ctx, int x); + /* Include the auto-generated decoder. */ #include "decode-msa.c.inc" @@ -27,8 +30,6 @@ enum { OPC_MSA_I8_00 = 0x00 | OPC_MSA, OPC_MSA_I8_01 = 0x01 | OPC_MSA, OPC_MSA_I8_02 = 0x02 | OPC_MSA, - OPC_MSA_BIT_09 = 0x09 | OPC_MSA, - OPC_MSA_BIT_0A = 0x0A | OPC_MSA, OPC_MSA_3R_0D = 0x0D | OPC_MSA, OPC_MSA_3R_0E = 0x0E | OPC_MSA, OPC_MSA_3R_0F = 0x0F | OPC_MSA, @@ -222,20 +223,6 @@ enum { OPC_MSUBR_Q_df = (0xE << 22) | OPC_MSA_3RF_1C, OPC_FSULE_df = (0xF << 22) | OPC_MSA_3RF_1A, OPC_FMAX_A_df = (0xF << 22) | OPC_MSA_3RF_1B, - - /* BIT instruction df(bits 22..16) = _B _H _W _D */ - OPC_SLLI_df = (0x0 << 23) | OPC_MSA_BIT_09, - OPC_SAT_S_df = (0x0 << 23) | OPC_MSA_BIT_0A, - OPC_SRAI_df = (0x1 << 23) | OPC_MSA_BIT_09, - OPC_SAT_U_df = (0x1 << 23) | OPC_MSA_BIT_0A, - OPC_SRLI_df = (0x2 << 23) | OPC_MSA_BIT_09, - OPC_SRARI_df = (0x2 << 23) | OPC_MSA_BIT_0A, - OPC_BCLRI_df = (0x3 << 23) | OPC_MSA_BIT_09, - OPC_SRLRI_df = (0x3 << 23) | OPC_MSA_BIT_0A, - OPC_BSETI_df = (0x4 << 23) | OPC_MSA_BIT_09, - OPC_BNEGI_df = (0x5 << 23) | OPC_MSA_BIT_09, - OPC_BINSLI_df = (0x6 << 23) | OPC_MSA_BIT_09, - OPC_BINSRI_df = (0x7 << 23) | OPC_MSA_BIT_09, }; static const char msaregnames[][6] = { @@ -257,6 +244,59 @@ static const char msaregnames[][6] = { "w30.d0", "w30.d1", "w31.d0", "w31.d1", }; +/* Encoding of Operation Field (must be indexed by CPUMIPSMSADataFormat) */ +struct dfe { + int start; + int length; + uint32_t mask; +}; + +/* + * Extract immediate from df/{m,n} format (used by ELM & BIT instructions). + * Returns the immediate value, or -1 if the format does not match. + */ +static int df_extract_val(DisasContext *ctx, int x, const struct dfe *s) +{ + for (unsigned i = 0; i < 4; i++) { + if (extract32(x, s->start, s->length) == s->mask) { + return extract32(x, 0, s->start); + } + } + return -1; +} + +/* + * Extract DataField from df/{m,n} format (used by ELM & BIT instructions). + * Returns the DataField, or -1 if the format does not match. + */ +static int df_extract_df(DisasContext *ctx, int x, const struct dfe *s) +{ + for (unsigned i = 0; i < 4; i++) { + if (extract32(x, s->start, s->length) == s->mask) { + return i; + } + } + return -1; +} + +static const struct dfe df_bit[] = { + /* Table 3.28 BIT Instruction Format */ + [DF_BYTE] = {3, 4, 0b1110}, + [DF_HALF] = {4, 3, 0b110}, + [DF_WORD] = {5, 2, 0b10}, + [DF_DOUBLE] = {6, 1, 0b0} +}; + +static int bit_m(DisasContext *ctx, int x) +{ + return df_extract_val(ctx, x, df_bit); +} + +static int bit_df(DisasContext *ctx, int x) +{ + return df_extract_df(ctx, x, df_bit); +} + static TCGv_i64 msa_wr_d[64]; void msa_translate_init(void) @@ -492,90 +532,39 @@ static bool trans_LDI(DisasContext *ctx, arg_msa_ldi *a) return true; } -static void gen_msa_bit(DisasContext *ctx) +static bool trans_msa_bit(DisasContext *ctx, arg_msa_bit *a, + gen_helper_piiii *gen_msa_bit) { -#define MASK_MSA_BIT(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) - uint8_t dfm = (ctx->opcode >> 16) & 0x7f; - uint32_t df = 0, m = 0; - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; - - TCGv_i32 tdf; - TCGv_i32 tm; - TCGv_i32 twd; - TCGv_i32 tws; - - if ((dfm & 0x40) == 0x00) { - m = dfm & 0x3f; - df = DF_DOUBLE; - } else if ((dfm & 0x60) == 0x40) { - m = dfm & 0x1f; - df = DF_WORD; - } else if ((dfm & 0x70) == 0x60) { - m = dfm & 0x0f; - df = DF_HALF; - } else if ((dfm & 0x78) == 0x70) { - m = dfm & 0x7; - df = DF_BYTE; - } else { - gen_reserved_instruction(ctx); - return; + if (a->df < 0) { + return false; } - tdf = tcg_const_i32(df); - tm = tcg_const_i32(m); - twd = tcg_const_i32(wd); - tws = tcg_const_i32(ws); - - switch (MASK_MSA_BIT(ctx->opcode)) { - case OPC_SLLI_df: - gen_helper_msa_slli_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_SRAI_df: - gen_helper_msa_srai_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_SRLI_df: - gen_helper_msa_srli_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_BCLRI_df: - gen_helper_msa_bclri_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_BSETI_df: - gen_helper_msa_bseti_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_BNEGI_df: - gen_helper_msa_bnegi_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_BINSLI_df: - gen_helper_msa_binsli_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_BINSRI_df: - gen_helper_msa_binsri_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_SAT_S_df: - gen_helper_msa_sat_s_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_SAT_U_df: - gen_helper_msa_sat_u_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_SRARI_df: - gen_helper_msa_srari_df(cpu_env, tdf, twd, tws, tm); - break; - case OPC_SRLRI_df: - gen_helper_msa_srlri_df(cpu_env, tdf, twd, tws, tm); - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; + if (!check_msa_enabled(ctx)) { + return true; } - tcg_temp_free_i32(tdf); - tcg_temp_free_i32(tm); - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); + gen_msa_bit(cpu_env, + tcg_constant_i32(a->df), + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws), + tcg_constant_i32(a->m)); + + return true; } +TRANS(SLLI, trans_msa_bit, gen_helper_msa_slli_df); +TRANS(SRAI, trans_msa_bit, gen_helper_msa_srai_df); +TRANS(SRLI, trans_msa_bit, gen_helper_msa_srli_df); +TRANS(BCLRI, trans_msa_bit, gen_helper_msa_bclri_df); +TRANS(BSETI, trans_msa_bit, gen_helper_msa_bseti_df); +TRANS(BNEGI, trans_msa_bit, gen_helper_msa_bnegi_df); +TRANS(BINSLI, trans_msa_bit, gen_helper_msa_binsli_df); +TRANS(BINSRI, trans_msa_bit, gen_helper_msa_binsri_df); +TRANS(SAT_S, trans_msa_bit, gen_helper_msa_sat_u_df); +TRANS(SAT_U, trans_msa_bit, gen_helper_msa_sat_u_df); +TRANS(SRARI, trans_msa_bit, gen_helper_msa_srari_df); +TRANS(SRLRI, trans_msa_bit, gen_helper_msa_srlri_df); + static void gen_msa_3r(DisasContext *ctx) { #define MASK_MSA_3R(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) @@ -2120,10 +2109,6 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) case OPC_MSA_I8_02: gen_msa_i8(ctx); break; - case OPC_MSA_BIT_09: - case OPC_MSA_BIT_0A: - gen_msa_bit(ctx); - break; case OPC_MSA_3R_0D: case OPC_MSA_3R_0E: case OPC_MSA_3R_0F: From a9e17958330a971c379ed99f349158b269b4e4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 09:54:17 +0200 Subject: [PATCH 1050/1334] target/mips: Convert MSA SHF opcode to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the SHF opcode (Immediate Set Shuffle Elements) to decodetree. Since the 'data format' field is a constant value, use tcg_constant_i32() instead of a TCG temporary. Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-12-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 3 +++ target/mips/tcg/msa_translate.c | 36 +++++++++++++++++---------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 3d6c6faf68..8e887f54ad 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -26,6 +26,7 @@ @bz ...... ... df:2 wt:5 sa:16 &msa_bz @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @s5 ...... ... df:2 sa:s5 ws:5 wd:5 ...... &msa_i +@i8_df ...... df:2 sa:s8 ws:5 wd:5 ...... &msa_i @ldi ...... ... df:2 sa:s10 wd:5 ...... &msa_ldi @bit ...... ... ....... ws:5 wd:5 ...... &msa_bit df=%bit_df m=%bit_m @@ -38,6 +39,8 @@ BZ 010001 110 .. ..... ................ @bz BNZ 010001 111 .. ..... ................ @bz { + SHF 011110 .. ........ ..... ..... 000010 @i8_df + ADDVI 011110 000 .. ..... ..... ..... 000110 @u5 SUBVI 011110 001 .. ..... ..... ..... 000110 @u5 MAXI_S 011110 010 .. ..... ..... ..... 000110 @s5 diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 9c1a24eb25..1b1d88ac64 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -60,13 +60,10 @@ enum { /* I8 instruction */ OPC_ANDI_B = (0x0 << 24) | OPC_MSA_I8_00, OPC_BMNZI_B = (0x0 << 24) | OPC_MSA_I8_01, - OPC_SHF_B = (0x0 << 24) | OPC_MSA_I8_02, OPC_ORI_B = (0x1 << 24) | OPC_MSA_I8_00, OPC_BMZI_B = (0x1 << 24) | OPC_MSA_I8_01, - OPC_SHF_H = (0x1 << 24) | OPC_MSA_I8_02, OPC_NORI_B = (0x2 << 24) | OPC_MSA_I8_00, OPC_BSELI_B = (0x2 << 24) | OPC_MSA_I8_01, - OPC_SHF_W = (0x2 << 24) | OPC_MSA_I8_02, OPC_XORI_B = (0x3 << 24) | OPC_MSA_I8_00, /* VEC/2R/2RF instruction */ @@ -465,20 +462,6 @@ static void gen_msa_i8(DisasContext *ctx) case OPC_BSELI_B: gen_helper_msa_bseli_b(cpu_env, twd, tws, ti8); break; - case OPC_SHF_B: - case OPC_SHF_H: - case OPC_SHF_W: - { - uint8_t df = (ctx->opcode >> 24) & 0x3; - if (df == DF_DOUBLE) { - gen_reserved_instruction(ctx); - } else { - TCGv_i32 tdf = tcg_const_i32(df); - gen_helper_msa_shf_df(cpu_env, tdf, twd, tws, ti8); - tcg_temp_free_i32(tdf); - } - } - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); @@ -490,6 +473,25 @@ static void gen_msa_i8(DisasContext *ctx) tcg_temp_free_i32(ti8); } +static bool trans_SHF(DisasContext *ctx, arg_msa_i *a) +{ + if (a->df == DF_DOUBLE) { + return false; + } + + if (!check_msa_enabled(ctx)) { + return true; + } + + gen_helper_msa_shf_df(cpu_env, + tcg_constant_i32(a->df), + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws), + tcg_constant_i32(a->sa)); + + return true; +} + static bool trans_msa_i5(DisasContext *ctx, arg_msa_i *a, gen_helper_piiii *gen_msa_i5) { From 7cc351ff9dc033d31ea45c72fc4b708ef585577d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 09:56:51 +0200 Subject: [PATCH 1051/1334] target/mips: Convert MSA I8 instruction format to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert instructions with an 8-bit immediate value and either implicit data format or data format df to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-13-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 8 ++++ target/mips/tcg/msa_translate.c | 75 +++++++++------------------------ 2 files changed, 27 insertions(+), 56 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 8e887f54ad..24847599a0 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -27,6 +27,7 @@ @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @s5 ...... ... df:2 sa:s5 ws:5 wd:5 ...... &msa_i @i8_df ...... df:2 sa:s8 ws:5 wd:5 ...... &msa_i +@i8 ...... .. sa:s8 ws:5 wd:5 ...... &msa_i df=0 @ldi ...... ... df:2 sa:s10 wd:5 ...... &msa_ldi @bit ...... ... ....... ws:5 wd:5 ...... &msa_bit df=%bit_df m=%bit_m @@ -39,6 +40,13 @@ BZ 010001 110 .. ..... ................ @bz BNZ 010001 111 .. ..... ................ @bz { + ANDI 011110 00 ........ ..... ..... 000000 @i8 + ORI 011110 01 ........ ..... ..... 000000 @i8 + NORI 011110 10 ........ ..... ..... 000000 @i8 + XORI 011110 11 ........ ..... ..... 000000 @i8 + BMNZI 011110 00 ........ ..... ..... 000001 @i8 + BMZI 011110 01 ........ ..... ..... 000001 @i8 + BSELI 011110 10 ........ ..... ..... 000001 @i8 SHF 011110 .. ........ ..... ..... 000010 @i8_df ADDVI 011110 000 .. ..... ..... ..... 000110 @u5 diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 1b1d88ac64..7e5bd783df 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -27,9 +27,6 @@ static int bit_df(DisasContext *ctx, int x); #define MASK_MSA_MINOR(op) (MASK_OP_MAJOR(op) | (op & 0x3F)) enum { - OPC_MSA_I8_00 = 0x00 | OPC_MSA, - OPC_MSA_I8_01 = 0x01 | OPC_MSA, - OPC_MSA_I8_02 = 0x02 | OPC_MSA, OPC_MSA_3R_0D = 0x0D | OPC_MSA, OPC_MSA_3R_0E = 0x0E | OPC_MSA, OPC_MSA_3R_0F = 0x0F | OPC_MSA, @@ -57,15 +54,6 @@ enum { }; enum { - /* I8 instruction */ - OPC_ANDI_B = (0x0 << 24) | OPC_MSA_I8_00, - OPC_BMNZI_B = (0x0 << 24) | OPC_MSA_I8_01, - OPC_ORI_B = (0x1 << 24) | OPC_MSA_I8_00, - OPC_BMZI_B = (0x1 << 24) | OPC_MSA_I8_01, - OPC_NORI_B = (0x2 << 24) | OPC_MSA_I8_00, - OPC_BSELI_B = (0x2 << 24) | OPC_MSA_I8_01, - OPC_XORI_B = (0x3 << 24) | OPC_MSA_I8_00, - /* VEC/2R/2RF instruction */ OPC_AND_V = (0x00 << 21) | OPC_MSA_VEC, OPC_OR_V = (0x01 << 21) | OPC_MSA_VEC, @@ -336,6 +324,7 @@ static inline bool check_msa_enabled(DisasContext *ctx) return true; } +typedef void gen_helper_piii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); typedef void gen_helper_piiii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, @@ -429,50 +418,29 @@ static bool trans_BNZ(DisasContext *ctx, arg_msa_bz *a) return gen_msa_BxZ(ctx, a->df, a->wt, a->sa, true); } -static void gen_msa_i8(DisasContext *ctx) +static bool trans_msa_i8(DisasContext *ctx, arg_msa_i *a, + gen_helper_piii *gen_msa_i8) { -#define MASK_MSA_I8(op) (MASK_MSA_MINOR(op) | (op & (0x03 << 24))) - uint8_t i8 = (ctx->opcode >> 16) & 0xff; - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; - - TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 tws = tcg_const_i32(ws); - TCGv_i32 ti8 = tcg_const_i32(i8); - - switch (MASK_MSA_I8(ctx->opcode)) { - case OPC_ANDI_B: - gen_helper_msa_andi_b(cpu_env, twd, tws, ti8); - break; - case OPC_ORI_B: - gen_helper_msa_ori_b(cpu_env, twd, tws, ti8); - break; - case OPC_NORI_B: - gen_helper_msa_nori_b(cpu_env, twd, tws, ti8); - break; - case OPC_XORI_B: - gen_helper_msa_xori_b(cpu_env, twd, tws, ti8); - break; - case OPC_BMNZI_B: - gen_helper_msa_bmnzi_b(cpu_env, twd, tws, ti8); - break; - case OPC_BMZI_B: - gen_helper_msa_bmzi_b(cpu_env, twd, tws, ti8); - break; - case OPC_BSELI_B: - gen_helper_msa_bseli_b(cpu_env, twd, tws, ti8); - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; + if (!check_msa_enabled(ctx)) { + return true; } - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); - tcg_temp_free_i32(ti8); + gen_msa_i8(cpu_env, + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws), + tcg_constant_i32(a->sa)); + + return true; } +TRANS(ANDI, trans_msa_i8, gen_helper_msa_andi_b); +TRANS(ORI, trans_msa_i8, gen_helper_msa_ori_b); +TRANS(NORI, trans_msa_i8, gen_helper_msa_nori_b); +TRANS(XORI, trans_msa_i8, gen_helper_msa_xori_b); +TRANS(BMNZI, trans_msa_i8, gen_helper_msa_bmnzi_b); +TRANS(BMZI, trans_msa_i8, gen_helper_msa_bmzi_b); +TRANS(BSELI, trans_msa_i8, gen_helper_msa_bseli_b); + static bool trans_SHF(DisasContext *ctx, arg_msa_i *a) { if (a->df == DF_DOUBLE) { @@ -2106,11 +2074,6 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) } switch (MASK_MSA_MINOR(opcode)) { - case OPC_MSA_I8_00: - case OPC_MSA_I8_01: - case OPC_MSA_I8_02: - gen_msa_i8(ctx); - break; case OPC_MSA_3R_0D: case OPC_MSA_3R_0E: case OPC_MSA_3R_0F: From ce121fe23443fbf885b3c0a583a91efd2efa6e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 10:02:55 +0200 Subject: [PATCH 1052/1334] target/mips: Convert MSA load/store instruction format to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert load/store instructions to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-14-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 4 ++ target/mips/tcg/msa_translate.c | 91 ++++++++++++--------------------- 2 files changed, 36 insertions(+), 59 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 24847599a0..0aeb83d5c5 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -22,6 +22,7 @@ %bit_m 16:7 !function=bit_m @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r +@ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @bz ...... ... df:2 wt:5 sa:16 &msa_bz @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @@ -78,5 +79,8 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + LD 011110 .......... ..... ..... 1000 .. @ldst + ST 011110 .......... ..... ..... 1001 .. @ldst + MSA 011110 -------------------------- } diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 7e5bd783df..2a7fb925b0 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -41,16 +41,6 @@ enum { OPC_MSA_3RF_1B = 0x1B | OPC_MSA, OPC_MSA_3RF_1C = 0x1C | OPC_MSA, OPC_MSA_VEC = 0x1E | OPC_MSA, - - /* MI10 instruction */ - OPC_LD_B = (0x20) | OPC_MSA, - OPC_LD_H = (0x21) | OPC_MSA, - OPC_LD_W = (0x22) | OPC_MSA, - OPC_LD_D = (0x23) | OPC_MSA, - OPC_ST_B = (0x24) | OPC_MSA, - OPC_ST_H = (0x25) | OPC_MSA, - OPC_ST_W = (0x26) | OPC_MSA, - OPC_ST_D = (0x27) | OPC_MSA, }; enum { @@ -324,9 +314,19 @@ static inline bool check_msa_enabled(DisasContext *ctx) return true; } +typedef void gen_helper_piv(TCGv_ptr, TCGv_i32, TCGv); typedef void gen_helper_piii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); typedef void gen_helper_piiii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); +#define TRANS_DF_x(TYPE, NAME, trans_func, gen_func) \ + static gen_helper_p##TYPE * const NAME##_tab[4] = { \ + gen_func##_b, gen_func##_h, gen_func##_w, gen_func##_d \ + }; \ + TRANS(NAME, trans_func, NAME##_tab[a->df]) + +#define TRANS_DF_iv(NAME, trans_func, gen_func) \ + TRANS_DF_x(iv, NAME, trans_func, gen_func) + static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, TCGCond cond) { @@ -2096,55 +2096,6 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) case OPC_MSA_VEC: gen_msa_vec(ctx); break; - case OPC_LD_B: - case OPC_LD_H: - case OPC_LD_W: - case OPC_LD_D: - case OPC_ST_B: - case OPC_ST_H: - case OPC_ST_W: - case OPC_ST_D: - { - int32_t s10 = sextract32(ctx->opcode, 16, 10); - uint8_t rs = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; - uint8_t df = (ctx->opcode >> 0) & 0x3; - - TCGv_i32 twd = tcg_const_i32(wd); - TCGv taddr = tcg_temp_new(); - gen_base_offset_addr(ctx, taddr, rs, s10 << df); - - switch (MASK_MSA_MINOR(opcode)) { - case OPC_LD_B: - gen_helper_msa_ld_b(cpu_env, twd, taddr); - break; - case OPC_LD_H: - gen_helper_msa_ld_h(cpu_env, twd, taddr); - break; - case OPC_LD_W: - gen_helper_msa_ld_w(cpu_env, twd, taddr); - break; - case OPC_LD_D: - gen_helper_msa_ld_d(cpu_env, twd, taddr); - break; - case OPC_ST_B: - gen_helper_msa_st_b(cpu_env, twd, taddr); - break; - case OPC_ST_H: - gen_helper_msa_st_h(cpu_env, twd, taddr); - break; - case OPC_ST_W: - gen_helper_msa_st_w(cpu_env, twd, taddr); - break; - case OPC_ST_D: - gen_helper_msa_st_d(cpu_env, twd, taddr); - break; - } - - tcg_temp_free_i32(twd); - tcg_temp_free(taddr); - } - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); @@ -2154,6 +2105,28 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) return true; } +static bool trans_msa_ldst(DisasContext *ctx, arg_msa_i *a, + gen_helper_piv *gen_msa_ldst) +{ + TCGv taddr; + + if (!check_msa_enabled(ctx)) { + return true; + } + + taddr = tcg_temp_new(); + + gen_base_offset_addr(ctx, taddr, a->ws, a->sa << a->df); + gen_msa_ldst(cpu_env, tcg_constant_i32(a->wd), taddr); + + tcg_temp_free(taddr); + + return true; +} + +TRANS_DF_iv(LD, trans_msa_ldst, gen_helper_msa_ld); +TRANS_DF_iv(ST, trans_msa_ldst, gen_helper_msa_st); + static bool trans_LSA(DisasContext *ctx, arg_r *a) { return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa); From 5c5b64000c751ed3f3535c17095161fea02db660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Oct 2021 22:38:21 +0200 Subject: [PATCH 1053/1334] target/mips: Convert MSA 2RF instruction format to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 2-register floating-point operations to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-15-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 20 ++++++ target/mips/tcg/msa_translate.c | 118 +++++++++----------------------- 2 files changed, 53 insertions(+), 85 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 0aeb83d5c5..33288b5035 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -13,6 +13,7 @@ &r rs rt rd sa +&msa_r df wd ws wt &msa_bz df wt sa &msa_ldi df wd sa &msa_i df wd ws sa @@ -20,11 +21,13 @@ %bit_df 16:7 !function=bit_df %bit_m 16:7 !function=bit_m +%2r_df_w 16:1 !function=plus_2 @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @bz ...... ... df:2 wt:5 sa:16 &msa_bz +@2rf ...... ......... . ws:5 wd:5 ...... &msa_r wt=0 df=%2r_df_w @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @s5 ...... ... df:2 sa:s5 ws:5 wd:5 ...... &msa_i @i8_df ...... df:2 sa:s8 ws:5 wd:5 ...... &msa_i @@ -79,6 +82,23 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + FCLASS 011110 110010000 . ..... ..... 011110 @2rf + FTRUNC_S 011110 110010001 . ..... ..... 011110 @2rf + FTRUNC_U 011110 110010010 . ..... ..... 011110 @2rf + FSQRT 011110 110010011 . ..... ..... 011110 @2rf + FRSQRT 011110 110010100 . ..... ..... 011110 @2rf + FRCP 011110 110010101 . ..... ..... 011110 @2rf + FRINT 011110 110010110 . ..... ..... 011110 @2rf + FLOG2 011110 110010111 . ..... ..... 011110 @2rf + FEXUPL 011110 110011000 . ..... ..... 011110 @2rf + FEXUPR 011110 110011001 . ..... ..... 011110 @2rf + FFQL 011110 110011010 . ..... ..... 011110 @2rf + FFQR 011110 110011011 . ..... ..... 011110 @2rf + FTINT_S 011110 110011100 . ..... ..... 011110 @2rf + FTINT_U 011110 110011101 . ..... ..... 011110 @2rf + FFINT_S 011110 110011110 . ..... ..... 011110 @2rf + FFINT_U 011110 110011111 . ..... ..... 011110 @2rf + LD 011110 .......... ..... ..... 1000 .. @ldst ST 011110 .......... ..... ..... 1001 .. @ldst diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 2a7fb925b0..704273dfd2 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -20,6 +20,11 @@ static int bit_m(DisasContext *ctx, int x); static int bit_df(DisasContext *ctx, int x); +static inline int plus_2(DisasContext *s, int x) +{ + return x + 2; +} + /* Include the auto-generated decoder. */ #include "decode-msa.c.inc" @@ -44,7 +49,7 @@ enum { }; enum { - /* VEC/2R/2RF instruction */ + /* VEC/2R instruction */ OPC_AND_V = (0x00 << 21) | OPC_MSA_VEC, OPC_OR_V = (0x01 << 21) | OPC_MSA_VEC, OPC_NOR_V = (0x02 << 21) | OPC_MSA_VEC, @@ -54,7 +59,6 @@ enum { OPC_BSEL_V = (0x06 << 21) | OPC_MSA_VEC, OPC_MSA_2R = (0x18 << 21) | OPC_MSA_VEC, - OPC_MSA_2RF = (0x19 << 21) | OPC_MSA_VEC, /* 2R instruction df(bits 17..16) = _b, _h, _w, _d */ OPC_FILL_df = (0x00 << 18) | OPC_MSA_2R, @@ -62,24 +66,6 @@ enum { OPC_NLOC_df = (0x02 << 18) | OPC_MSA_2R, OPC_NLZC_df = (0x03 << 18) | OPC_MSA_2R, - /* 2RF instruction df(bit 16) = _w, _d */ - OPC_FCLASS_df = (0x00 << 17) | OPC_MSA_2RF, - OPC_FTRUNC_S_df = (0x01 << 17) | OPC_MSA_2RF, - OPC_FTRUNC_U_df = (0x02 << 17) | OPC_MSA_2RF, - OPC_FSQRT_df = (0x03 << 17) | OPC_MSA_2RF, - OPC_FRSQRT_df = (0x04 << 17) | OPC_MSA_2RF, - OPC_FRCP_df = (0x05 << 17) | OPC_MSA_2RF, - OPC_FRINT_df = (0x06 << 17) | OPC_MSA_2RF, - OPC_FLOG2_df = (0x07 << 17) | OPC_MSA_2RF, - OPC_FEXUPL_df = (0x08 << 17) | OPC_MSA_2RF, - OPC_FEXUPR_df = (0x09 << 17) | OPC_MSA_2RF, - OPC_FFQL_df = (0x0A << 17) | OPC_MSA_2RF, - OPC_FFQR_df = (0x0B << 17) | OPC_MSA_2RF, - OPC_FTINT_S_df = (0x0C << 17) | OPC_MSA_2RF, - OPC_FTINT_U_df = (0x0D << 17) | OPC_MSA_2RF, - OPC_FFINT_S_df = (0x0E << 17) | OPC_MSA_2RF, - OPC_FFINT_U_df = (0x0F << 17) | OPC_MSA_2RF, - /* 3R instruction df(bits 22..21) = _b, _h, _w, d */ OPC_SLL_df = (0x0 << 23) | OPC_MSA_3R_0D, OPC_ADDV_df = (0x0 << 23) | OPC_MSA_3R_0E, @@ -1930,73 +1916,38 @@ static void gen_msa_2r(DisasContext *ctx) tcg_temp_free_i32(tws); } -static void gen_msa_2rf(DisasContext *ctx) +static bool trans_msa_2rf(DisasContext *ctx, arg_msa_r *a, + gen_helper_piii *gen_msa_2rf) { -#define MASK_MSA_2RF(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21)) | \ - (op & (0xf << 17))) - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; - uint8_t df = (ctx->opcode >> 16) & 0x1; - TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 tws = tcg_const_i32(ws); - /* adjust df value for floating-point instruction */ - TCGv_i32 tdf = tcg_constant_i32(DF_WORD + df); - - switch (MASK_MSA_2RF(ctx->opcode)) { - case OPC_FCLASS_df: - gen_helper_msa_fclass_df(cpu_env, tdf, twd, tws); - break; - case OPC_FTRUNC_S_df: - gen_helper_msa_ftrunc_s_df(cpu_env, tdf, twd, tws); - break; - case OPC_FTRUNC_U_df: - gen_helper_msa_ftrunc_u_df(cpu_env, tdf, twd, tws); - break; - case OPC_FSQRT_df: - gen_helper_msa_fsqrt_df(cpu_env, tdf, twd, tws); - break; - case OPC_FRSQRT_df: - gen_helper_msa_frsqrt_df(cpu_env, tdf, twd, tws); - break; - case OPC_FRCP_df: - gen_helper_msa_frcp_df(cpu_env, tdf, twd, tws); - break; - case OPC_FRINT_df: - gen_helper_msa_frint_df(cpu_env, tdf, twd, tws); - break; - case OPC_FLOG2_df: - gen_helper_msa_flog2_df(cpu_env, tdf, twd, tws); - break; - case OPC_FEXUPL_df: - gen_helper_msa_fexupl_df(cpu_env, tdf, twd, tws); - break; - case OPC_FEXUPR_df: - gen_helper_msa_fexupr_df(cpu_env, tdf, twd, tws); - break; - case OPC_FFQL_df: - gen_helper_msa_ffql_df(cpu_env, tdf, twd, tws); - break; - case OPC_FFQR_df: - gen_helper_msa_ffqr_df(cpu_env, tdf, twd, tws); - break; - case OPC_FTINT_S_df: - gen_helper_msa_ftint_s_df(cpu_env, tdf, twd, tws); - break; - case OPC_FTINT_U_df: - gen_helper_msa_ftint_u_df(cpu_env, tdf, twd, tws); - break; - case OPC_FFINT_S_df: - gen_helper_msa_ffint_s_df(cpu_env, tdf, twd, tws); - break; - case OPC_FFINT_U_df: - gen_helper_msa_ffint_u_df(cpu_env, tdf, twd, tws); - break; + if (!check_msa_enabled(ctx)) { + return true; } - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); + gen_msa_2rf(cpu_env, + tcg_constant_i32(a->df), + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws)); + + return true; } +TRANS(FCLASS, trans_msa_2rf, gen_helper_msa_fclass_df); +TRANS(FTRUNC_S, trans_msa_2rf, gen_helper_msa_fclass_df); +TRANS(FTRUNC_U, trans_msa_2rf, gen_helper_msa_ftrunc_s_df); +TRANS(FSQRT, trans_msa_2rf, gen_helper_msa_fsqrt_df); +TRANS(FRSQRT, trans_msa_2rf, gen_helper_msa_frsqrt_df); +TRANS(FRCP, trans_msa_2rf, gen_helper_msa_frcp_df); +TRANS(FRINT, trans_msa_2rf, gen_helper_msa_frint_df); +TRANS(FLOG2, trans_msa_2rf, gen_helper_msa_flog2_df); +TRANS(FEXUPL, trans_msa_2rf, gen_helper_msa_fexupl_df); +TRANS(FEXUPR, trans_msa_2rf, gen_helper_msa_fexupr_df); +TRANS(FFQL, trans_msa_2rf, gen_helper_msa_ffql_df); +TRANS(FFQR, trans_msa_2rf, gen_helper_msa_ffqr_df); +TRANS(FTINT_S, trans_msa_2rf, gen_helper_msa_ftint_s_df); +TRANS(FTINT_U, trans_msa_2rf, gen_helper_msa_ftint_u_df); +TRANS(FFINT_S, trans_msa_2rf, gen_helper_msa_ffint_s_df); +TRANS(FFINT_U, trans_msa_2rf, gen_helper_msa_ffint_u_df); + static void gen_msa_vec_v(DisasContext *ctx) { #define MASK_MSA_VEC(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21))) @@ -2055,9 +2006,6 @@ static void gen_msa_vec(DisasContext *ctx) case OPC_MSA_2R: gen_msa_2r(ctx); break; - case OPC_MSA_2RF: - gen_msa_2rf(ctx); - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); From 675bf34a6fe9db202edd49755c8c586764f5eabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 10:20:04 +0200 Subject: [PATCH 1054/1334] target/mips: Convert MSA FILL opcode to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the FILL opcode (Vector Fill from GPR) to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-16-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 2 ++ target/mips/tcg/msa_translate.c | 31 +++++++++++++++++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 33288b5035..bcbc573dee 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -27,6 +27,7 @@ @ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @bz ...... ... df:2 wt:5 sa:16 &msa_bz +@2r ...... ........ df:2 ws:5 wd:5 ...... &msa_r wt=0 @2rf ...... ......... . ws:5 wd:5 ...... &msa_r wt=0 df=%2r_df_w @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @s5 ...... ... df:2 sa:s5 ws:5 wd:5 ...... &msa_i @@ -82,6 +83,7 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + FILL 011110 11000000 .. ..... ..... 011110 @2r FCLASS 011110 110010000 . ..... ..... 011110 @2rf FTRUNC_S 011110 110010001 . ..... ..... 011110 @2rf FTRUNC_U 011110 110010010 . ..... ..... 011110 @2rf diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 704273dfd2..c750908898 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -61,7 +61,6 @@ enum { OPC_MSA_2R = (0x18 << 21) | OPC_MSA_VEC, /* 2R instruction df(bits 17..16) = _b, _h, _w, _d */ - OPC_FILL_df = (0x00 << 18) | OPC_MSA_2R, OPC_PCNT_df = (0x01 << 18) | OPC_MSA_2R, OPC_NLOC_df = (0x02 << 18) | OPC_MSA_2R, OPC_NLZC_df = (0x03 << 18) | OPC_MSA_2R, @@ -1847,17 +1846,6 @@ static void gen_msa_2r(DisasContext *ctx) TCGv_i32 tws = tcg_const_i32(ws); switch (MASK_MSA_2R(ctx->opcode)) { - case OPC_FILL_df: -#if !defined(TARGET_MIPS64) - /* Double format valid only for MIPS64 */ - if (df == DF_DOUBLE) { - gen_reserved_instruction(ctx); - break; - } -#endif - gen_helper_msa_fill_df(cpu_env, tcg_constant_i32(df), - twd, tws); /* trs */ - break; case OPC_NLOC_df: switch (df) { case DF_BYTE: @@ -1916,6 +1904,25 @@ static void gen_msa_2r(DisasContext *ctx) tcg_temp_free_i32(tws); } +static bool trans_FILL(DisasContext *ctx, arg_msa_r *a) +{ + if (TARGET_LONG_BITS != 64 && a->df == DF_DOUBLE) { + /* Double format valid only for MIPS64 */ + return false; + } + + if (!check_msa_enabled(ctx)) { + return true; + } + + gen_helper_msa_fill_df(cpu_env, + tcg_constant_i32(a->df), + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws)); + + return true; +} + static bool trans_msa_2rf(DisasContext *ctx, arg_msa_r *a, gen_helper_piii *gen_msa_2rf) { From adcff99a6b41c029841aee30582606e6ae196507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 10:23:08 +0200 Subject: [PATCH 1055/1334] target/mips: Convert MSA 2R instruction format to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 2-register operations to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-17-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 3 ++ target/mips/tcg/msa_translate.c | 91 ++++++--------------------------- 2 files changed, 19 insertions(+), 75 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index bcbc573dee..b6ac80560f 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -84,6 +84,9 @@ BNZ 010001 111 .. ..... ................ @bz SRLRI 011110 011 ....... ..... ..... 001010 @bit FILL 011110 11000000 .. ..... ..... 011110 @2r + PCNT 011110 11000001 .. ..... ..... 011110 @2r + NLOC 011110 11000010 .. ..... ..... 011110 @2r + NLZC 011110 11000011 .. ..... ..... 011110 @2r FCLASS 011110 110010000 . ..... ..... 011110 @2rf FTRUNC_S 011110 110010001 . ..... ..... 011110 @2rf FTRUNC_U 011110 110010010 . ..... ..... 011110 @2rf diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index c750908898..c6e38281a6 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -49,7 +49,7 @@ enum { }; enum { - /* VEC/2R instruction */ + /* VEC instruction */ OPC_AND_V = (0x00 << 21) | OPC_MSA_VEC, OPC_OR_V = (0x01 << 21) | OPC_MSA_VEC, OPC_NOR_V = (0x02 << 21) | OPC_MSA_VEC, @@ -58,13 +58,6 @@ enum { OPC_BMZ_V = (0x05 << 21) | OPC_MSA_VEC, OPC_BSEL_V = (0x06 << 21) | OPC_MSA_VEC, - OPC_MSA_2R = (0x18 << 21) | OPC_MSA_VEC, - - /* 2R instruction df(bits 17..16) = _b, _h, _w, _d */ - OPC_PCNT_df = (0x01 << 18) | OPC_MSA_2R, - OPC_NLOC_df = (0x02 << 18) | OPC_MSA_2R, - OPC_NLZC_df = (0x03 << 18) | OPC_MSA_2R, - /* 3R instruction df(bits 22..21) = _b, _h, _w, d */ OPC_SLL_df = (0x0 << 23) | OPC_MSA_3R_0D, OPC_ADDV_df = (0x0 << 23) | OPC_MSA_3R_0E, @@ -300,6 +293,7 @@ static inline bool check_msa_enabled(DisasContext *ctx) } typedef void gen_helper_piv(TCGv_ptr, TCGv_i32, TCGv); +typedef void gen_helper_pii(TCGv_ptr, TCGv_i32, TCGv_i32); typedef void gen_helper_piii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); typedef void gen_helper_piiii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); @@ -312,6 +306,9 @@ typedef void gen_helper_piiii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); #define TRANS_DF_iv(NAME, trans_func, gen_func) \ TRANS_DF_x(iv, NAME, trans_func, gen_func) +#define TRANS_DF_ii(NAME, trans_func, gen_func) \ + TRANS_DF_x(ii, NAME, trans_func, gen_func) + static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, TCGCond cond) { @@ -1835,75 +1832,22 @@ static void gen_msa_3rf(DisasContext *ctx) tcg_temp_free_i32(twt); } -static void gen_msa_2r(DisasContext *ctx) +static bool trans_msa_2r(DisasContext *ctx, arg_msa_r *a, + gen_helper_pii *gen_msa_2r) { -#define MASK_MSA_2R(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21)) | \ - (op & (0x7 << 18))) - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; - uint8_t df = (ctx->opcode >> 16) & 0x3; - TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 tws = tcg_const_i32(ws); - - switch (MASK_MSA_2R(ctx->opcode)) { - case OPC_NLOC_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_nloc_b(cpu_env, twd, tws); - break; - case DF_HALF: - gen_helper_msa_nloc_h(cpu_env, twd, tws); - break; - case DF_WORD: - gen_helper_msa_nloc_w(cpu_env, twd, tws); - break; - case DF_DOUBLE: - gen_helper_msa_nloc_d(cpu_env, twd, tws); - break; - } - break; - case OPC_NLZC_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_nlzc_b(cpu_env, twd, tws); - break; - case DF_HALF: - gen_helper_msa_nlzc_h(cpu_env, twd, tws); - break; - case DF_WORD: - gen_helper_msa_nlzc_w(cpu_env, twd, tws); - break; - case DF_DOUBLE: - gen_helper_msa_nlzc_d(cpu_env, twd, tws); - break; - } - break; - case OPC_PCNT_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_pcnt_b(cpu_env, twd, tws); - break; - case DF_HALF: - gen_helper_msa_pcnt_h(cpu_env, twd, tws); - break; - case DF_WORD: - gen_helper_msa_pcnt_w(cpu_env, twd, tws); - break; - case DF_DOUBLE: - gen_helper_msa_pcnt_d(cpu_env, twd, tws); - break; - } - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; + if (!check_msa_enabled(ctx)) { + return true; } - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); + gen_msa_2r(cpu_env, tcg_constant_i32(a->wd), tcg_constant_i32(a->ws)); + + return true; } +TRANS_DF_ii(PCNT, trans_msa_2r, gen_helper_msa_pcnt); +TRANS_DF_ii(NLOC, trans_msa_2r, gen_helper_msa_nloc); +TRANS_DF_ii(NLZC, trans_msa_2r, gen_helper_msa_nlzc); + static bool trans_FILL(DisasContext *ctx, arg_msa_r *a) { if (TARGET_LONG_BITS != 64 && a->df == DF_DOUBLE) { @@ -2010,9 +1954,6 @@ static void gen_msa_vec(DisasContext *ctx) case OPC_BSEL_V: gen_msa_vec_v(ctx); break; - case OPC_MSA_2R: - gen_msa_2r(ctx); - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); From 7acb5c78a75421851b778b463c88c232ce1dc184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 10:09:42 +0200 Subject: [PATCH 1056/1334] target/mips: Convert MSA VEC instruction format to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 3-register instructions with implicit data formats to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-18-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 8 +++ target/mips/tcg/msa_translate.c | 98 ++++++++------------------------- 2 files changed, 31 insertions(+), 75 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index b6ac80560f..afcb868aad 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -27,6 +27,7 @@ @ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @bz ...... ... df:2 wt:5 sa:16 &msa_bz +@vec ...... ..... wt:5 ws:5 wd:5 ...... &msa_r df=0 @2r ...... ........ df:2 ws:5 wd:5 ...... &msa_r wt=0 @2rf ...... ......... . ws:5 wd:5 ...... &msa_r wt=0 df=%2r_df_w @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @@ -83,6 +84,13 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + AND_V 011110 00000 ..... ..... ..... 011110 @vec + OR_V 011110 00001 ..... ..... ..... 011110 @vec + NOR_V 011110 00010 ..... ..... ..... 011110 @vec + XOR_V 011110 00011 ..... ..... ..... 011110 @vec + BMNZ_V 011110 00100 ..... ..... ..... 011110 @vec + BMZ_V 011110 00101 ..... ..... ..... 011110 @vec + BSEL_V 011110 00110 ..... ..... ..... 011110 @vec FILL 011110 11000000 .. ..... ..... 011110 @2r PCNT 011110 11000001 .. ..... ..... 011110 @2r NLOC 011110 11000010 .. ..... ..... 011110 @2r diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index c6e38281a6..45a6b60d54 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -45,19 +45,9 @@ enum { OPC_MSA_3RF_1A = 0x1A | OPC_MSA, OPC_MSA_3RF_1B = 0x1B | OPC_MSA, OPC_MSA_3RF_1C = 0x1C | OPC_MSA, - OPC_MSA_VEC = 0x1E | OPC_MSA, }; enum { - /* VEC instruction */ - OPC_AND_V = (0x00 << 21) | OPC_MSA_VEC, - OPC_OR_V = (0x01 << 21) | OPC_MSA_VEC, - OPC_NOR_V = (0x02 << 21) | OPC_MSA_VEC, - OPC_XOR_V = (0x03 << 21) | OPC_MSA_VEC, - OPC_BMNZ_V = (0x04 << 21) | OPC_MSA_VEC, - OPC_BMZ_V = (0x05 << 21) | OPC_MSA_VEC, - OPC_BSEL_V = (0x06 << 21) | OPC_MSA_VEC, - /* 3R instruction df(bits 22..21) = _b, _h, _w, d */ OPC_SLL_df = (0x0 << 23) | OPC_MSA_3R_0D, OPC_ADDV_df = (0x0 << 23) | OPC_MSA_3R_0E, @@ -517,6 +507,29 @@ TRANS(SAT_U, trans_msa_bit, gen_helper_msa_sat_u_df); TRANS(SRARI, trans_msa_bit, gen_helper_msa_srari_df); TRANS(SRLRI, trans_msa_bit, gen_helper_msa_srlri_df); +static bool trans_msa_3r(DisasContext *ctx, arg_msa_r *a, + gen_helper_piii *gen_msa_3r) +{ + if (!check_msa_enabled(ctx)) { + return true; + } + + gen_msa_3r(cpu_env, + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws), + tcg_constant_i32(a->wt)); + + return true; +} + +TRANS(AND_V, trans_msa_3r, gen_helper_msa_and_v); +TRANS(OR_V, trans_msa_3r, gen_helper_msa_or_v); +TRANS(NOR_V, trans_msa_3r, gen_helper_msa_nor_v); +TRANS(XOR_V, trans_msa_3r, gen_helper_msa_xor_v); +TRANS(BMNZ_V, trans_msa_3r, gen_helper_msa_bmnz_v); +TRANS(BMZ_V, trans_msa_3r, gen_helper_msa_bmz_v); +TRANS(BSEL_V, trans_msa_3r, gen_helper_msa_bsel_v); + static void gen_msa_3r(DisasContext *ctx) { #define MASK_MSA_3R(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) @@ -1899,68 +1912,6 @@ TRANS(FTINT_U, trans_msa_2rf, gen_helper_msa_ftint_u_df); TRANS(FFINT_S, trans_msa_2rf, gen_helper_msa_ffint_s_df); TRANS(FFINT_U, trans_msa_2rf, gen_helper_msa_ffint_u_df); -static void gen_msa_vec_v(DisasContext *ctx) -{ -#define MASK_MSA_VEC(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21))) - uint8_t wt = (ctx->opcode >> 16) & 0x1f; - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; - TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 tws = tcg_const_i32(ws); - TCGv_i32 twt = tcg_const_i32(wt); - - switch (MASK_MSA_VEC(ctx->opcode)) { - case OPC_AND_V: - gen_helper_msa_and_v(cpu_env, twd, tws, twt); - break; - case OPC_OR_V: - gen_helper_msa_or_v(cpu_env, twd, tws, twt); - break; - case OPC_NOR_V: - gen_helper_msa_nor_v(cpu_env, twd, tws, twt); - break; - case OPC_XOR_V: - gen_helper_msa_xor_v(cpu_env, twd, tws, twt); - break; - case OPC_BMNZ_V: - gen_helper_msa_bmnz_v(cpu_env, twd, tws, twt); - break; - case OPC_BMZ_V: - gen_helper_msa_bmz_v(cpu_env, twd, tws, twt); - break; - case OPC_BSEL_V: - gen_helper_msa_bsel_v(cpu_env, twd, tws, twt); - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; - } - - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); - tcg_temp_free_i32(twt); -} - -static void gen_msa_vec(DisasContext *ctx) -{ - switch (MASK_MSA_VEC(ctx->opcode)) { - case OPC_AND_V: - case OPC_OR_V: - case OPC_NOR_V: - case OPC_XOR_V: - case OPC_BMNZ_V: - case OPC_BMZ_V: - case OPC_BSEL_V: - gen_msa_vec_v(ctx); - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; - } -} - static bool trans_MSA(DisasContext *ctx, arg_MSA *a) { uint32_t opcode = ctx->opcode; @@ -1989,9 +1940,6 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) case OPC_MSA_3RF_1C: gen_msa_3rf(ctx); break; - case OPC_MSA_VEC: - gen_msa_vec(ctx); - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); From ff29e5d3c0ac7f88b9cf1840451f5fc6c958171d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 10:35:20 +0200 Subject: [PATCH 1057/1334] target/mips: Convert MSA 3RF instruction format to decodetree (DF_HALF) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 3-register floating-point or fixed-point operations to decodetree. Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211028210843.2120802-19-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 9 +++++ target/mips/tcg/msa_translate.c | 68 ++++++++++++++------------------- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index afcb868aad..f90b2d21c9 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -22,6 +22,7 @@ %bit_df 16:7 !function=bit_df %bit_m 16:7 !function=bit_m %2r_df_w 16:1 !function=plus_2 +%3r_df_h 21:1 !function=plus_1 @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i @@ -30,6 +31,7 @@ @vec ...... ..... wt:5 ws:5 wd:5 ...... &msa_r df=0 @2r ...... ........ df:2 ws:5 wd:5 ...... &msa_r wt=0 @2rf ...... ......... . ws:5 wd:5 ...... &msa_r wt=0 df=%2r_df_w +@3rf_h ...... .... . wt:5 ws:5 wd:5 ...... &msa_r df=%3r_df_h @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @s5 ...... ... df:2 sa:s5 ws:5 wd:5 ...... &msa_i @i8_df ...... df:2 sa:s8 ws:5 wd:5 ...... &msa_i @@ -84,6 +86,13 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + MUL_Q 011110 0100 . ..... ..... ..... 011100 @3rf_h + MADD_Q 011110 0101 . ..... ..... ..... 011100 @3rf_h + MSUB_Q 011110 0110 . ..... ..... ..... 011100 @3rf_h + MULR_Q 011110 1100 . ..... ..... ..... 011100 @3rf_h + MADDR_Q 011110 1101 . ..... ..... ..... 011100 @3rf_h + MSUBR_Q 011110 1110 . ..... ..... ..... 011100 @3rf_h + AND_V 011110 00000 ..... ..... ..... 011110 @vec OR_V 011110 00001 ..... ..... ..... 011110 @vec NOR_V 011110 00010 ..... ..... ..... 011110 @vec diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 45a6b60d54..65e56b2317 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -20,6 +20,11 @@ static int bit_m(DisasContext *ctx, int x); static int bit_df(DisasContext *ctx, int x); +static inline int plus_1(DisasContext *s, int x) +{ + return x + 1; +} + static inline int plus_2(DisasContext *s, int x) { return x + 2; @@ -138,12 +143,9 @@ enum { OPC_FCNE_df = (0x3 << 22) | OPC_MSA_3RF_1C, OPC_FCLT_df = (0x4 << 22) | OPC_MSA_3RF_1A, OPC_FMADD_df = (0x4 << 22) | OPC_MSA_3RF_1B, - OPC_MUL_Q_df = (0x4 << 22) | OPC_MSA_3RF_1C, OPC_FCULT_df = (0x5 << 22) | OPC_MSA_3RF_1A, OPC_FMSUB_df = (0x5 << 22) | OPC_MSA_3RF_1B, - OPC_MADD_Q_df = (0x5 << 22) | OPC_MSA_3RF_1C, OPC_FCLE_df = (0x6 << 22) | OPC_MSA_3RF_1A, - OPC_MSUB_Q_df = (0x6 << 22) | OPC_MSA_3RF_1C, OPC_FCULE_df = (0x7 << 22) | OPC_MSA_3RF_1A, OPC_FEXP2_df = (0x7 << 22) | OPC_MSA_3RF_1B, OPC_FSAF_df = (0x8 << 22) | OPC_MSA_3RF_1A, @@ -157,13 +159,10 @@ enum { OPC_FSNE_df = (0xB << 22) | OPC_MSA_3RF_1C, OPC_FSLT_df = (0xC << 22) | OPC_MSA_3RF_1A, OPC_FMIN_df = (0xC << 22) | OPC_MSA_3RF_1B, - OPC_MULR_Q_df = (0xC << 22) | OPC_MSA_3RF_1C, OPC_FSULT_df = (0xD << 22) | OPC_MSA_3RF_1A, OPC_FMIN_A_df = (0xD << 22) | OPC_MSA_3RF_1B, - OPC_MADDR_Q_df = (0xD << 22) | OPC_MSA_3RF_1C, OPC_FSLE_df = (0xE << 22) | OPC_MSA_3RF_1A, OPC_FMAX_df = (0xE << 22) | OPC_MSA_3RF_1B, - OPC_MSUBR_Q_df = (0xE << 22) | OPC_MSA_3RF_1C, OPC_FSULE_df = (0xF << 22) | OPC_MSA_3RF_1A, OPC_FMAX_A_df = (0xF << 22) | OPC_MSA_3RF_1B, }; @@ -507,6 +506,22 @@ TRANS(SAT_U, trans_msa_bit, gen_helper_msa_sat_u_df); TRANS(SRARI, trans_msa_bit, gen_helper_msa_srari_df); TRANS(SRLRI, trans_msa_bit, gen_helper_msa_srlri_df); +static bool trans_msa_3rf(DisasContext *ctx, arg_msa_r *a, + gen_helper_piiii *gen_msa_3rf) +{ + if (!check_msa_enabled(ctx)) { + return true; + } + + gen_msa_3rf(cpu_env, + tcg_constant_i32(a->df), + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws), + tcg_constant_i32(a->wt)); + + return true; +} + static bool trans_msa_3r(DisasContext *ctx, arg_msa_r *a, gen_helper_piii *gen_msa_3r) { @@ -1682,6 +1697,13 @@ static void gen_msa_elm(DisasContext *ctx) gen_msa_elm_df(ctx, df, n); } +TRANS(MUL_Q, trans_msa_3rf, gen_helper_msa_mul_q_df); +TRANS(MADD_Q, trans_msa_3rf, gen_helper_msa_madd_q_df); +TRANS(MSUB_Q, trans_msa_3rf, gen_helper_msa_msub_q_df); +TRANS(MULR_Q, trans_msa_3rf, gen_helper_msa_mulr_q_df); +TRANS(MADDR_Q, trans_msa_3rf, gen_helper_msa_maddr_q_df); +TRANS(MSUBR_Q, trans_msa_3rf, gen_helper_msa_msubr_q_df); + static void gen_msa_3rf(DisasContext *ctx) { #define MASK_MSA_3RF(op) (MASK_MSA_MINOR(op) | (op & (0xf << 22))) @@ -1693,22 +1715,8 @@ static void gen_msa_3rf(DisasContext *ctx) TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tws = tcg_const_i32(ws); TCGv_i32 twt = tcg_const_i32(wt); - TCGv_i32 tdf; - /* adjust df value for floating-point instruction */ - switch (MASK_MSA_3RF(ctx->opcode)) { - case OPC_MUL_Q_df: - case OPC_MADD_Q_df: - case OPC_MSUB_Q_df: - case OPC_MULR_Q_df: - case OPC_MADDR_Q_df: - case OPC_MSUBR_Q_df: - tdf = tcg_constant_i32(DF_HALF + df); - break; - default: - tdf = tcg_constant_i32(DF_WORD + df); - break; - } + TCGv_i32 tdf = tcg_constant_i32(DF_WORD + df); switch (MASK_MSA_3RF(ctx->opcode)) { case OPC_FCAF_df: @@ -1750,24 +1758,15 @@ static void gen_msa_3rf(DisasContext *ctx) case OPC_FMADD_df: gen_helper_msa_fmadd_df(cpu_env, tdf, twd, tws, twt); break; - case OPC_MUL_Q_df: - gen_helper_msa_mul_q_df(cpu_env, tdf, twd, tws, twt); - break; case OPC_FCULT_df: gen_helper_msa_fcult_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FMSUB_df: gen_helper_msa_fmsub_df(cpu_env, tdf, twd, tws, twt); break; - case OPC_MADD_Q_df: - gen_helper_msa_madd_q_df(cpu_env, tdf, twd, tws, twt); - break; case OPC_FCLE_df: gen_helper_msa_fcle_df(cpu_env, tdf, twd, tws, twt); break; - case OPC_MSUB_Q_df: - gen_helper_msa_msub_q_df(cpu_env, tdf, twd, tws, twt); - break; case OPC_FCULE_df: gen_helper_msa_fcule_df(cpu_env, tdf, twd, tws, twt); break; @@ -1807,27 +1806,18 @@ static void gen_msa_3rf(DisasContext *ctx) case OPC_FMIN_df: gen_helper_msa_fmin_df(cpu_env, tdf, twd, tws, twt); break; - case OPC_MULR_Q_df: - gen_helper_msa_mulr_q_df(cpu_env, tdf, twd, tws, twt); - break; case OPC_FSULT_df: gen_helper_msa_fsult_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FMIN_A_df: gen_helper_msa_fmin_a_df(cpu_env, tdf, twd, tws, twt); break; - case OPC_MADDR_Q_df: - gen_helper_msa_maddr_q_df(cpu_env, tdf, twd, tws, twt); - break; case OPC_FSLE_df: gen_helper_msa_fsle_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FMAX_df: gen_helper_msa_fmax_df(cpu_env, tdf, twd, tws, twt); break; - case OPC_MSUBR_Q_df: - gen_helper_msa_msubr_q_df(cpu_env, tdf, twd, tws, twt); - break; case OPC_FSULE_df: gen_helper_msa_fsule_df(cpu_env, tdf, twd, tws, twt); break; From 2d5246f30573c27cee609021850fdd3c56cda1ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 10:37:13 +0200 Subject: [PATCH 1058/1334] target/mips: Convert MSA 3RF instruction format to decodetree (DF_WORD) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 3-register floating-point or fixed-point operations to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-20-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 39 ++++++ target/mips/tcg/msa_translate.c | 213 ++++++-------------------------- 2 files changed, 76 insertions(+), 176 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index f90b2d21c9..1d6ada4c14 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -23,6 +23,7 @@ %bit_m 16:7 !function=bit_m %2r_df_w 16:1 !function=plus_2 %3r_df_h 21:1 !function=plus_1 +%3r_df_w 21:1 !function=plus_2 @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i @@ -32,6 +33,7 @@ @2r ...... ........ df:2 ws:5 wd:5 ...... &msa_r wt=0 @2rf ...... ......... . ws:5 wd:5 ...... &msa_r wt=0 df=%2r_df_w @3rf_h ...... .... . wt:5 ws:5 wd:5 ...... &msa_r df=%3r_df_h +@3rf_w ...... .... . wt:5 ws:5 wd:5 ...... &msa_r df=%3r_df_w @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @s5 ...... ... df:2 sa:s5 ws:5 wd:5 ...... &msa_i @i8_df ...... df:2 sa:s8 ws:5 wd:5 ...... &msa_i @@ -86,9 +88,46 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + FCAF 011110 0000 . ..... ..... ..... 011010 @3rf_w + FCUN 011110 0001 . ..... ..... ..... 011010 @3rf_w + FCEQ 011110 0010 . ..... ..... ..... 011010 @3rf_w + FCUEQ 011110 0011 . ..... ..... ..... 011010 @3rf_w + FCLT 011110 0100 . ..... ..... ..... 011010 @3rf_w + FCULT 011110 0101 . ..... ..... ..... 011010 @3rf_w + FCLE 011110 0110 . ..... ..... ..... 011010 @3rf_w + FCULE 011110 0111 . ..... ..... ..... 011010 @3rf_w + FSAF 011110 1000 . ..... ..... ..... 011010 @3rf_w + FSUN 011110 1001 . ..... ..... ..... 011010 @3rf_w + FSEQ 011110 1010 . ..... ..... ..... 011010 @3rf_w + FSUEQ 011110 1011 . ..... ..... ..... 011010 @3rf_w + FSLT 011110 1100 . ..... ..... ..... 011010 @3rf_w + FSULT 011110 1101 . ..... ..... ..... 011010 @3rf_w + FSLE 011110 1110 . ..... ..... ..... 011010 @3rf_w + FSULE 011110 1111 . ..... ..... ..... 011010 @3rf_w + + FADD 011110 0000 . ..... ..... ..... 011011 @3rf_w + FSUB 011110 0001 . ..... ..... ..... 011011 @3rf_w + FMUL 011110 0010 . ..... ..... ..... 011011 @3rf_w + FDIV 011110 0011 . ..... ..... ..... 011011 @3rf_w + FMADD 011110 0100 . ..... ..... ..... 011011 @3rf_w + FMSUB 011110 0101 . ..... ..... ..... 011011 @3rf_w + FEXP2 011110 0111 . ..... ..... ..... 011011 @3rf_w + FEXDO 011110 1000 . ..... ..... ..... 011011 @3rf_w + FTQ 011110 1010 . ..... ..... ..... 011011 @3rf_w + FMIN 011110 1100 . ..... ..... ..... 011011 @3rf_w + FMIN_A 011110 1101 . ..... ..... ..... 011011 @3rf_w + FMAX 011110 1110 . ..... ..... ..... 011011 @3rf_w + FMAX_A 011110 1111 . ..... ..... ..... 011011 @3rf_w + + FCOR 011110 0001 . ..... ..... ..... 011100 @3rf_w + FCUNE 011110 0010 . ..... ..... ..... 011100 @3rf_w + FCNE 011110 0011 . ..... ..... ..... 011100 @3rf_w MUL_Q 011110 0100 . ..... ..... ..... 011100 @3rf_h MADD_Q 011110 0101 . ..... ..... ..... 011100 @3rf_h MSUB_Q 011110 0110 . ..... ..... ..... 011100 @3rf_h + FSOR 011110 1001 . ..... ..... ..... 011100 @3rf_w + FSUNE 011110 1010 . ..... ..... ..... 011100 @3rf_w + FSNE 011110 1011 . ..... ..... ..... 011100 @3rf_w MULR_Q 011110 1100 . ..... ..... ..... 011100 @3rf_h MADDR_Q 011110 1101 . ..... ..... ..... 011100 @3rf_h MSUBR_Q 011110 1110 . ..... ..... ..... 011100 @3rf_h diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 65e56b2317..26d05a87c8 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -47,9 +47,6 @@ enum { OPC_MSA_3R_14 = 0x14 | OPC_MSA, OPC_MSA_3R_15 = 0x15 | OPC_MSA, OPC_MSA_ELM = 0x19 | OPC_MSA, - OPC_MSA_3RF_1A = 0x1A | OPC_MSA, - OPC_MSA_3RF_1B = 0x1B | OPC_MSA, - OPC_MSA_3RF_1C = 0x1C | OPC_MSA, }; enum { @@ -128,43 +125,6 @@ enum { OPC_COPY_U_df = (0x3 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_INSERT_df = (0x4 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_INSVE_df = (0x5 << 22) | (0x00 << 16) | OPC_MSA_ELM, - - /* 3RF instruction _df(bit 21) = _w, _d */ - OPC_FCAF_df = (0x0 << 22) | OPC_MSA_3RF_1A, - OPC_FADD_df = (0x0 << 22) | OPC_MSA_3RF_1B, - OPC_FCUN_df = (0x1 << 22) | OPC_MSA_3RF_1A, - OPC_FSUB_df = (0x1 << 22) | OPC_MSA_3RF_1B, - OPC_FCOR_df = (0x1 << 22) | OPC_MSA_3RF_1C, - OPC_FCEQ_df = (0x2 << 22) | OPC_MSA_3RF_1A, - OPC_FMUL_df = (0x2 << 22) | OPC_MSA_3RF_1B, - OPC_FCUNE_df = (0x2 << 22) | OPC_MSA_3RF_1C, - OPC_FCUEQ_df = (0x3 << 22) | OPC_MSA_3RF_1A, - OPC_FDIV_df = (0x3 << 22) | OPC_MSA_3RF_1B, - OPC_FCNE_df = (0x3 << 22) | OPC_MSA_3RF_1C, - OPC_FCLT_df = (0x4 << 22) | OPC_MSA_3RF_1A, - OPC_FMADD_df = (0x4 << 22) | OPC_MSA_3RF_1B, - OPC_FCULT_df = (0x5 << 22) | OPC_MSA_3RF_1A, - OPC_FMSUB_df = (0x5 << 22) | OPC_MSA_3RF_1B, - OPC_FCLE_df = (0x6 << 22) | OPC_MSA_3RF_1A, - OPC_FCULE_df = (0x7 << 22) | OPC_MSA_3RF_1A, - OPC_FEXP2_df = (0x7 << 22) | OPC_MSA_3RF_1B, - OPC_FSAF_df = (0x8 << 22) | OPC_MSA_3RF_1A, - OPC_FEXDO_df = (0x8 << 22) | OPC_MSA_3RF_1B, - OPC_FSUN_df = (0x9 << 22) | OPC_MSA_3RF_1A, - OPC_FSOR_df = (0x9 << 22) | OPC_MSA_3RF_1C, - OPC_FSEQ_df = (0xA << 22) | OPC_MSA_3RF_1A, - OPC_FTQ_df = (0xA << 22) | OPC_MSA_3RF_1B, - OPC_FSUNE_df = (0xA << 22) | OPC_MSA_3RF_1C, - OPC_FSUEQ_df = (0xB << 22) | OPC_MSA_3RF_1A, - OPC_FSNE_df = (0xB << 22) | OPC_MSA_3RF_1C, - OPC_FSLT_df = (0xC << 22) | OPC_MSA_3RF_1A, - OPC_FMIN_df = (0xC << 22) | OPC_MSA_3RF_1B, - OPC_FSULT_df = (0xD << 22) | OPC_MSA_3RF_1A, - OPC_FMIN_A_df = (0xD << 22) | OPC_MSA_3RF_1B, - OPC_FSLE_df = (0xE << 22) | OPC_MSA_3RF_1A, - OPC_FMAX_df = (0xE << 22) | OPC_MSA_3RF_1B, - OPC_FSULE_df = (0xF << 22) | OPC_MSA_3RF_1A, - OPC_FMAX_A_df = (0xF << 22) | OPC_MSA_3RF_1B, }; static const char msaregnames[][6] = { @@ -1697,144 +1657,50 @@ static void gen_msa_elm(DisasContext *ctx) gen_msa_elm_df(ctx, df, n); } +TRANS(FCAF, trans_msa_3rf, gen_helper_msa_fcaf_df); +TRANS(FCUN, trans_msa_3rf, gen_helper_msa_fcun_df); +TRANS(FCEQ, trans_msa_3rf, gen_helper_msa_fceq_df); +TRANS(FCUEQ, trans_msa_3rf, gen_helper_msa_fcueq_df); +TRANS(FCLT, trans_msa_3rf, gen_helper_msa_fclt_df); +TRANS(FCULT, trans_msa_3rf, gen_helper_msa_fcult_df); +TRANS(FCLE, trans_msa_3rf, gen_helper_msa_fcle_df); +TRANS(FCULE, trans_msa_3rf, gen_helper_msa_fcule_df); +TRANS(FSAF, trans_msa_3rf, gen_helper_msa_fsaf_df); +TRANS(FSUN, trans_msa_3rf, gen_helper_msa_fsun_df); +TRANS(FSEQ, trans_msa_3rf, gen_helper_msa_fseq_df); +TRANS(FSUEQ, trans_msa_3rf, gen_helper_msa_fsueq_df); +TRANS(FSLT, trans_msa_3rf, gen_helper_msa_fslt_df); +TRANS(FSULT, trans_msa_3rf, gen_helper_msa_fsult_df); +TRANS(FSLE, trans_msa_3rf, gen_helper_msa_fsle_df); +TRANS(FSULE, trans_msa_3rf, gen_helper_msa_fsule_df); + +TRANS(FADD, trans_msa_3rf, gen_helper_msa_fadd_df); +TRANS(FSUB, trans_msa_3rf, gen_helper_msa_fsub_df); +TRANS(FMUL, trans_msa_3rf, gen_helper_msa_fmul_df); +TRANS(FDIV, trans_msa_3rf, gen_helper_msa_fdiv_df); +TRANS(FMADD, trans_msa_3rf, gen_helper_msa_fmadd_df); +TRANS(FMSUB, trans_msa_3rf, gen_helper_msa_fmsub_df); +TRANS(FEXP2, trans_msa_3rf, gen_helper_msa_fexp2_df); +TRANS(FEXDO, trans_msa_3rf, gen_helper_msa_fexdo_df); +TRANS(FTQ, trans_msa_3rf, gen_helper_msa_ftq_df); +TRANS(FMIN, trans_msa_3rf, gen_helper_msa_fmin_df); +TRANS(FMIN_A, trans_msa_3rf, gen_helper_msa_fmin_a_df); +TRANS(FMAX, trans_msa_3rf, gen_helper_msa_fmax_df); +TRANS(FMAX_A, trans_msa_3rf, gen_helper_msa_fmax_a_df); + +TRANS(FCOR, trans_msa_3rf, gen_helper_msa_fcor_df); +TRANS(FCUNE, trans_msa_3rf, gen_helper_msa_fcune_df); +TRANS(FCNE, trans_msa_3rf, gen_helper_msa_fcne_df); TRANS(MUL_Q, trans_msa_3rf, gen_helper_msa_mul_q_df); TRANS(MADD_Q, trans_msa_3rf, gen_helper_msa_madd_q_df); TRANS(MSUB_Q, trans_msa_3rf, gen_helper_msa_msub_q_df); +TRANS(FSOR, trans_msa_3rf, gen_helper_msa_fsor_df); +TRANS(FSUNE, trans_msa_3rf, gen_helper_msa_fsune_df); +TRANS(FSNE, trans_msa_3rf, gen_helper_msa_fsne_df); TRANS(MULR_Q, trans_msa_3rf, gen_helper_msa_mulr_q_df); TRANS(MADDR_Q, trans_msa_3rf, gen_helper_msa_maddr_q_df); TRANS(MSUBR_Q, trans_msa_3rf, gen_helper_msa_msubr_q_df); -static void gen_msa_3rf(DisasContext *ctx) -{ -#define MASK_MSA_3RF(op) (MASK_MSA_MINOR(op) | (op & (0xf << 22))) - uint8_t df = (ctx->opcode >> 21) & 0x1; - uint8_t wt = (ctx->opcode >> 16) & 0x1f; - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; - - TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 tws = tcg_const_i32(ws); - TCGv_i32 twt = tcg_const_i32(wt); - /* adjust df value for floating-point instruction */ - TCGv_i32 tdf = tcg_constant_i32(DF_WORD + df); - - switch (MASK_MSA_3RF(ctx->opcode)) { - case OPC_FCAF_df: - gen_helper_msa_fcaf_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FADD_df: - gen_helper_msa_fadd_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCUN_df: - gen_helper_msa_fcun_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSUB_df: - gen_helper_msa_fsub_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCOR_df: - gen_helper_msa_fcor_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCEQ_df: - gen_helper_msa_fceq_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FMUL_df: - gen_helper_msa_fmul_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCUNE_df: - gen_helper_msa_fcune_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCUEQ_df: - gen_helper_msa_fcueq_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FDIV_df: - gen_helper_msa_fdiv_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCNE_df: - gen_helper_msa_fcne_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCLT_df: - gen_helper_msa_fclt_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FMADD_df: - gen_helper_msa_fmadd_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCULT_df: - gen_helper_msa_fcult_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FMSUB_df: - gen_helper_msa_fmsub_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCLE_df: - gen_helper_msa_fcle_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FCULE_df: - gen_helper_msa_fcule_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FEXP2_df: - gen_helper_msa_fexp2_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSAF_df: - gen_helper_msa_fsaf_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FEXDO_df: - gen_helper_msa_fexdo_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSUN_df: - gen_helper_msa_fsun_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSOR_df: - gen_helper_msa_fsor_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSEQ_df: - gen_helper_msa_fseq_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FTQ_df: - gen_helper_msa_ftq_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSUNE_df: - gen_helper_msa_fsune_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSUEQ_df: - gen_helper_msa_fsueq_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSNE_df: - gen_helper_msa_fsne_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSLT_df: - gen_helper_msa_fslt_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FMIN_df: - gen_helper_msa_fmin_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSULT_df: - gen_helper_msa_fsult_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FMIN_A_df: - gen_helper_msa_fmin_a_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSLE_df: - gen_helper_msa_fsle_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FMAX_df: - gen_helper_msa_fmax_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FSULE_df: - gen_helper_msa_fsule_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_FMAX_A_df: - gen_helper_msa_fmax_a_df(cpu_env, tdf, twd, tws, twt); - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; - } - - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); - tcg_temp_free_i32(twt); -} - static bool trans_msa_2r(DisasContext *ctx, arg_msa_r *a, gen_helper_pii *gen_msa_2r) { @@ -1925,11 +1791,6 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) case OPC_MSA_ELM: gen_msa_elm(ctx); break; - case OPC_MSA_3RF_1A: - case OPC_MSA_3RF_1B: - case OPC_MSA_3RF_1C: - gen_msa_3rf(ctx); - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); From 67bedef51aa1144975c619f8559848819cdc309a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 13:27:51 +0200 Subject: [PATCH 1059/1334] target/mips: Convert MSA 3R instruction format to decodetree (part 1/4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 3-register operations to decodetree. Since the 'data format' field is a constant value, use tcg_constant_i32() instead of a TCG temporary. Note, the format definition could be named @3rf_b (for 3R with a df field BYTE-based) but since the instruction class is named '3R', we simply call the format @3r to ease reviewing the msa.decode file. However we directly call the trans_msa_3rf() function, which handles the BYTE-based df field. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-21-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 6 ++++++ target/mips/tcg/msa_translate.c | 17 +++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 1d6ada4c14..4b14acce26 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -32,6 +32,7 @@ @vec ...... ..... wt:5 ws:5 wd:5 ...... &msa_r df=0 @2r ...... ........ df:2 ws:5 wd:5 ...... &msa_r wt=0 @2rf ...... ......... . ws:5 wd:5 ...... &msa_r wt=0 df=%2r_df_w +@3r ...... ... df:2 wt:5 ws:5 wd:5 ...... &msa_r @3rf_h ...... .... . wt:5 ws:5 wd:5 ...... &msa_r df=%3r_df_h @3rf_w ...... .... . wt:5 ws:5 wd:5 ...... &msa_r df=%3r_df_w @u5 ...... ... df:2 sa:5 ws:5 wd:5 ...... &msa_i @@ -88,6 +89,11 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + SLD 011110 000 .. ..... ..... ..... 010100 @3r + SPLAT 011110 001 .. ..... ..... ..... 010100 @3r + + VSHF 011110 000 .. ..... ..... ..... 010101 @3r + FCAF 011110 0000 . ..... ..... ..... 011010 @3rf_w FCUN 011110 0001 . ..... ..... ..... 011010 @3rf_w FCEQ 011110 0010 . ..... ..... ..... 011010 @3rf_w diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 26d05a87c8..ddc0bd08dd 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -58,15 +58,12 @@ enum { OPC_SUBS_S_df = (0x0 << 23) | OPC_MSA_3R_11, OPC_MULV_df = (0x0 << 23) | OPC_MSA_3R_12, OPC_DOTP_S_df = (0x0 << 23) | OPC_MSA_3R_13, - OPC_SLD_df = (0x0 << 23) | OPC_MSA_3R_14, - OPC_VSHF_df = (0x0 << 23) | OPC_MSA_3R_15, OPC_SRA_df = (0x1 << 23) | OPC_MSA_3R_0D, OPC_SUBV_df = (0x1 << 23) | OPC_MSA_3R_0E, OPC_ADDS_A_df = (0x1 << 23) | OPC_MSA_3R_10, OPC_SUBS_U_df = (0x1 << 23) | OPC_MSA_3R_11, OPC_MADDV_df = (0x1 << 23) | OPC_MSA_3R_12, OPC_DOTP_U_df = (0x1 << 23) | OPC_MSA_3R_13, - OPC_SPLAT_df = (0x1 << 23) | OPC_MSA_3R_14, OPC_SRAR_df = (0x1 << 23) | OPC_MSA_3R_15, OPC_SRL_df = (0x2 << 23) | OPC_MSA_3R_0D, OPC_MAX_S_df = (0x2 << 23) | OPC_MSA_3R_0E, @@ -505,6 +502,11 @@ TRANS(BMNZ_V, trans_msa_3r, gen_helper_msa_bmnz_v); TRANS(BMZ_V, trans_msa_3r, gen_helper_msa_bmz_v); TRANS(BSEL_V, trans_msa_3r, gen_helper_msa_bsel_v); +TRANS(SLD, trans_msa_3rf, gen_helper_msa_sld_df); +TRANS(SPLAT, trans_msa_3rf, gen_helper_msa_splat_df); + +TRANS(VSHF, trans_msa_3rf, gen_helper_msa_vshf_df); + static void gen_msa_3r(DisasContext *ctx) { #define MASK_MSA_3R(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) @@ -1255,12 +1257,6 @@ static void gen_msa_3r(DisasContext *ctx) break; } break; - case OPC_SLD_df: - gen_helper_msa_sld_df(cpu_env, tdf, twd, tws, twt); - break; - case OPC_VSHF_df: - gen_helper_msa_vshf_df(cpu_env, tdf, twd, tws, twt); - break; case OPC_SUBV_df: switch (df) { case DF_BYTE: @@ -1293,9 +1289,6 @@ static void gen_msa_3r(DisasContext *ctx) break; } break; - case OPC_SPLAT_df: - gen_helper_msa_splat_df(cpu_env, tdf, twd, tws, twt); - break; case OPC_SUBSUS_U_df: switch (df) { case DF_BYTE: From f18708a53adc0549526fbeba5d1e892c0c4bf49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 13:51:07 +0200 Subject: [PATCH 1060/1334] target/mips: Convert MSA 3R instruction format to decodetree (part 2/4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 3-register operations to decodetree. Per the Encoding of Operation Field for 3R Instruction Format' (Table 3.25), these instructions are not defined for the BYTE format. Therefore the TRANS_DF_iii_b() macro returns 'false' in that case, because no such instruction is decoded. Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211028210843.2120802-22-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 11 ++ target/mips/tcg/msa_translate.c | 182 +++++--------------------------- 2 files changed, 35 insertions(+), 158 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 4b14acce26..0e2f474cde 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -89,10 +89,21 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + DOTP_S 011110 000.. ..... ..... ..... 010011 @3r + DOTP_U 011110 001.. ..... ..... ..... 010011 @3r + DPADD_S 011110 010.. ..... ..... ..... 010011 @3r + DPADD_U 011110 011.. ..... ..... ..... 010011 @3r + DPSUB_S 011110 100.. ..... ..... ..... 010011 @3r + DPSUB_U 011110 101.. ..... ..... ..... 010011 @3r + SLD 011110 000 .. ..... ..... ..... 010100 @3r SPLAT 011110 001 .. ..... ..... ..... 010100 @3r VSHF 011110 000 .. ..... ..... ..... 010101 @3r + HADD_S 011110 100.. ..... ..... ..... 010101 @3r + HADD_U 011110 101.. ..... ..... ..... 010101 @3r + HSUB_S 011110 110.. ..... ..... ..... 010101 @3r + HSUB_U 011110 111.. ..... ..... ..... 010101 @3r FCAF 011110 0000 . ..... ..... ..... 011010 @3rf_w FCUN 011110 0001 . ..... ..... ..... 011010 @3rf_w diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index ddc0bd08dd..5f3e1573e4 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -57,13 +57,11 @@ enum { OPC_ADD_A_df = (0x0 << 23) | OPC_MSA_3R_10, OPC_SUBS_S_df = (0x0 << 23) | OPC_MSA_3R_11, OPC_MULV_df = (0x0 << 23) | OPC_MSA_3R_12, - OPC_DOTP_S_df = (0x0 << 23) | OPC_MSA_3R_13, OPC_SRA_df = (0x1 << 23) | OPC_MSA_3R_0D, OPC_SUBV_df = (0x1 << 23) | OPC_MSA_3R_0E, OPC_ADDS_A_df = (0x1 << 23) | OPC_MSA_3R_10, OPC_SUBS_U_df = (0x1 << 23) | OPC_MSA_3R_11, OPC_MADDV_df = (0x1 << 23) | OPC_MSA_3R_12, - OPC_DOTP_U_df = (0x1 << 23) | OPC_MSA_3R_13, OPC_SRAR_df = (0x1 << 23) | OPC_MSA_3R_15, OPC_SRL_df = (0x2 << 23) | OPC_MSA_3R_0D, OPC_MAX_S_df = (0x2 << 23) | OPC_MSA_3R_0E, @@ -71,7 +69,6 @@ enum { OPC_ADDS_S_df = (0x2 << 23) | OPC_MSA_3R_10, OPC_SUBSUS_U_df = (0x2 << 23) | OPC_MSA_3R_11, OPC_MSUBV_df = (0x2 << 23) | OPC_MSA_3R_12, - OPC_DPADD_S_df = (0x2 << 23) | OPC_MSA_3R_13, OPC_PCKEV_df = (0x2 << 23) | OPC_MSA_3R_14, OPC_SRLR_df = (0x2 << 23) | OPC_MSA_3R_15, OPC_BCLR_df = (0x3 << 23) | OPC_MSA_3R_0D, @@ -79,7 +76,6 @@ enum { OPC_CLT_U_df = (0x3 << 23) | OPC_MSA_3R_0F, OPC_ADDS_U_df = (0x3 << 23) | OPC_MSA_3R_10, OPC_SUBSUU_S_df = (0x3 << 23) | OPC_MSA_3R_11, - OPC_DPADD_U_df = (0x3 << 23) | OPC_MSA_3R_13, OPC_PCKOD_df = (0x3 << 23) | OPC_MSA_3R_14, OPC_BSET_df = (0x4 << 23) | OPC_MSA_3R_0D, OPC_MIN_S_df = (0x4 << 23) | OPC_MSA_3R_0E, @@ -87,30 +83,24 @@ enum { OPC_AVE_S_df = (0x4 << 23) | OPC_MSA_3R_10, OPC_ASUB_S_df = (0x4 << 23) | OPC_MSA_3R_11, OPC_DIV_S_df = (0x4 << 23) | OPC_MSA_3R_12, - OPC_DPSUB_S_df = (0x4 << 23) | OPC_MSA_3R_13, OPC_ILVL_df = (0x4 << 23) | OPC_MSA_3R_14, - OPC_HADD_S_df = (0x4 << 23) | OPC_MSA_3R_15, OPC_BNEG_df = (0x5 << 23) | OPC_MSA_3R_0D, OPC_MIN_U_df = (0x5 << 23) | OPC_MSA_3R_0E, OPC_CLE_U_df = (0x5 << 23) | OPC_MSA_3R_0F, OPC_AVE_U_df = (0x5 << 23) | OPC_MSA_3R_10, OPC_ASUB_U_df = (0x5 << 23) | OPC_MSA_3R_11, OPC_DIV_U_df = (0x5 << 23) | OPC_MSA_3R_12, - OPC_DPSUB_U_df = (0x5 << 23) | OPC_MSA_3R_13, OPC_ILVR_df = (0x5 << 23) | OPC_MSA_3R_14, - OPC_HADD_U_df = (0x5 << 23) | OPC_MSA_3R_15, OPC_BINSL_df = (0x6 << 23) | OPC_MSA_3R_0D, OPC_MAX_A_df = (0x6 << 23) | OPC_MSA_3R_0E, OPC_AVER_S_df = (0x6 << 23) | OPC_MSA_3R_10, OPC_MOD_S_df = (0x6 << 23) | OPC_MSA_3R_12, OPC_ILVEV_df = (0x6 << 23) | OPC_MSA_3R_14, - OPC_HSUB_S_df = (0x6 << 23) | OPC_MSA_3R_15, OPC_BINSR_df = (0x7 << 23) | OPC_MSA_3R_0D, OPC_MIN_A_df = (0x7 << 23) | OPC_MSA_3R_0E, OPC_AVER_U_df = (0x7 << 23) | OPC_MSA_3R_10, OPC_MOD_U_df = (0x7 << 23) | OPC_MSA_3R_12, OPC_ILVOD_df = (0x7 << 23) | OPC_MSA_3R_14, - OPC_HSUB_U_df = (0x7 << 23) | OPC_MSA_3R_15, /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ OPC_SLDI_df = (0x0 << 22) | (0x00 << 16) | OPC_MSA_ELM, @@ -255,6 +245,15 @@ typedef void gen_helper_piiii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); #define TRANS_DF_ii(NAME, trans_func, gen_func) \ TRANS_DF_x(ii, NAME, trans_func, gen_func) +#define TRANS_DF_iii_b(NAME, trans_func, gen_func) \ + static gen_helper_piii * const NAME##_tab[4] = { \ + NULL, gen_func##_h, gen_func##_w, gen_func##_d \ + }; \ + static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ + { \ + return trans_func(ctx, a, NAME##_tab[a->df]); \ + } + static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, TCGCond cond) { @@ -482,6 +481,10 @@ static bool trans_msa_3rf(DisasContext *ctx, arg_msa_r *a, static bool trans_msa_3r(DisasContext *ctx, arg_msa_r *a, gen_helper_piii *gen_msa_3r) { + if (!gen_msa_3r) { + return false; + } + if (!check_msa_enabled(ctx)) { return true; } @@ -502,10 +505,21 @@ TRANS(BMNZ_V, trans_msa_3r, gen_helper_msa_bmnz_v); TRANS(BMZ_V, trans_msa_3r, gen_helper_msa_bmz_v); TRANS(BSEL_V, trans_msa_3r, gen_helper_msa_bsel_v); +TRANS_DF_iii_b(DOTP_S, trans_msa_3r, gen_helper_msa_dotp_s); +TRANS_DF_iii_b(DOTP_U, trans_msa_3r, gen_helper_msa_dotp_u); +TRANS_DF_iii_b(DPADD_S, trans_msa_3r, gen_helper_msa_dpadd_s); +TRANS_DF_iii_b(DPADD_U, trans_msa_3r, gen_helper_msa_dpadd_u); +TRANS_DF_iii_b(DPSUB_S, trans_msa_3r, gen_helper_msa_dpsub_s); +TRANS_DF_iii_b(DPSUB_U, trans_msa_3r, gen_helper_msa_dpsub_u); + TRANS(SLD, trans_msa_3rf, gen_helper_msa_sld_df); TRANS(SPLAT, trans_msa_3rf, gen_helper_msa_splat_df); TRANS(VSHF, trans_msa_3rf, gen_helper_msa_vshf_df); +TRANS_DF_iii_b(HADD_S, trans_msa_3r, gen_helper_msa_hadd_s); +TRANS_DF_iii_b(HADD_U, trans_msa_3r, gen_helper_msa_hadd_u); +TRANS_DF_iii_b(HSUB_S, trans_msa_3r, gen_helper_msa_hsub_s); +TRANS_DF_iii_b(HSUB_U, trans_msa_3r, gen_helper_msa_hsub_u); static void gen_msa_3r(DisasContext *ctx) { @@ -1321,154 +1335,6 @@ static void gen_msa_3r(DisasContext *ctx) break; } break; - - case OPC_DOTP_S_df: - case OPC_DOTP_U_df: - case OPC_DPADD_S_df: - case OPC_DPADD_U_df: - case OPC_DPSUB_S_df: - case OPC_HADD_S_df: - case OPC_DPSUB_U_df: - case OPC_HADD_U_df: - case OPC_HSUB_S_df: - case OPC_HSUB_U_df: - if (df == DF_BYTE) { - gen_reserved_instruction(ctx); - break; - } - switch (MASK_MSA_3R(ctx->opcode)) { - case OPC_HADD_S_df: - switch (df) { - case DF_HALF: - gen_helper_msa_hadd_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_hadd_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_hadd_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_HADD_U_df: - switch (df) { - case DF_HALF: - gen_helper_msa_hadd_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_hadd_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_hadd_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_HSUB_S_df: - switch (df) { - case DF_HALF: - gen_helper_msa_hsub_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_hsub_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_hsub_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_HSUB_U_df: - switch (df) { - case DF_HALF: - gen_helper_msa_hsub_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_hsub_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_hsub_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_DOTP_S_df: - switch (df) { - case DF_HALF: - gen_helper_msa_dotp_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_dotp_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_dotp_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_DOTP_U_df: - switch (df) { - case DF_HALF: - gen_helper_msa_dotp_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_dotp_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_dotp_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_DPADD_S_df: - switch (df) { - case DF_HALF: - gen_helper_msa_dpadd_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_dpadd_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_dpadd_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_DPADD_U_df: - switch (df) { - case DF_HALF: - gen_helper_msa_dpadd_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_dpadd_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_dpadd_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_DPSUB_S_df: - switch (df) { - case DF_HALF: - gen_helper_msa_dpsub_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_dpsub_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_dpsub_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_DPSUB_U_df: - switch (df) { - case DF_HALF: - gen_helper_msa_dpsub_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_dpsub_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_dpsub_u_d(cpu_env, twd, tws, twt); - break; - } - break; - } - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); From c79db8c239fb4272de3cd0741c0ecfd549d5588a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 14:22:32 +0200 Subject: [PATCH 1061/1334] target/mips: Convert MSA 3R instruction format to decodetree (part 3/4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert BINSL (Vector Bit Insert Left) and BINSR (Vector Bit Insert Right) opcodes to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-23-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 3 +++ target/mips/tcg/msa_translate.c | 40 +++++---------------------------- 2 files changed, 9 insertions(+), 34 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 0e2f474cde..f2bacbaea8 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -89,6 +89,9 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + BINSL 011110 110.. ..... ..... ..... 001101 @3r + BINSR 011110 111.. ..... ..... ..... 001101 @3r + DOTP_S 011110 000.. ..... ..... ..... 010011 @3r DOTP_U 011110 001.. ..... ..... ..... 010011 @3r DPADD_S 011110 010.. ..... ..... ..... 010011 @3r diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 5f3e1573e4..c52913632c 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -91,12 +91,10 @@ enum { OPC_ASUB_U_df = (0x5 << 23) | OPC_MSA_3R_11, OPC_DIV_U_df = (0x5 << 23) | OPC_MSA_3R_12, OPC_ILVR_df = (0x5 << 23) | OPC_MSA_3R_14, - OPC_BINSL_df = (0x6 << 23) | OPC_MSA_3R_0D, OPC_MAX_A_df = (0x6 << 23) | OPC_MSA_3R_0E, OPC_AVER_S_df = (0x6 << 23) | OPC_MSA_3R_10, OPC_MOD_S_df = (0x6 << 23) | OPC_MSA_3R_12, OPC_ILVEV_df = (0x6 << 23) | OPC_MSA_3R_14, - OPC_BINSR_df = (0x7 << 23) | OPC_MSA_3R_0D, OPC_MIN_A_df = (0x7 << 23) | OPC_MSA_3R_0E, OPC_AVER_U_df = (0x7 << 23) | OPC_MSA_3R_10, OPC_MOD_U_df = (0x7 << 23) | OPC_MSA_3R_12, @@ -245,6 +243,9 @@ typedef void gen_helper_piiii(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); #define TRANS_DF_ii(NAME, trans_func, gen_func) \ TRANS_DF_x(ii, NAME, trans_func, gen_func) +#define TRANS_DF_iii(NAME, trans_func, gen_func) \ + TRANS_DF_x(iii, NAME, trans_func, gen_func) + #define TRANS_DF_iii_b(NAME, trans_func, gen_func) \ static gen_helper_piii * const NAME##_tab[4] = { \ NULL, gen_func##_h, gen_func##_w, gen_func##_d \ @@ -505,6 +506,9 @@ TRANS(BMNZ_V, trans_msa_3r, gen_helper_msa_bmnz_v); TRANS(BMZ_V, trans_msa_3r, gen_helper_msa_bmz_v); TRANS(BSEL_V, trans_msa_3r, gen_helper_msa_bsel_v); +TRANS_DF_iii(BINSL, trans_msa_3r, gen_helper_msa_binsl); +TRANS_DF_iii(BINSR, trans_msa_3r, gen_helper_msa_binsr); + TRANS_DF_iii_b(DOTP_S, trans_msa_3r, gen_helper_msa_dotp_s); TRANS_DF_iii_b(DOTP_U, trans_msa_3r, gen_helper_msa_dotp_u); TRANS_DF_iii_b(DPADD_S, trans_msa_3r, gen_helper_msa_dpadd_s); @@ -535,38 +539,6 @@ static void gen_msa_3r(DisasContext *ctx) TCGv_i32 twt = tcg_const_i32(wt); switch (MASK_MSA_3R(ctx->opcode)) { - case OPC_BINSL_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_binsl_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_binsl_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_binsl_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_binsl_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_BINSR_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_binsr_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_binsr_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_binsr_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_binsr_d(cpu_env, twd, tws, twt); - break; - } - break; case OPC_BCLR_df: switch (df) { case DF_BYTE: From 0a086d2e80d3214b56f672d2f976525e46f1b476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 15:49:52 +0200 Subject: [PATCH 1062/1334] target/mips: Convert MSA 3R instruction format to decodetree (part 4/4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 3-register operations to decodetree. Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-24-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 53 ++ target/mips/tcg/msa_translate.c | 916 ++------------------------------ 2 files changed, 106 insertions(+), 863 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index f2bacbaea8..391261109a 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -89,9 +89,54 @@ BNZ 010001 111 .. ..... ................ @bz SRARI 011110 010 ....... ..... ..... 001010 @bit SRLRI 011110 011 ....... ..... ..... 001010 @bit + SLL 011110 000.. ..... ..... ..... 001101 @3r + SRA 011110 001.. ..... ..... ..... 001101 @3r + SRL 011110 010.. ..... ..... ..... 001101 @3r + BCLR 011110 011.. ..... ..... ..... 001101 @3r + BSET 011110 100.. ..... ..... ..... 001101 @3r + BNEG 011110 101.. ..... ..... ..... 001101 @3r BINSL 011110 110.. ..... ..... ..... 001101 @3r BINSR 011110 111.. ..... ..... ..... 001101 @3r + ADDV 011110 000.. ..... ..... ..... 001110 @3r + SUBV 011110 001.. ..... ..... ..... 001110 @3r + MAX_S 011110 010.. ..... ..... ..... 001110 @3r + MAX_U 011110 011.. ..... ..... ..... 001110 @3r + MIN_S 011110 100.. ..... ..... ..... 001110 @3r + MIN_U 011110 101.. ..... ..... ..... 001110 @3r + MAX_A 011110 110.. ..... ..... ..... 001110 @3r + MIN_A 011110 111.. ..... ..... ..... 001110 @3r + + CEQ 011110 000.. ..... ..... ..... 001111 @3r + CLT_S 011110 010.. ..... ..... ..... 001111 @3r + CLT_U 011110 011.. ..... ..... ..... 001111 @3r + CLE_S 011110 100.. ..... ..... ..... 001111 @3r + CLE_U 011110 101.. ..... ..... ..... 001111 @3r + + ADD_A 011110 000.. ..... ..... ..... 010000 @3r + ADDS_A 011110 001.. ..... ..... ..... 010000 @3r + ADDS_S 011110 010.. ..... ..... ..... 010000 @3r + ADDS_U 011110 011.. ..... ..... ..... 010000 @3r + AVE_S 011110 100.. ..... ..... ..... 010000 @3r + AVE_U 011110 101.. ..... ..... ..... 010000 @3r + AVER_S 011110 110.. ..... ..... ..... 010000 @3r + AVER_U 011110 111.. ..... ..... ..... 010000 @3r + + SUBS_S 011110 000.. ..... ..... ..... 010001 @3r + SUBS_U 011110 001.. ..... ..... ..... 010001 @3r + SUBSUS_U 011110 010.. ..... ..... ..... 010001 @3r + SUBSUU_S 011110 011.. ..... ..... ..... 010001 @3r + ASUB_S 011110 100.. ..... ..... ..... 010001 @3r + ASUB_U 011110 101.. ..... ..... ..... 010001 @3r + + MULV 011110 000.. ..... ..... ..... 010010 @3r + MADDV 011110 001.. ..... ..... ..... 010010 @3r + MSUBV 011110 010.. ..... ..... ..... 010010 @3r + DIV_S 011110 100.. ..... ..... ..... 010010 @3r + DIV_U 011110 101.. ..... ..... ..... 010010 @3r + MOD_S 011110 110.. ..... ..... ..... 010010 @3r + MOD_U 011110 111.. ..... ..... ..... 010010 @3r + DOTP_S 011110 000.. ..... ..... ..... 010011 @3r DOTP_U 011110 001.. ..... ..... ..... 010011 @3r DPADD_S 011110 010.. ..... ..... ..... 010011 @3r @@ -101,8 +146,16 @@ BNZ 010001 111 .. ..... ................ @bz SLD 011110 000 .. ..... ..... ..... 010100 @3r SPLAT 011110 001 .. ..... ..... ..... 010100 @3r + PCKEV 011110 010 .. ..... ..... ..... 010100 @3r + PCKOD 011110 011 .. ..... ..... ..... 010100 @3r + ILVL 011110 100 .. ..... ..... ..... 010100 @3r + ILVR 011110 101 .. ..... ..... ..... 010100 @3r + ILVEV 011110 110 .. ..... ..... ..... 010100 @3r + ILVOD 011110 111 .. ..... ..... ..... 010100 @3r VSHF 011110 000 .. ..... ..... ..... 010101 @3r + SRAR 011110 001 .. ..... ..... ..... 010101 @3r + SRLR 011110 010 .. ..... ..... ..... 010101 @3r HADD_S 011110 100.. ..... ..... ..... 010101 @3r HADD_U 011110 101.. ..... ..... ..... 010101 @3r HSUB_S 011110 110.. ..... ..... ..... 010101 @3r diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index c52913632c..3b95e081a0 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -37,69 +37,10 @@ static inline int plus_2(DisasContext *s, int x) #define MASK_MSA_MINOR(op) (MASK_OP_MAJOR(op) | (op & 0x3F)) enum { - OPC_MSA_3R_0D = 0x0D | OPC_MSA, - OPC_MSA_3R_0E = 0x0E | OPC_MSA, - OPC_MSA_3R_0F = 0x0F | OPC_MSA, - OPC_MSA_3R_10 = 0x10 | OPC_MSA, - OPC_MSA_3R_11 = 0x11 | OPC_MSA, - OPC_MSA_3R_12 = 0x12 | OPC_MSA, - OPC_MSA_3R_13 = 0x13 | OPC_MSA, - OPC_MSA_3R_14 = 0x14 | OPC_MSA, - OPC_MSA_3R_15 = 0x15 | OPC_MSA, OPC_MSA_ELM = 0x19 | OPC_MSA, }; enum { - /* 3R instruction df(bits 22..21) = _b, _h, _w, d */ - OPC_SLL_df = (0x0 << 23) | OPC_MSA_3R_0D, - OPC_ADDV_df = (0x0 << 23) | OPC_MSA_3R_0E, - OPC_CEQ_df = (0x0 << 23) | OPC_MSA_3R_0F, - OPC_ADD_A_df = (0x0 << 23) | OPC_MSA_3R_10, - OPC_SUBS_S_df = (0x0 << 23) | OPC_MSA_3R_11, - OPC_MULV_df = (0x0 << 23) | OPC_MSA_3R_12, - OPC_SRA_df = (0x1 << 23) | OPC_MSA_3R_0D, - OPC_SUBV_df = (0x1 << 23) | OPC_MSA_3R_0E, - OPC_ADDS_A_df = (0x1 << 23) | OPC_MSA_3R_10, - OPC_SUBS_U_df = (0x1 << 23) | OPC_MSA_3R_11, - OPC_MADDV_df = (0x1 << 23) | OPC_MSA_3R_12, - OPC_SRAR_df = (0x1 << 23) | OPC_MSA_3R_15, - OPC_SRL_df = (0x2 << 23) | OPC_MSA_3R_0D, - OPC_MAX_S_df = (0x2 << 23) | OPC_MSA_3R_0E, - OPC_CLT_S_df = (0x2 << 23) | OPC_MSA_3R_0F, - OPC_ADDS_S_df = (0x2 << 23) | OPC_MSA_3R_10, - OPC_SUBSUS_U_df = (0x2 << 23) | OPC_MSA_3R_11, - OPC_MSUBV_df = (0x2 << 23) | OPC_MSA_3R_12, - OPC_PCKEV_df = (0x2 << 23) | OPC_MSA_3R_14, - OPC_SRLR_df = (0x2 << 23) | OPC_MSA_3R_15, - OPC_BCLR_df = (0x3 << 23) | OPC_MSA_3R_0D, - OPC_MAX_U_df = (0x3 << 23) | OPC_MSA_3R_0E, - OPC_CLT_U_df = (0x3 << 23) | OPC_MSA_3R_0F, - OPC_ADDS_U_df = (0x3 << 23) | OPC_MSA_3R_10, - OPC_SUBSUU_S_df = (0x3 << 23) | OPC_MSA_3R_11, - OPC_PCKOD_df = (0x3 << 23) | OPC_MSA_3R_14, - OPC_BSET_df = (0x4 << 23) | OPC_MSA_3R_0D, - OPC_MIN_S_df = (0x4 << 23) | OPC_MSA_3R_0E, - OPC_CLE_S_df = (0x4 << 23) | OPC_MSA_3R_0F, - OPC_AVE_S_df = (0x4 << 23) | OPC_MSA_3R_10, - OPC_ASUB_S_df = (0x4 << 23) | OPC_MSA_3R_11, - OPC_DIV_S_df = (0x4 << 23) | OPC_MSA_3R_12, - OPC_ILVL_df = (0x4 << 23) | OPC_MSA_3R_14, - OPC_BNEG_df = (0x5 << 23) | OPC_MSA_3R_0D, - OPC_MIN_U_df = (0x5 << 23) | OPC_MSA_3R_0E, - OPC_CLE_U_df = (0x5 << 23) | OPC_MSA_3R_0F, - OPC_AVE_U_df = (0x5 << 23) | OPC_MSA_3R_10, - OPC_ASUB_U_df = (0x5 << 23) | OPC_MSA_3R_11, - OPC_DIV_U_df = (0x5 << 23) | OPC_MSA_3R_12, - OPC_ILVR_df = (0x5 << 23) | OPC_MSA_3R_14, - OPC_MAX_A_df = (0x6 << 23) | OPC_MSA_3R_0E, - OPC_AVER_S_df = (0x6 << 23) | OPC_MSA_3R_10, - OPC_MOD_S_df = (0x6 << 23) | OPC_MSA_3R_12, - OPC_ILVEV_df = (0x6 << 23) | OPC_MSA_3R_14, - OPC_MIN_A_df = (0x7 << 23) | OPC_MSA_3R_0E, - OPC_AVER_U_df = (0x7 << 23) | OPC_MSA_3R_10, - OPC_MOD_U_df = (0x7 << 23) | OPC_MSA_3R_12, - OPC_ILVOD_df = (0x7 << 23) | OPC_MSA_3R_14, - /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ OPC_SLDI_df = (0x0 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_CTCMSA = (0x0 << 22) | (0x3E << 16) | OPC_MSA_ELM, @@ -506,9 +447,54 @@ TRANS(BMNZ_V, trans_msa_3r, gen_helper_msa_bmnz_v); TRANS(BMZ_V, trans_msa_3r, gen_helper_msa_bmz_v); TRANS(BSEL_V, trans_msa_3r, gen_helper_msa_bsel_v); +TRANS_DF_iii(SLL, trans_msa_3r, gen_helper_msa_sll); +TRANS_DF_iii(SRA, trans_msa_3r, gen_helper_msa_sra); +TRANS_DF_iii(SRL, trans_msa_3r, gen_helper_msa_srl); +TRANS_DF_iii(BCLR, trans_msa_3r, gen_helper_msa_bclr); +TRANS_DF_iii(BSET, trans_msa_3r, gen_helper_msa_bset); +TRANS_DF_iii(BNEG, trans_msa_3r, gen_helper_msa_bneg); TRANS_DF_iii(BINSL, trans_msa_3r, gen_helper_msa_binsl); TRANS_DF_iii(BINSR, trans_msa_3r, gen_helper_msa_binsr); +TRANS_DF_iii(ADDV, trans_msa_3r, gen_helper_msa_addv); +TRANS_DF_iii(SUBV, trans_msa_3r, gen_helper_msa_subv); +TRANS_DF_iii(MAX_S, trans_msa_3r, gen_helper_msa_max_s); +TRANS_DF_iii(MAX_U, trans_msa_3r, gen_helper_msa_max_u); +TRANS_DF_iii(MIN_S, trans_msa_3r, gen_helper_msa_min_s); +TRANS_DF_iii(MIN_U, trans_msa_3r, gen_helper_msa_min_u); +TRANS_DF_iii(MAX_A, trans_msa_3r, gen_helper_msa_max_a); +TRANS_DF_iii(MIN_A, trans_msa_3r, gen_helper_msa_min_a); + +TRANS_DF_iii(CEQ, trans_msa_3r, gen_helper_msa_ceq); +TRANS_DF_iii(CLT_S, trans_msa_3r, gen_helper_msa_clt_s); +TRANS_DF_iii(CLT_U, trans_msa_3r, gen_helper_msa_clt_u); +TRANS_DF_iii(CLE_S, trans_msa_3r, gen_helper_msa_cle_s); +TRANS_DF_iii(CLE_U, trans_msa_3r, gen_helper_msa_cle_u); + +TRANS_DF_iii(ADD_A, trans_msa_3r, gen_helper_msa_add_a); +TRANS_DF_iii(ADDS_A, trans_msa_3r, gen_helper_msa_adds_a); +TRANS_DF_iii(ADDS_S, trans_msa_3r, gen_helper_msa_adds_s); +TRANS_DF_iii(ADDS_U, trans_msa_3r, gen_helper_msa_adds_u); +TRANS_DF_iii(AVE_S, trans_msa_3r, gen_helper_msa_ave_s); +TRANS_DF_iii(AVE_U, trans_msa_3r, gen_helper_msa_ave_u); +TRANS_DF_iii(AVER_S, trans_msa_3r, gen_helper_msa_aver_s); +TRANS_DF_iii(AVER_U, trans_msa_3r, gen_helper_msa_aver_u); + +TRANS_DF_iii(SUBS_S, trans_msa_3r, gen_helper_msa_subs_s); +TRANS_DF_iii(SUBS_U, trans_msa_3r, gen_helper_msa_subs_u); +TRANS_DF_iii(SUBSUS_U, trans_msa_3r, gen_helper_msa_subsus_u); +TRANS_DF_iii(SUBSUU_S, trans_msa_3r, gen_helper_msa_subsuu_s); +TRANS_DF_iii(ASUB_S, trans_msa_3r, gen_helper_msa_asub_s); +TRANS_DF_iii(ASUB_U, trans_msa_3r, gen_helper_msa_asub_u); + +TRANS_DF_iii(MULV, trans_msa_3r, gen_helper_msa_mulv); +TRANS_DF_iii(MADDV, trans_msa_3r, gen_helper_msa_maddv); +TRANS_DF_iii(MSUBV, trans_msa_3r, gen_helper_msa_msubv); +TRANS_DF_iii(DIV_S, trans_msa_3r, gen_helper_msa_div_s); +TRANS_DF_iii(DIV_U, trans_msa_3r, gen_helper_msa_div_u); +TRANS_DF_iii(MOD_S, trans_msa_3r, gen_helper_msa_mod_s); +TRANS_DF_iii(MOD_U, trans_msa_3r, gen_helper_msa_mod_u); + TRANS_DF_iii_b(DOTP_S, trans_msa_3r, gen_helper_msa_dotp_s); TRANS_DF_iii_b(DOTP_U, trans_msa_3r, gen_helper_msa_dotp_u); TRANS_DF_iii_b(DPADD_S, trans_msa_3r, gen_helper_msa_dpadd_s); @@ -518,806 +504,21 @@ TRANS_DF_iii_b(DPSUB_U, trans_msa_3r, gen_helper_msa_dpsub_u); TRANS(SLD, trans_msa_3rf, gen_helper_msa_sld_df); TRANS(SPLAT, trans_msa_3rf, gen_helper_msa_splat_df); +TRANS_DF_iii(PCKEV, trans_msa_3r, gen_helper_msa_pckev); +TRANS_DF_iii(PCKOD, trans_msa_3r, gen_helper_msa_pckod); +TRANS_DF_iii(ILVL, trans_msa_3r, gen_helper_msa_ilvl); +TRANS_DF_iii(ILVR, trans_msa_3r, gen_helper_msa_ilvr); +TRANS_DF_iii(ILVEV, trans_msa_3r, gen_helper_msa_ilvev); +TRANS_DF_iii(ILVOD, trans_msa_3r, gen_helper_msa_ilvod); TRANS(VSHF, trans_msa_3rf, gen_helper_msa_vshf_df); +TRANS_DF_iii(SRAR, trans_msa_3r, gen_helper_msa_srar); +TRANS_DF_iii(SRLR, trans_msa_3r, gen_helper_msa_srlr); TRANS_DF_iii_b(HADD_S, trans_msa_3r, gen_helper_msa_hadd_s); TRANS_DF_iii_b(HADD_U, trans_msa_3r, gen_helper_msa_hadd_u); TRANS_DF_iii_b(HSUB_S, trans_msa_3r, gen_helper_msa_hsub_s); TRANS_DF_iii_b(HSUB_U, trans_msa_3r, gen_helper_msa_hsub_u); -static void gen_msa_3r(DisasContext *ctx) -{ -#define MASK_MSA_3R(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) - uint8_t df = (ctx->opcode >> 21) & 0x3; - uint8_t wt = (ctx->opcode >> 16) & 0x1f; - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; - - TCGv_i32 tdf = tcg_const_i32(df); - TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 tws = tcg_const_i32(ws); - TCGv_i32 twt = tcg_const_i32(wt); - - switch (MASK_MSA_3R(ctx->opcode)) { - case OPC_BCLR_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_bclr_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_bclr_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_bclr_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_bclr_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_BNEG_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_bneg_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_bneg_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_bneg_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_bneg_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_BSET_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_bset_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_bset_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_bset_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_bset_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ADD_A_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_add_a_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_add_a_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_add_a_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_add_a_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ADDS_A_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_adds_a_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_adds_a_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_adds_a_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_adds_a_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ADDS_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_adds_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_adds_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_adds_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_adds_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ADDS_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_adds_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_adds_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_adds_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_adds_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ADDV_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_addv_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_addv_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_addv_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_addv_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_AVE_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_ave_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_ave_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_ave_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_ave_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_AVE_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_ave_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_ave_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_ave_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_ave_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_AVER_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_aver_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_aver_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_aver_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_aver_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_AVER_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_aver_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_aver_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_aver_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_aver_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_CEQ_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_ceq_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_ceq_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_ceq_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_ceq_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_CLE_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_cle_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_cle_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_cle_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_cle_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_CLE_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_cle_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_cle_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_cle_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_cle_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_CLT_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_clt_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_clt_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_clt_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_clt_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_CLT_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_clt_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_clt_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_clt_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_clt_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_DIV_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_div_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_div_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_div_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_div_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_DIV_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_div_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_div_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_div_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_div_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MAX_A_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_max_a_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_max_a_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_max_a_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_max_a_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MAX_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_max_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_max_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_max_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_max_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MAX_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_max_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_max_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_max_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_max_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MIN_A_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_min_a_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_min_a_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_min_a_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_min_a_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MIN_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_min_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_min_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_min_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_min_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MIN_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_min_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_min_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_min_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_min_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MOD_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_mod_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_mod_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_mod_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_mod_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MOD_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_mod_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_mod_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_mod_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_mod_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MADDV_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_maddv_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_maddv_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_maddv_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_maddv_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MSUBV_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_msubv_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_msubv_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_msubv_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_msubv_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ASUB_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_asub_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_asub_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_asub_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_asub_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ASUB_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_asub_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_asub_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_asub_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_asub_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ILVEV_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_ilvev_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_ilvev_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_ilvev_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_ilvev_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ILVOD_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_ilvod_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_ilvod_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_ilvod_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_ilvod_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ILVL_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_ilvl_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_ilvl_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_ilvl_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_ilvl_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_ILVR_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_ilvr_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_ilvr_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_ilvr_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_ilvr_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_PCKEV_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_pckev_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_pckev_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_pckev_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_pckev_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_PCKOD_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_pckod_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_pckod_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_pckod_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_pckod_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SLL_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_sll_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_sll_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_sll_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_sll_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SRA_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_sra_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_sra_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_sra_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_sra_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SRAR_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_srar_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_srar_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_srar_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_srar_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SRL_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_srl_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_srl_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_srl_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_srl_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SRLR_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_srlr_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_srlr_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_srlr_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_srlr_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SUBS_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_subs_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_subs_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_subs_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_subs_s_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_MULV_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_mulv_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_mulv_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_mulv_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_mulv_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SUBV_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_subv_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_subv_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_subv_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_subv_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SUBS_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_subs_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_subs_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_subs_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_subs_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SUBSUS_U_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_subsus_u_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_subsus_u_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_subsus_u_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_subsus_u_d(cpu_env, twd, tws, twt); - break; - } - break; - case OPC_SUBSUU_S_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_subsuu_s_b(cpu_env, twd, tws, twt); - break; - case DF_HALF: - gen_helper_msa_subsuu_s_h(cpu_env, twd, tws, twt); - break; - case DF_WORD: - gen_helper_msa_subsuu_s_w(cpu_env, twd, tws, twt); - break; - case DF_DOUBLE: - gen_helper_msa_subsuu_s_d(cpu_env, twd, tws, twt); - break; - } - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; - } - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); - tcg_temp_free_i32(twt); - tcg_temp_free_i32(tdf); -} - static void gen_msa_elm_3e(DisasContext *ctx) { #define MASK_MSA_ELM_DF3E(op) (MASK_MSA_MINOR(op) | (op & (0x3FF << 16))) @@ -1608,17 +809,6 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) } switch (MASK_MSA_MINOR(opcode)) { - case OPC_MSA_3R_0D: - case OPC_MSA_3R_0E: - case OPC_MSA_3R_0F: - case OPC_MSA_3R_10: - case OPC_MSA_3R_11: - case OPC_MSA_3R_12: - case OPC_MSA_3R_13: - case OPC_MSA_3R_14: - case OPC_MSA_3R_15: - gen_msa_3r(ctx); - break; case OPC_MSA_ELM: gen_msa_elm(ctx); break; From 0a510c934c36f6314508ad4e7b5fd6ca8eb02c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 17:16:45 +0200 Subject: [PATCH 1063/1334] target/mips: Convert MSA ELM instruction format to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert instructions with an immediate element index and data format df/n to decodetree. Since the 'data format' and 'n' fields are constant values, use tcg_constant_i32() instead of a TCG temporaries. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-25-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 8 +++++ target/mips/tcg/msa_translate.c | 57 +++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 391261109a..bf014524ee 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -18,7 +18,10 @@ &msa_ldi df wd sa &msa_i df wd ws sa &msa_bit df wd ws m +&msa_elm_df df wd ws n +%elm_df 16:6 !function=elm_df +%elm_n 16:6 !function=elm_n %bit_df 16:7 !function=bit_df %bit_m 16:7 !function=bit_m %2r_df_w 16:1 !function=plus_2 @@ -29,6 +32,7 @@ @ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @bz ...... ... df:2 wt:5 sa:16 &msa_bz +@elm_df ...... .... ...... ws:5 wd:5 ...... &msa_elm_df df=%elm_df n=%elm_n @vec ...... ..... wt:5 ws:5 wd:5 ...... &msa_r df=0 @2r ...... ........ df:2 ws:5 wd:5 ...... &msa_r wt=0 @2rf ...... ......... . ws:5 wd:5 ...... &msa_r wt=0 df=%2r_df_w @@ -161,6 +165,10 @@ BNZ 010001 111 .. ..... ................ @bz HSUB_S 011110 110.. ..... ..... ..... 010101 @3r HSUB_U 011110 111.. ..... ..... ..... 010101 @3r + SLDI 011110 0000 ...... ..... ..... 011001 @elm_df + SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df + INSVE 011110 0101 ...... ..... ..... 011001 @elm_df + FCAF 011110 0000 . ..... ..... ..... 011010 @3rf_w FCUN 011110 0001 . ..... ..... ..... 011010 @3rf_w FCEQ 011110 0010 . ..... ..... ..... 011010 @3rf_w diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 3b95e081a0..14e0a8879c 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -17,6 +17,8 @@ #include "fpu_helper.h" #include "internal.h" +static int elm_n(DisasContext *ctx, int x); +static int elm_df(DisasContext *ctx, int x); static int bit_m(DisasContext *ctx, int x); static int bit_df(DisasContext *ctx, int x); @@ -42,15 +44,12 @@ enum { enum { /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ - OPC_SLDI_df = (0x0 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_CTCMSA = (0x0 << 22) | (0x3E << 16) | OPC_MSA_ELM, - OPC_SPLATI_df = (0x1 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_CFCMSA = (0x1 << 22) | (0x3E << 16) | OPC_MSA_ELM, OPC_COPY_S_df = (0x2 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_MOVE_V = (0x2 << 22) | (0x3E << 16) | OPC_MSA_ELM, OPC_COPY_U_df = (0x3 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_INSERT_df = (0x4 << 22) | (0x00 << 16) | OPC_MSA_ELM, - OPC_INSVE_df = (0x5 << 22) | (0x00 << 16) | OPC_MSA_ELM, }; static const char msaregnames[][6] = { @@ -107,6 +106,24 @@ static int df_extract_df(DisasContext *ctx, int x, const struct dfe *s) return -1; } +static const struct dfe df_elm[] = { + /* Table 3.26 ELM Instruction Format */ + [DF_BYTE] = {4, 2, 0b00}, + [DF_HALF] = {3, 3, 0b100}, + [DF_WORD] = {2, 4, 0b1100}, + [DF_DOUBLE] = {1, 5, 0b11100} +}; + +static int elm_n(DisasContext *ctx, int x) +{ + return df_extract_val(ctx, x, df_elm); +} + +static int elm_df(DisasContext *ctx, int x) +{ + return df_extract_df(ctx, x, df_elm); +} + static const struct dfe df_bit[] = { /* Table 3.28 BIT Instruction Format */ [DF_BYTE] = {3, 4, 0b1110}, @@ -551,6 +568,30 @@ static void gen_msa_elm_3e(DisasContext *ctx) tcg_temp_free_i32(tsr); } +static bool trans_msa_elm(DisasContext *ctx, arg_msa_elm_df *a, + gen_helper_piiii *gen_msa_elm_df) +{ + if (a->df < 0) { + return false; + } + + if (!check_msa_enabled(ctx)) { + return true; + } + + gen_msa_elm_df(cpu_env, + tcg_constant_i32(a->df), + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws), + tcg_constant_i32(a->n)); + + return true; +} + +TRANS(SLDI, trans_msa_elm, gen_helper_msa_sldi_df); +TRANS(SPLATI, trans_msa_elm, gen_helper_msa_splati_df); +TRANS(INSVE, trans_msa_elm, gen_helper_msa_insve_df); + static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) { #define MASK_MSA_ELM(op) (MASK_MSA_MINOR(op) | (op & (0xf << 22))) @@ -560,18 +601,8 @@ static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) TCGv_i32 tws = tcg_const_i32(ws); TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tn = tcg_const_i32(n); - TCGv_i32 tdf = tcg_constant_i32(df); switch (MASK_MSA_ELM(ctx->opcode)) { - case OPC_SLDI_df: - gen_helper_msa_sldi_df(cpu_env, tdf, twd, tws, tn); - break; - case OPC_SPLATI_df: - gen_helper_msa_splati_df(cpu_env, tdf, twd, tws, tn); - break; - case OPC_INSVE_df: - gen_helper_msa_insve_df(cpu_env, tdf, twd, tws, tn); - break; case OPC_COPY_S_df: case OPC_COPY_U_df: case OPC_INSERT_df: From 2f2745c81a3c196fc149fcf243b11cae13fd126b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 17:58:41 +0200 Subject: [PATCH 1064/1334] target/mips: Convert MSA COPY_U opcode to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the COPY_U opcode (Element Copy to GPR Unsigned) to decodetree. Since the 'n' field is a constant value, use tcg_constant_i32() instead of a TCG temporary. Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211028210843.2120802-26-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 1 + target/mips/tcg/msa_translate.c | 66 ++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index bf014524ee..0e166a4e61 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -167,6 +167,7 @@ BNZ 010001 111 .. ..... ................ @bz SLDI 011110 0000 ...... ..... ..... 011001 @elm_df SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df + COPY_U 011110 0011 ...... ..... ..... 011001 @elm_df INSVE 011110 0101 ...... ..... ..... 011001 @elm_df FCAF 011110 0000 . ..... ..... ..... 011010 @3rf_w diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 14e0a8879c..4c560aa1e1 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -48,7 +48,6 @@ enum { OPC_CFCMSA = (0x1 << 22) | (0x3E << 16) | OPC_MSA_ELM, OPC_COPY_S_df = (0x2 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_MOVE_V = (0x2 << 22) | (0x3E << 16) | OPC_MSA_ELM, - OPC_COPY_U_df = (0x3 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_INSERT_df = (0x4 << 22) | (0x00 << 16) | OPC_MSA_ELM, }; @@ -592,6 +591,46 @@ TRANS(SLDI, trans_msa_elm, gen_helper_msa_sldi_df); TRANS(SPLATI, trans_msa_elm, gen_helper_msa_splati_df); TRANS(INSVE, trans_msa_elm, gen_helper_msa_insve_df); +static bool trans_msa_elm_fn(DisasContext *ctx, arg_msa_elm_df *a, + gen_helper_piii * const gen_msa_elm[4]) +{ + if (a->df < 0 || !gen_msa_elm[a->df]) { + return false; + } + + if (check_msa_enabled(ctx)) { + return true; + } + + if (a->wd == 0) { + /* Treat as NOP. */ + return true; + } + + gen_msa_elm[a->df](cpu_env, + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws), + tcg_constant_i32(a->n)); + + return true; +} + +#if defined(TARGET_MIPS64) +#define NULL_IF_MIPS32(function) function +#else +#define NULL_IF_MIPS32(function) NULL +#endif + +static bool trans_COPY_U(DisasContext *ctx, arg_msa_elm_df *a) +{ + static gen_helper_piii * const gen_msa_copy_u[4] = { + gen_helper_msa_copy_u_b, gen_helper_msa_copy_u_h, + NULL_IF_MIPS32(gen_helper_msa_copy_u_w), NULL + }; + + return trans_msa_elm_fn(ctx, a, gen_msa_copy_u); +} + static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) { #define MASK_MSA_ELM(op) (MASK_MSA_MINOR(op) | (op & (0xf << 22))) @@ -604,7 +643,6 @@ static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) switch (MASK_MSA_ELM(ctx->opcode)) { case OPC_COPY_S_df: - case OPC_COPY_U_df: case OPC_INSERT_df: #if !defined(TARGET_MIPS64) /* Double format valid only for MIPS64 */ @@ -612,11 +650,6 @@ static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) gen_reserved_instruction(ctx); break; } - if ((MASK_MSA_ELM(ctx->opcode) == OPC_COPY_U_df) && - (df == DF_WORD)) { - gen_reserved_instruction(ctx); - break; - } #endif switch (MASK_MSA_ELM(ctx->opcode)) { case OPC_COPY_S_df: @@ -635,25 +668,6 @@ static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) case DF_DOUBLE: gen_helper_msa_copy_s_d(cpu_env, twd, tws, tn); break; -#endif - default: - assert(0); - } - } - break; - case OPC_COPY_U_df: - if (likely(wd != 0)) { - switch (df) { - case DF_BYTE: - gen_helper_msa_copy_u_b(cpu_env, twd, tws, tn); - break; - case DF_HALF: - gen_helper_msa_copy_u_h(cpu_env, twd, tws, tn); - break; -#if defined(TARGET_MIPS64) - case DF_WORD: - gen_helper_msa_copy_u_w(cpu_env, twd, tws, tn); - break; #endif default: assert(0); From 97fe675519d3e7406dfed2fe8cc7af9cdbec3cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Oct 2021 18:08:24 +0200 Subject: [PATCH 1065/1334] target/mips: Convert MSA COPY_S and INSERT opcodes to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the COPY_S (Element Copy to GPR Signed) opcode and INSERT (GPR Insert Element) opcode to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-27-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 2 + target/mips/tcg/msa_translate.c | 103 +++++--------------------------- 2 files changed, 18 insertions(+), 87 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 0e166a4e61..9aac6808fc 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -167,7 +167,9 @@ BNZ 010001 111 .. ..... ................ @bz SLDI 011110 0000 ...... ..... ..... 011001 @elm_df SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df + COPY_S 011110 0010 ...... ..... ..... 011001 @elm_df COPY_U 011110 0011 ...... ..... ..... 011001 @elm_df + INSERT 011110 0100 ...... ..... ..... 011001 @elm_df INSVE 011110 0101 ...... ..... ..... 011001 @elm_df FCAF 011110 0000 . ..... ..... ..... 011010 @3rf_w diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 4c560aa1e1..6a034831ef 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -46,9 +46,7 @@ enum { /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ OPC_CTCMSA = (0x0 << 22) | (0x3E << 16) | OPC_MSA_ELM, OPC_CFCMSA = (0x1 << 22) | (0x3E << 16) | OPC_MSA_ELM, - OPC_COPY_S_df = (0x2 << 22) | (0x00 << 16) | OPC_MSA_ELM, OPC_MOVE_V = (0x2 << 22) | (0x3E << 16) | OPC_MSA_ELM, - OPC_INSERT_df = (0x4 << 22) | (0x00 << 16) | OPC_MSA_ELM, }; static const char msaregnames[][6] = { @@ -631,98 +629,31 @@ static bool trans_COPY_U(DisasContext *ctx, arg_msa_elm_df *a) return trans_msa_elm_fn(ctx, a, gen_msa_copy_u); } -static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) +static bool trans_COPY_S(DisasContext *ctx, arg_msa_elm_df *a) { -#define MASK_MSA_ELM(op) (MASK_MSA_MINOR(op) | (op & (0xf << 22))) - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; + static gen_helper_piii * const gen_msa_copy_s[4] = { + gen_helper_msa_copy_s_b, gen_helper_msa_copy_s_h, + gen_helper_msa_copy_s_w, NULL_IF_MIPS32(gen_helper_msa_copy_s_d) + }; - TCGv_i32 tws = tcg_const_i32(ws); - TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 tn = tcg_const_i32(n); + return trans_msa_elm_fn(ctx, a, gen_msa_copy_s); +} - switch (MASK_MSA_ELM(ctx->opcode)) { - case OPC_COPY_S_df: - case OPC_INSERT_df: -#if !defined(TARGET_MIPS64) - /* Double format valid only for MIPS64 */ - if (df == DF_DOUBLE) { - gen_reserved_instruction(ctx); - break; - } -#endif - switch (MASK_MSA_ELM(ctx->opcode)) { - case OPC_COPY_S_df: - if (likely(wd != 0)) { - switch (df) { - case DF_BYTE: - gen_helper_msa_copy_s_b(cpu_env, twd, tws, tn); - break; - case DF_HALF: - gen_helper_msa_copy_s_h(cpu_env, twd, tws, tn); - break; - case DF_WORD: - gen_helper_msa_copy_s_w(cpu_env, twd, tws, tn); - break; -#if defined(TARGET_MIPS64) - case DF_DOUBLE: - gen_helper_msa_copy_s_d(cpu_env, twd, tws, tn); - break; -#endif - default: - assert(0); - } - } - break; - case OPC_INSERT_df: - switch (df) { - case DF_BYTE: - gen_helper_msa_insert_b(cpu_env, twd, tws, tn); - break; - case DF_HALF: - gen_helper_msa_insert_h(cpu_env, twd, tws, tn); - break; - case DF_WORD: - gen_helper_msa_insert_w(cpu_env, twd, tws, tn); - break; -#if defined(TARGET_MIPS64) - case DF_DOUBLE: - gen_helper_msa_insert_d(cpu_env, twd, tws, tn); - break; -#endif - default: - assert(0); - } - break; - } - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - } - tcg_temp_free_i32(twd); - tcg_temp_free_i32(tws); - tcg_temp_free_i32(tn); +static bool trans_INSERT(DisasContext *ctx, arg_msa_elm_df *a) +{ + static gen_helper_piii * const gen_msa_insert[4] = { + gen_helper_msa_insert_b, gen_helper_msa_insert_h, + gen_helper_msa_insert_w, NULL_IF_MIPS32(gen_helper_msa_insert_d) + }; + + return trans_msa_elm_fn(ctx, a, gen_msa_insert); } static void gen_msa_elm(DisasContext *ctx) { uint8_t dfn = (ctx->opcode >> 16) & 0x3f; - uint32_t df = 0, n = 0; - if ((dfn & 0x30) == 0x00) { - n = dfn & 0x0f; - df = DF_BYTE; - } else if ((dfn & 0x38) == 0x20) { - n = dfn & 0x07; - df = DF_HALF; - } else if ((dfn & 0x3c) == 0x30) { - n = dfn & 0x03; - df = DF_WORD; - } else if ((dfn & 0x3e) == 0x38) { - n = dfn & 0x01; - df = DF_DOUBLE; - } else if (dfn == 0x3E) { + if (dfn == 0x3E) { /* CTCMSA, CFCMSA, MOVE.V */ gen_msa_elm_3e(ctx); return; @@ -730,8 +661,6 @@ static void gen_msa_elm(DisasContext *ctx) gen_reserved_instruction(ctx); return; } - - gen_msa_elm_df(ctx, df, n); } TRANS(FCAF, trans_msa_3rf, gen_helper_msa_fcaf_df); From 62ba0e855a0ffe90142de9b6aac0e89ab8c0b894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 18:26:03 +0200 Subject: [PATCH 1066/1334] target/mips: Convert MSA MOVE.V opcode to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the MOVE.V opcode (Vector Move) to decodetree. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-28-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 7 ++++++- target/mips/tcg/msa_translate.c | 19 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 9aac6808fc..d1b6a63b52 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -19,6 +19,7 @@ &msa_i df wd ws sa &msa_bit df wd ws m &msa_elm_df df wd ws n +&msa_elm wd ws %elm_df 16:6 !function=elm_df %elm_n 16:6 !function=elm_n @@ -33,6 +34,7 @@ @bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 @bz ...... ... df:2 wt:5 sa:16 &msa_bz @elm_df ...... .... ...... ws:5 wd:5 ...... &msa_elm_df df=%elm_df n=%elm_n +@elm ...... .......... ws:5 wd:5 ...... &msa_elm @vec ...... ..... wt:5 ws:5 wd:5 ...... &msa_r df=0 @2r ...... ........ df:2 ws:5 wd:5 ...... &msa_r wt=0 @2rf ...... ......... . ws:5 wd:5 ...... &msa_r wt=0 df=%2r_df_w @@ -167,7 +169,10 @@ BNZ 010001 111 .. ..... ................ @bz SLDI 011110 0000 ...... ..... ..... 011001 @elm_df SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df - COPY_S 011110 0010 ...... ..... ..... 011001 @elm_df + { + MOVE_V 011110 0010111110 ..... ..... 011001 @elm + COPY_S 011110 0010 ...... ..... ..... 011001 @elm_df + } COPY_U 011110 0011 ...... ..... ..... 011001 @elm_df INSERT 011110 0100 ...... ..... ..... 011001 @elm_df INSVE 011110 0101 ...... ..... ..... 011001 @elm_df diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 6a034831ef..ea572413ed 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -46,7 +46,6 @@ enum { /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ OPC_CTCMSA = (0x0 << 22) | (0x3E << 16) | OPC_MSA_ELM, OPC_CFCMSA = (0x1 << 22) | (0x3E << 16) | OPC_MSA_ELM, - OPC_MOVE_V = (0x2 << 22) | (0x3E << 16) | OPC_MSA_ELM, }; static const char msaregnames[][6] = { @@ -533,6 +532,19 @@ TRANS_DF_iii_b(HADD_U, trans_msa_3r, gen_helper_msa_hadd_u); TRANS_DF_iii_b(HSUB_S, trans_msa_3r, gen_helper_msa_hsub_s); TRANS_DF_iii_b(HSUB_U, trans_msa_3r, gen_helper_msa_hsub_u); +static bool trans_MOVE_V(DisasContext *ctx, arg_msa_elm *a) +{ + if (!check_msa_enabled(ctx)) { + return true; + } + + gen_helper_msa_move_v(cpu_env, + tcg_constant_i32(a->wd), + tcg_constant_i32(a->ws)); + + return true; +} + static void gen_msa_elm_3e(DisasContext *ctx) { #define MASK_MSA_ELM_DF3E(op) (MASK_MSA_MINOR(op) | (op & (0x3FF << 16))) @@ -551,9 +563,6 @@ static void gen_msa_elm_3e(DisasContext *ctx) gen_helper_msa_cfcmsa(telm, cpu_env, tsr); gen_store_gpr(telm, dest); break; - case OPC_MOVE_V: - gen_helper_msa_move_v(cpu_env, tdt, tsr); - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); @@ -654,7 +663,7 @@ static void gen_msa_elm(DisasContext *ctx) uint8_t dfn = (ctx->opcode >> 16) & 0x3f; if (dfn == 0x3E) { - /* CTCMSA, CFCMSA, MOVE.V */ + /* CTCMSA, CFCMSA */ gen_msa_elm_3e(ctx); return; } else { From 6f74237691461fb2f875aecb35a92a073d0bb7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 18:29:14 +0200 Subject: [PATCH 1067/1334] target/mips: Convert CFCMSA opcode to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the CFCMSA (Copy From Control MSA register) opcode to decodetree. Since it overlaps with the SPLATI opcode, use a decodetree overlap group. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-29-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 5 ++++- target/mips/tcg/msa_translate.c | 27 +++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index d1b6a63b52..de8153a89b 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -168,7 +168,10 @@ BNZ 010001 111 .. ..... ................ @bz HSUB_U 011110 111.. ..... ..... ..... 010101 @3r SLDI 011110 0000 ...... ..... ..... 011001 @elm_df - SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df + { + CFCMSA 011110 0001111110 ..... ..... 011001 @elm + SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df + } { MOVE_V 011110 0010111110 ..... ..... 011001 @elm COPY_S 011110 0010 ...... ..... ..... 011001 @elm_df diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index ea572413ed..764b33741a 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -45,7 +45,6 @@ enum { enum { /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ OPC_CTCMSA = (0x0 << 22) | (0x3E << 16) | OPC_MSA_ELM, - OPC_CFCMSA = (0x1 << 22) | (0x3E << 16) | OPC_MSA_ELM, }; static const char msaregnames[][6] = { @@ -551,7 +550,6 @@ static void gen_msa_elm_3e(DisasContext *ctx) uint8_t source = (ctx->opcode >> 11) & 0x1f; uint8_t dest = (ctx->opcode >> 6) & 0x1f; TCGv telm = tcg_temp_new(); - TCGv_i32 tsr = tcg_const_i32(source); TCGv_i32 tdt = tcg_const_i32(dest); switch (MASK_MSA_ELM_DF3E(ctx->opcode)) { @@ -559,10 +557,6 @@ static void gen_msa_elm_3e(DisasContext *ctx) gen_load_gpr(telm, source); gen_helper_msa_ctcmsa(cpu_env, telm, tdt); break; - case OPC_CFCMSA: - gen_helper_msa_cfcmsa(telm, cpu_env, tsr); - gen_store_gpr(telm, dest); - break; default: MIPS_INVAL("MSA instruction"); gen_reserved_instruction(ctx); @@ -571,7 +565,24 @@ static void gen_msa_elm_3e(DisasContext *ctx) tcg_temp_free(telm); tcg_temp_free_i32(tdt); - tcg_temp_free_i32(tsr); +} + +static bool trans_CFCMSA(DisasContext *ctx, arg_msa_elm *a) +{ + TCGv telm; + + if (!check_msa_enabled(ctx)) { + return true; + } + + telm = tcg_temp_new(); + + gen_helper_msa_cfcmsa(telm, cpu_env, tcg_constant_i32(a->ws)); + gen_store_gpr(telm, a->wd); + + tcg_temp_free(telm); + + return true; } static bool trans_msa_elm(DisasContext *ctx, arg_msa_elm_df *a, @@ -663,7 +674,7 @@ static void gen_msa_elm(DisasContext *ctx) uint8_t dfn = (ctx->opcode >> 16) & 0x3f; if (dfn == 0x3E) { - /* CTCMSA, CFCMSA */ + /* CTCMSA */ gen_msa_elm_3e(ctx); return; } else { From 643ec9022e72bb9100c6455de3d81cab8eed0a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 18:29:14 +0200 Subject: [PATCH 1068/1334] target/mips: Convert CTCMSA opcode to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the CTCMSA (Copy To Control MSA register) opcode to decodetree. Since it overlaps with the SLDI opcode, use a decodetree overlap group. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-30-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 5 ++- target/mips/tcg/msa_translate.c | 69 ++++++--------------------------- 2 files changed, 16 insertions(+), 58 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index de8153a89b..a4c7cceb15 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -167,7 +167,10 @@ BNZ 010001 111 .. ..... ................ @bz HSUB_S 011110 110.. ..... ..... ..... 010101 @3r HSUB_U 011110 111.. ..... ..... ..... 010101 @3r - SLDI 011110 0000 ...... ..... ..... 011001 @elm_df + { + CTCMSA 011110 0000111110 ..... ..... 011001 @elm + SLDI 011110 0000 ...... ..... ..... 011001 @elm_df + } { CFCMSA 011110 0001111110 ..... ..... 011001 @elm SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 764b33741a..c054a05f8b 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -35,18 +35,6 @@ static inline int plus_2(DisasContext *s, int x) /* Include the auto-generated decoder. */ #include "decode-msa.c.inc" -#define OPC_MSA (0x1E << 26) - -#define MASK_MSA_MINOR(op) (MASK_OP_MAJOR(op) | (op & 0x3F)) -enum { - OPC_MSA_ELM = 0x19 | OPC_MSA, -}; - -enum { - /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ - OPC_CTCMSA = (0x0 << 22) | (0x3E << 16) | OPC_MSA_ELM, -}; - static const char msaregnames[][6] = { "w0.d0", "w0.d1", "w1.d0", "w1.d1", "w2.d0", "w2.d1", "w3.d0", "w3.d1", @@ -544,27 +532,22 @@ static bool trans_MOVE_V(DisasContext *ctx, arg_msa_elm *a) return true; } -static void gen_msa_elm_3e(DisasContext *ctx) +static bool trans_CTCMSA(DisasContext *ctx, arg_msa_elm *a) { -#define MASK_MSA_ELM_DF3E(op) (MASK_MSA_MINOR(op) | (op & (0x3FF << 16))) - uint8_t source = (ctx->opcode >> 11) & 0x1f; - uint8_t dest = (ctx->opcode >> 6) & 0x1f; - TCGv telm = tcg_temp_new(); - TCGv_i32 tdt = tcg_const_i32(dest); + TCGv telm; - switch (MASK_MSA_ELM_DF3E(ctx->opcode)) { - case OPC_CTCMSA: - gen_load_gpr(telm, source); - gen_helper_msa_ctcmsa(cpu_env, telm, tdt); - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; + if (!check_msa_enabled(ctx)) { + return true; } + telm = tcg_temp_new(); + + gen_load_gpr(telm, a->ws); + gen_helper_msa_ctcmsa(cpu_env, telm, tcg_constant_i32(a->wd)); + tcg_temp_free(telm); - tcg_temp_free_i32(tdt); + + return true; } static bool trans_CFCMSA(DisasContext *ctx, arg_msa_elm *a) @@ -669,20 +652,6 @@ static bool trans_INSERT(DisasContext *ctx, arg_msa_elm_df *a) return trans_msa_elm_fn(ctx, a, gen_msa_insert); } -static void gen_msa_elm(DisasContext *ctx) -{ - uint8_t dfn = (ctx->opcode >> 16) & 0x3f; - - if (dfn == 0x3E) { - /* CTCMSA */ - gen_msa_elm_3e(ctx); - return; - } else { - gen_reserved_instruction(ctx); - return; - } -} - TRANS(FCAF, trans_msa_3rf, gen_helper_msa_fcaf_df); TRANS(FCUN, trans_msa_3rf, gen_helper_msa_fcun_df); TRANS(FCEQ, trans_msa_3rf, gen_helper_msa_fceq_df); @@ -796,21 +765,7 @@ TRANS(FFINT_U, trans_msa_2rf, gen_helper_msa_ffint_u_df); static bool trans_MSA(DisasContext *ctx, arg_MSA *a) { - uint32_t opcode = ctx->opcode; - - if (!check_msa_enabled(ctx)) { - return true; - } - - switch (MASK_MSA_MINOR(opcode)) { - case OPC_MSA_ELM: - gen_msa_elm(ctx); - break; - default: - MIPS_INVAL("MSA instruction"); - gen_reserved_instruction(ctx); - break; - } + gen_reserved_instruction(ctx); return true; } From 75d12c8c249c1791e4b24bfea0fba14d2ccd64ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 18:38:29 +0200 Subject: [PATCH 1069/1334] target/mips: Remove generic MSA opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All opcodes have been converted to decodetree. The generic MSA handler is now pointless, remove it. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-31-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 2 -- target/mips/tcg/msa_translate.c | 7 ------- 2 files changed, 9 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index a4c7cceb15..124768132b 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -257,6 +257,4 @@ BNZ 010001 111 .. ..... ................ @bz LD 011110 .......... ..... ..... 1000 .. @ldst ST 011110 .......... ..... ..... 1001 .. @ldst - - MSA 011110 -------------------------- } diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index c054a05f8b..7576b3ed86 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -763,13 +763,6 @@ TRANS(FTINT_U, trans_msa_2rf, gen_helper_msa_ftint_u_df); TRANS(FFINT_S, trans_msa_2rf, gen_helper_msa_ffint_s_df); TRANS(FFINT_U, trans_msa_2rf, gen_helper_msa_ffint_u_df); -static bool trans_MSA(DisasContext *ctx, arg_MSA *a) -{ - gen_reserved_instruction(ctx); - - return true; -} - static bool trans_msa_ldst(DisasContext *ctx, arg_msa_i *a, gen_helper_piv *gen_msa_ldst) { From 73053f62287256cb69e95f43fa424eb4342d6935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Oct 2021 18:39:47 +0200 Subject: [PATCH 1070/1334] target/mips: Remove one MSA unnecessary decodetree overlap group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only the MSA generic opcode was overlapping with the other instructions. Since the previous commit removed it, we can now remove the overlap group. The decodetree script forces us to re-indent the opcodes. Diff trivial to review using `git-diff --ignore-all-space`. Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211028210843.2120802-32-f4bug@amsat.org> --- target/mips/tcg/msa.decode | 398 ++++++++++++++++++------------------- 1 file changed, 198 insertions(+), 200 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 124768132b..9575289195 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -56,205 +56,203 @@ BNZ_V 010001 01111 ..... ................ @bz_v BZ 010001 110 .. ..... ................ @bz BNZ 010001 111 .. ..... ................ @bz +ANDI 011110 00 ........ ..... ..... 000000 @i8 +ORI 011110 01 ........ ..... ..... 000000 @i8 +NORI 011110 10 ........ ..... ..... 000000 @i8 +XORI 011110 11 ........ ..... ..... 000000 @i8 +BMNZI 011110 00 ........ ..... ..... 000001 @i8 +BMZI 011110 01 ........ ..... ..... 000001 @i8 +BSELI 011110 10 ........ ..... ..... 000001 @i8 +SHF 011110 .. ........ ..... ..... 000010 @i8_df + +ADDVI 011110 000 .. ..... ..... ..... 000110 @u5 +SUBVI 011110 001 .. ..... ..... ..... 000110 @u5 +MAXI_S 011110 010 .. ..... ..... ..... 000110 @s5 +MAXI_U 011110 011 .. ..... ..... ..... 000110 @u5 +MINI_S 011110 100 .. ..... ..... ..... 000110 @s5 +MINI_U 011110 101 .. ..... ..... ..... 000110 @u5 + +CEQI 011110 000 .. ..... ..... ..... 000111 @s5 +CLTI_S 011110 010 .. ..... ..... ..... 000111 @s5 +CLTI_U 011110 011 .. ..... ..... ..... 000111 @u5 +CLEI_S 011110 100 .. ..... ..... ..... 000111 @s5 +CLEI_U 011110 101 .. ..... ..... ..... 000111 @u5 + +LDI 011110 110 .. .......... ..... 000111 @ldi + +SLLI 011110 000 ....... ..... ..... 001001 @bit +SRAI 011110 001 ....... ..... ..... 001001 @bit +SRLI 011110 010 ....... ..... ..... 001001 @bit +BCLRI 011110 011 ....... ..... ..... 001001 @bit +BSETI 011110 100 ....... ..... ..... 001001 @bit +BNEGI 011110 101 ....... ..... ..... 001001 @bit +BINSLI 011110 110 ....... ..... ..... 001001 @bit +BINSRI 011110 111 ....... ..... ..... 001001 @bit + +SAT_S 011110 000 ....... ..... ..... 001010 @bit +SAT_U 011110 001 ....... ..... ..... 001010 @bit +SRARI 011110 010 ....... ..... ..... 001010 @bit +SRLRI 011110 011 ....... ..... ..... 001010 @bit + +SLL 011110 000.. ..... ..... ..... 001101 @3r +SRA 011110 001.. ..... ..... ..... 001101 @3r +SRL 011110 010.. ..... ..... ..... 001101 @3r +BCLR 011110 011.. ..... ..... ..... 001101 @3r +BSET 011110 100.. ..... ..... ..... 001101 @3r +BNEG 011110 101.. ..... ..... ..... 001101 @3r +BINSL 011110 110.. ..... ..... ..... 001101 @3r +BINSR 011110 111.. ..... ..... ..... 001101 @3r + +ADDV 011110 000.. ..... ..... ..... 001110 @3r +SUBV 011110 001.. ..... ..... ..... 001110 @3r +MAX_S 011110 010.. ..... ..... ..... 001110 @3r +MAX_U 011110 011.. ..... ..... ..... 001110 @3r +MIN_S 011110 100.. ..... ..... ..... 001110 @3r +MIN_U 011110 101.. ..... ..... ..... 001110 @3r +MAX_A 011110 110.. ..... ..... ..... 001110 @3r +MIN_A 011110 111.. ..... ..... ..... 001110 @3r + +CEQ 011110 000.. ..... ..... ..... 001111 @3r +CLT_S 011110 010.. ..... ..... ..... 001111 @3r +CLT_U 011110 011.. ..... ..... ..... 001111 @3r +CLE_S 011110 100.. ..... ..... ..... 001111 @3r +CLE_U 011110 101.. ..... ..... ..... 001111 @3r + +ADD_A 011110 000.. ..... ..... ..... 010000 @3r +ADDS_A 011110 001.. ..... ..... ..... 010000 @3r +ADDS_S 011110 010.. ..... ..... ..... 010000 @3r +ADDS_U 011110 011.. ..... ..... ..... 010000 @3r +AVE_S 011110 100.. ..... ..... ..... 010000 @3r +AVE_U 011110 101.. ..... ..... ..... 010000 @3r +AVER_S 011110 110.. ..... ..... ..... 010000 @3r +AVER_U 011110 111.. ..... ..... ..... 010000 @3r + +SUBS_S 011110 000.. ..... ..... ..... 010001 @3r +SUBS_U 011110 001.. ..... ..... ..... 010001 @3r +SUBSUS_U 011110 010.. ..... ..... ..... 010001 @3r +SUBSUU_S 011110 011.. ..... ..... ..... 010001 @3r +ASUB_S 011110 100.. ..... ..... ..... 010001 @3r +ASUB_U 011110 101.. ..... ..... ..... 010001 @3r + +MULV 011110 000.. ..... ..... ..... 010010 @3r +MADDV 011110 001.. ..... ..... ..... 010010 @3r +MSUBV 011110 010.. ..... ..... ..... 010010 @3r +DIV_S 011110 100.. ..... ..... ..... 010010 @3r +DIV_U 011110 101.. ..... ..... ..... 010010 @3r +MOD_S 011110 110.. ..... ..... ..... 010010 @3r +MOD_U 011110 111.. ..... ..... ..... 010010 @3r + +DOTP_S 011110 000.. ..... ..... ..... 010011 @3r +DOTP_U 011110 001.. ..... ..... ..... 010011 @3r +DPADD_S 011110 010.. ..... ..... ..... 010011 @3r +DPADD_U 011110 011.. ..... ..... ..... 010011 @3r +DPSUB_S 011110 100.. ..... ..... ..... 010011 @3r +DPSUB_U 011110 101.. ..... ..... ..... 010011 @3r + +SLD 011110 000 .. ..... ..... ..... 010100 @3r +SPLAT 011110 001 .. ..... ..... ..... 010100 @3r +PCKEV 011110 010 .. ..... ..... ..... 010100 @3r +PCKOD 011110 011 .. ..... ..... ..... 010100 @3r +ILVL 011110 100 .. ..... ..... ..... 010100 @3r +ILVR 011110 101 .. ..... ..... ..... 010100 @3r +ILVEV 011110 110 .. ..... ..... ..... 010100 @3r +ILVOD 011110 111 .. ..... ..... ..... 010100 @3r + +VSHF 011110 000 .. ..... ..... ..... 010101 @3r +SRAR 011110 001 .. ..... ..... ..... 010101 @3r +SRLR 011110 010 .. ..... ..... ..... 010101 @3r +HADD_S 011110 100.. ..... ..... ..... 010101 @3r +HADD_U 011110 101.. ..... ..... ..... 010101 @3r +HSUB_S 011110 110.. ..... ..... ..... 010101 @3r +HSUB_U 011110 111.. ..... ..... ..... 010101 @3r + { - ANDI 011110 00 ........ ..... ..... 000000 @i8 - ORI 011110 01 ........ ..... ..... 000000 @i8 - NORI 011110 10 ........ ..... ..... 000000 @i8 - XORI 011110 11 ........ ..... ..... 000000 @i8 - BMNZI 011110 00 ........ ..... ..... 000001 @i8 - BMZI 011110 01 ........ ..... ..... 000001 @i8 - BSELI 011110 10 ........ ..... ..... 000001 @i8 - SHF 011110 .. ........ ..... ..... 000010 @i8_df - - ADDVI 011110 000 .. ..... ..... ..... 000110 @u5 - SUBVI 011110 001 .. ..... ..... ..... 000110 @u5 - MAXI_S 011110 010 .. ..... ..... ..... 000110 @s5 - MAXI_U 011110 011 .. ..... ..... ..... 000110 @u5 - MINI_S 011110 100 .. ..... ..... ..... 000110 @s5 - MINI_U 011110 101 .. ..... ..... ..... 000110 @u5 - - CEQI 011110 000 .. ..... ..... ..... 000111 @s5 - CLTI_S 011110 010 .. ..... ..... ..... 000111 @s5 - CLTI_U 011110 011 .. ..... ..... ..... 000111 @u5 - CLEI_S 011110 100 .. ..... ..... ..... 000111 @s5 - CLEI_U 011110 101 .. ..... ..... ..... 000111 @u5 - - LDI 011110 110 .. .......... ..... 000111 @ldi - - SLLI 011110 000 ....... ..... ..... 001001 @bit - SRAI 011110 001 ....... ..... ..... 001001 @bit - SRLI 011110 010 ....... ..... ..... 001001 @bit - BCLRI 011110 011 ....... ..... ..... 001001 @bit - BSETI 011110 100 ....... ..... ..... 001001 @bit - BNEGI 011110 101 ....... ..... ..... 001001 @bit - BINSLI 011110 110 ....... ..... ..... 001001 @bit - BINSRI 011110 111 ....... ..... ..... 001001 @bit - - SAT_S 011110 000 ....... ..... ..... 001010 @bit - SAT_U 011110 001 ....... ..... ..... 001010 @bit - SRARI 011110 010 ....... ..... ..... 001010 @bit - SRLRI 011110 011 ....... ..... ..... 001010 @bit - - SLL 011110 000.. ..... ..... ..... 001101 @3r - SRA 011110 001.. ..... ..... ..... 001101 @3r - SRL 011110 010.. ..... ..... ..... 001101 @3r - BCLR 011110 011.. ..... ..... ..... 001101 @3r - BSET 011110 100.. ..... ..... ..... 001101 @3r - BNEG 011110 101.. ..... ..... ..... 001101 @3r - BINSL 011110 110.. ..... ..... ..... 001101 @3r - BINSR 011110 111.. ..... ..... ..... 001101 @3r - - ADDV 011110 000.. ..... ..... ..... 001110 @3r - SUBV 011110 001.. ..... ..... ..... 001110 @3r - MAX_S 011110 010.. ..... ..... ..... 001110 @3r - MAX_U 011110 011.. ..... ..... ..... 001110 @3r - MIN_S 011110 100.. ..... ..... ..... 001110 @3r - MIN_U 011110 101.. ..... ..... ..... 001110 @3r - MAX_A 011110 110.. ..... ..... ..... 001110 @3r - MIN_A 011110 111.. ..... ..... ..... 001110 @3r - - CEQ 011110 000.. ..... ..... ..... 001111 @3r - CLT_S 011110 010.. ..... ..... ..... 001111 @3r - CLT_U 011110 011.. ..... ..... ..... 001111 @3r - CLE_S 011110 100.. ..... ..... ..... 001111 @3r - CLE_U 011110 101.. ..... ..... ..... 001111 @3r - - ADD_A 011110 000.. ..... ..... ..... 010000 @3r - ADDS_A 011110 001.. ..... ..... ..... 010000 @3r - ADDS_S 011110 010.. ..... ..... ..... 010000 @3r - ADDS_U 011110 011.. ..... ..... ..... 010000 @3r - AVE_S 011110 100.. ..... ..... ..... 010000 @3r - AVE_U 011110 101.. ..... ..... ..... 010000 @3r - AVER_S 011110 110.. ..... ..... ..... 010000 @3r - AVER_U 011110 111.. ..... ..... ..... 010000 @3r - - SUBS_S 011110 000.. ..... ..... ..... 010001 @3r - SUBS_U 011110 001.. ..... ..... ..... 010001 @3r - SUBSUS_U 011110 010.. ..... ..... ..... 010001 @3r - SUBSUU_S 011110 011.. ..... ..... ..... 010001 @3r - ASUB_S 011110 100.. ..... ..... ..... 010001 @3r - ASUB_U 011110 101.. ..... ..... ..... 010001 @3r - - MULV 011110 000.. ..... ..... ..... 010010 @3r - MADDV 011110 001.. ..... ..... ..... 010010 @3r - MSUBV 011110 010.. ..... ..... ..... 010010 @3r - DIV_S 011110 100.. ..... ..... ..... 010010 @3r - DIV_U 011110 101.. ..... ..... ..... 010010 @3r - MOD_S 011110 110.. ..... ..... ..... 010010 @3r - MOD_U 011110 111.. ..... ..... ..... 010010 @3r - - DOTP_S 011110 000.. ..... ..... ..... 010011 @3r - DOTP_U 011110 001.. ..... ..... ..... 010011 @3r - DPADD_S 011110 010.. ..... ..... ..... 010011 @3r - DPADD_U 011110 011.. ..... ..... ..... 010011 @3r - DPSUB_S 011110 100.. ..... ..... ..... 010011 @3r - DPSUB_U 011110 101.. ..... ..... ..... 010011 @3r - - SLD 011110 000 .. ..... ..... ..... 010100 @3r - SPLAT 011110 001 .. ..... ..... ..... 010100 @3r - PCKEV 011110 010 .. ..... ..... ..... 010100 @3r - PCKOD 011110 011 .. ..... ..... ..... 010100 @3r - ILVL 011110 100 .. ..... ..... ..... 010100 @3r - ILVR 011110 101 .. ..... ..... ..... 010100 @3r - ILVEV 011110 110 .. ..... ..... ..... 010100 @3r - ILVOD 011110 111 .. ..... ..... ..... 010100 @3r - - VSHF 011110 000 .. ..... ..... ..... 010101 @3r - SRAR 011110 001 .. ..... ..... ..... 010101 @3r - SRLR 011110 010 .. ..... ..... ..... 010101 @3r - HADD_S 011110 100.. ..... ..... ..... 010101 @3r - HADD_U 011110 101.. ..... ..... ..... 010101 @3r - HSUB_S 011110 110.. ..... ..... ..... 010101 @3r - HSUB_U 011110 111.. ..... ..... ..... 010101 @3r - - { - CTCMSA 011110 0000111110 ..... ..... 011001 @elm - SLDI 011110 0000 ...... ..... ..... 011001 @elm_df - } - { - CFCMSA 011110 0001111110 ..... ..... 011001 @elm - SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df - } - { - MOVE_V 011110 0010111110 ..... ..... 011001 @elm - COPY_S 011110 0010 ...... ..... ..... 011001 @elm_df - } - COPY_U 011110 0011 ...... ..... ..... 011001 @elm_df - INSERT 011110 0100 ...... ..... ..... 011001 @elm_df - INSVE 011110 0101 ...... ..... ..... 011001 @elm_df - - FCAF 011110 0000 . ..... ..... ..... 011010 @3rf_w - FCUN 011110 0001 . ..... ..... ..... 011010 @3rf_w - FCEQ 011110 0010 . ..... ..... ..... 011010 @3rf_w - FCUEQ 011110 0011 . ..... ..... ..... 011010 @3rf_w - FCLT 011110 0100 . ..... ..... ..... 011010 @3rf_w - FCULT 011110 0101 . ..... ..... ..... 011010 @3rf_w - FCLE 011110 0110 . ..... ..... ..... 011010 @3rf_w - FCULE 011110 0111 . ..... ..... ..... 011010 @3rf_w - FSAF 011110 1000 . ..... ..... ..... 011010 @3rf_w - FSUN 011110 1001 . ..... ..... ..... 011010 @3rf_w - FSEQ 011110 1010 . ..... ..... ..... 011010 @3rf_w - FSUEQ 011110 1011 . ..... ..... ..... 011010 @3rf_w - FSLT 011110 1100 . ..... ..... ..... 011010 @3rf_w - FSULT 011110 1101 . ..... ..... ..... 011010 @3rf_w - FSLE 011110 1110 . ..... ..... ..... 011010 @3rf_w - FSULE 011110 1111 . ..... ..... ..... 011010 @3rf_w - - FADD 011110 0000 . ..... ..... ..... 011011 @3rf_w - FSUB 011110 0001 . ..... ..... ..... 011011 @3rf_w - FMUL 011110 0010 . ..... ..... ..... 011011 @3rf_w - FDIV 011110 0011 . ..... ..... ..... 011011 @3rf_w - FMADD 011110 0100 . ..... ..... ..... 011011 @3rf_w - FMSUB 011110 0101 . ..... ..... ..... 011011 @3rf_w - FEXP2 011110 0111 . ..... ..... ..... 011011 @3rf_w - FEXDO 011110 1000 . ..... ..... ..... 011011 @3rf_w - FTQ 011110 1010 . ..... ..... ..... 011011 @3rf_w - FMIN 011110 1100 . ..... ..... ..... 011011 @3rf_w - FMIN_A 011110 1101 . ..... ..... ..... 011011 @3rf_w - FMAX 011110 1110 . ..... ..... ..... 011011 @3rf_w - FMAX_A 011110 1111 . ..... ..... ..... 011011 @3rf_w - - FCOR 011110 0001 . ..... ..... ..... 011100 @3rf_w - FCUNE 011110 0010 . ..... ..... ..... 011100 @3rf_w - FCNE 011110 0011 . ..... ..... ..... 011100 @3rf_w - MUL_Q 011110 0100 . ..... ..... ..... 011100 @3rf_h - MADD_Q 011110 0101 . ..... ..... ..... 011100 @3rf_h - MSUB_Q 011110 0110 . ..... ..... ..... 011100 @3rf_h - FSOR 011110 1001 . ..... ..... ..... 011100 @3rf_w - FSUNE 011110 1010 . ..... ..... ..... 011100 @3rf_w - FSNE 011110 1011 . ..... ..... ..... 011100 @3rf_w - MULR_Q 011110 1100 . ..... ..... ..... 011100 @3rf_h - MADDR_Q 011110 1101 . ..... ..... ..... 011100 @3rf_h - MSUBR_Q 011110 1110 . ..... ..... ..... 011100 @3rf_h - - AND_V 011110 00000 ..... ..... ..... 011110 @vec - OR_V 011110 00001 ..... ..... ..... 011110 @vec - NOR_V 011110 00010 ..... ..... ..... 011110 @vec - XOR_V 011110 00011 ..... ..... ..... 011110 @vec - BMNZ_V 011110 00100 ..... ..... ..... 011110 @vec - BMZ_V 011110 00101 ..... ..... ..... 011110 @vec - BSEL_V 011110 00110 ..... ..... ..... 011110 @vec - FILL 011110 11000000 .. ..... ..... 011110 @2r - PCNT 011110 11000001 .. ..... ..... 011110 @2r - NLOC 011110 11000010 .. ..... ..... 011110 @2r - NLZC 011110 11000011 .. ..... ..... 011110 @2r - FCLASS 011110 110010000 . ..... ..... 011110 @2rf - FTRUNC_S 011110 110010001 . ..... ..... 011110 @2rf - FTRUNC_U 011110 110010010 . ..... ..... 011110 @2rf - FSQRT 011110 110010011 . ..... ..... 011110 @2rf - FRSQRT 011110 110010100 . ..... ..... 011110 @2rf - FRCP 011110 110010101 . ..... ..... 011110 @2rf - FRINT 011110 110010110 . ..... ..... 011110 @2rf - FLOG2 011110 110010111 . ..... ..... 011110 @2rf - FEXUPL 011110 110011000 . ..... ..... 011110 @2rf - FEXUPR 011110 110011001 . ..... ..... 011110 @2rf - FFQL 011110 110011010 . ..... ..... 011110 @2rf - FFQR 011110 110011011 . ..... ..... 011110 @2rf - FTINT_S 011110 110011100 . ..... ..... 011110 @2rf - FTINT_U 011110 110011101 . ..... ..... 011110 @2rf - FFINT_S 011110 110011110 . ..... ..... 011110 @2rf - FFINT_U 011110 110011111 . ..... ..... 011110 @2rf - - LD 011110 .......... ..... ..... 1000 .. @ldst - ST 011110 .......... ..... ..... 1001 .. @ldst + CTCMSA 011110 0000111110 ..... ..... 011001 @elm + SLDI 011110 0000 ...... ..... ..... 011001 @elm_df } +{ + CFCMSA 011110 0001111110 ..... ..... 011001 @elm + SPLATI 011110 0001 ...... ..... ..... 011001 @elm_df +} +{ + MOVE_V 011110 0010111110 ..... ..... 011001 @elm + COPY_S 011110 0010 ...... ..... ..... 011001 @elm_df +} +COPY_U 011110 0011 ...... ..... ..... 011001 @elm_df +INSERT 011110 0100 ...... ..... ..... 011001 @elm_df +INSVE 011110 0101 ...... ..... ..... 011001 @elm_df + +FCAF 011110 0000 . ..... ..... ..... 011010 @3rf_w +FCUN 011110 0001 . ..... ..... ..... 011010 @3rf_w +FCEQ 011110 0010 . ..... ..... ..... 011010 @3rf_w +FCUEQ 011110 0011 . ..... ..... ..... 011010 @3rf_w +FCLT 011110 0100 . ..... ..... ..... 011010 @3rf_w +FCULT 011110 0101 . ..... ..... ..... 011010 @3rf_w +FCLE 011110 0110 . ..... ..... ..... 011010 @3rf_w +FCULE 011110 0111 . ..... ..... ..... 011010 @3rf_w +FSAF 011110 1000 . ..... ..... ..... 011010 @3rf_w +FSUN 011110 1001 . ..... ..... ..... 011010 @3rf_w +FSEQ 011110 1010 . ..... ..... ..... 011010 @3rf_w +FSUEQ 011110 1011 . ..... ..... ..... 011010 @3rf_w +FSLT 011110 1100 . ..... ..... ..... 011010 @3rf_w +FSULT 011110 1101 . ..... ..... ..... 011010 @3rf_w +FSLE 011110 1110 . ..... ..... ..... 011010 @3rf_w +FSULE 011110 1111 . ..... ..... ..... 011010 @3rf_w + +FADD 011110 0000 . ..... ..... ..... 011011 @3rf_w +FSUB 011110 0001 . ..... ..... ..... 011011 @3rf_w +FMUL 011110 0010 . ..... ..... ..... 011011 @3rf_w +FDIV 011110 0011 . ..... ..... ..... 011011 @3rf_w +FMADD 011110 0100 . ..... ..... ..... 011011 @3rf_w +FMSUB 011110 0101 . ..... ..... ..... 011011 @3rf_w +FEXP2 011110 0111 . ..... ..... ..... 011011 @3rf_w +FEXDO 011110 1000 . ..... ..... ..... 011011 @3rf_w +FTQ 011110 1010 . ..... ..... ..... 011011 @3rf_w +FMIN 011110 1100 . ..... ..... ..... 011011 @3rf_w +FMIN_A 011110 1101 . ..... ..... ..... 011011 @3rf_w +FMAX 011110 1110 . ..... ..... ..... 011011 @3rf_w +FMAX_A 011110 1111 . ..... ..... ..... 011011 @3rf_w + +FCOR 011110 0001 . ..... ..... ..... 011100 @3rf_w +FCUNE 011110 0010 . ..... ..... ..... 011100 @3rf_w +FCNE 011110 0011 . ..... ..... ..... 011100 @3rf_w +MUL_Q 011110 0100 . ..... ..... ..... 011100 @3rf_h +MADD_Q 011110 0101 . ..... ..... ..... 011100 @3rf_h +MSUB_Q 011110 0110 . ..... ..... ..... 011100 @3rf_h +FSOR 011110 1001 . ..... ..... ..... 011100 @3rf_w +FSUNE 011110 1010 . ..... ..... ..... 011100 @3rf_w +FSNE 011110 1011 . ..... ..... ..... 011100 @3rf_w +MULR_Q 011110 1100 . ..... ..... ..... 011100 @3rf_h +MADDR_Q 011110 1101 . ..... ..... ..... 011100 @3rf_h +MSUBR_Q 011110 1110 . ..... ..... ..... 011100 @3rf_h + +AND_V 011110 00000 ..... ..... ..... 011110 @vec +OR_V 011110 00001 ..... ..... ..... 011110 @vec +NOR_V 011110 00010 ..... ..... ..... 011110 @vec +XOR_V 011110 00011 ..... ..... ..... 011110 @vec +BMNZ_V 011110 00100 ..... ..... ..... 011110 @vec +BMZ_V 011110 00101 ..... ..... ..... 011110 @vec +BSEL_V 011110 00110 ..... ..... ..... 011110 @vec +FILL 011110 11000000 .. ..... ..... 011110 @2r +PCNT 011110 11000001 .. ..... ..... 011110 @2r +NLOC 011110 11000010 .. ..... ..... 011110 @2r +NLZC 011110 11000011 .. ..... ..... 011110 @2r +FCLASS 011110 110010000 . ..... ..... 011110 @2rf +FTRUNC_S 011110 110010001 . ..... ..... 011110 @2rf +FTRUNC_U 011110 110010010 . ..... ..... 011110 @2rf +FSQRT 011110 110010011 . ..... ..... 011110 @2rf +FRSQRT 011110 110010100 . ..... ..... 011110 @2rf +FRCP 011110 110010101 . ..... ..... 011110 @2rf +FRINT 011110 110010110 . ..... ..... 011110 @2rf +FLOG2 011110 110010111 . ..... ..... 011110 @2rf +FEXUPL 011110 110011000 . ..... ..... 011110 @2rf +FEXUPR 011110 110011001 . ..... ..... 011110 @2rf +FFQL 011110 110011010 . ..... ..... 011110 @2rf +FFQR 011110 110011011 . ..... ..... 011110 @2rf +FTINT_S 011110 110011100 . ..... ..... 011110 @2rf +FTINT_U 011110 110011101 . ..... ..... 011110 @2rf +FFINT_S 011110 110011110 . ..... ..... 011110 @2rf +FFINT_U 011110 110011111 . ..... ..... 011110 @2rf + +LD 011110 .......... ..... ..... 1000 .. @ldst +ST 011110 .......... ..... ..... 1001 .. @ldst From ba7b6f025ba84a5a32c6cd07ffc8634ad2b33c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Oct 2021 15:58:42 +0200 Subject: [PATCH 1071/1334] target/mips: Fix Loongson-3A4000 MSAIR config register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using the Loongson-3A4000 CPU, the MSAIR is returned with a zero value (because unimplemented). Checking on real hardware, this value appears incorrect: $ cat /proc/cpuinfo system type : generic-loongson-machine machine : loongson,generic cpu model : Loongson-3 V0.4 FPU V0.1 model name : Loongson-3A R4 (Loongson-3A4000) @ 1800MHz isa : mips1 mips2 mips3 mips4 mips5 mips32r1 mips32r2 mips64r1 mips64r2 ASEs implemented : vz msa loongson-mmi loongson-cam loongson-ext loongson-ext2 ... Checking the CFCMSA opcode result with gdb we get 0x60140: Breakpoint 1, 0x00000001200037c4 in main () 1: x/i $pc => 0x1200037c4 : cfcmsa v0,msa_ir (gdb) si 0x00000001200037c8 in main () (gdb) i r v0 v0: 0x60140 MSAIR bits 17 and 18 are "reserved" per the spec revision 1.12, so mask them out, and set MSAIR=0x0140 for the Loongson-3A4000 CPU model added in commit af868995e1b. Cc: Huacai Chen Reviewed-by: Jiaxun Yang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211026180920.1085516-1-f4bug@amsat.org> --- target/mips/cpu-defs.c.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index cbc45fcb0e..ee8b322a56 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -886,6 +886,7 @@ const mips_def_t mips_defs[] = (0x1 << FCR0_D) | (0x1 << FCR0_S), .CP1_fcr31 = 0, .CP1_fcr31_rw_bitmask = 0xFF83FFFF, + .MSAIR = (0x01 << MSAIR_ProcID) | (0x40 << MSAIR_Rev), .SEGBITS = 48, .PABITS = 48, .insn_flags = CPU_MIPS64R2 | INSN_LOONGSON3A | From 675cf7817c9c0171af387faf8169bd5b8bd3d303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Oct 2021 19:56:19 +0200 Subject: [PATCH 1072/1334] target/mips: Remove obsolete FCR0_HAS2008 comment on P5600 CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FCR0_HAS2008 flag has been enabled in commit ba5c79f2622 ("target-mips: indicate presence of IEEE 754-2008 FPU in R6/R5+MSA CPUs"), so remove the obsolete FIXME comment. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Jiaxun Yang Reviewed-by: Richard Henderson Message-Id: <20211028212103.2126176-1-f4bug@amsat.org> --- target/mips/cpu-defs.c.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index ee8b322a56..582f940070 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -369,7 +369,6 @@ const mips_def_t mips_defs[] = * Config3: VZ, CTXTC, CDMM, TL * Config4: MMUExtDef * Config5: MRP - * FIR(FCR0): Has2008 * */ .name = "P5600", .CP0_PRid = 0x0001A800, From d3647ef1fdaf4dcaecb794b525e9def1e5d81245 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 25 Oct 2021 13:33:49 +0200 Subject: [PATCH 1073/1334] usb/uhci: Misc clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a comment for coding style so subsequent patch will not get checkpatch error and simplify and shorten uhci_update_irq(). Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-uhci.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 0cb02a6432..c557566ec2 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -290,7 +290,7 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t td_addr) static void uhci_update_irq(UHCIState *s) { - int level; + int level = 0; if (((s->status2 & 1) && (s->intr & (1 << 2))) || ((s->status2 & 2) && (s->intr & (1 << 3))) || ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) || @@ -298,8 +298,6 @@ static void uhci_update_irq(UHCIState *s) (s->status & UHCI_STS_HSERR) || (s->status & UHCI_STS_HCPERR)) { level = 1; - } else { - level = 0; } pci_set_irq(&s->dev, level); } @@ -1170,8 +1168,7 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp) pci_conf[PCI_CLASS_PROG] = 0x00; /* TODO: reset value should be 0. */ - pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - + pci_conf[USB_SBRN] = USB_RELEASE_1; /* release number */ pci_config_set_interrupt_pin(pci_conf, u->info.irq_pin + 1); if (s->masterbus) { From ece29df33b8e0e35760c4f76b0cf7b1af928b0b4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 25 Oct 2021 13:33:49 +0200 Subject: [PATCH 1074/1334] usb/uhci: Disallow user creating a vt82c686-uhci-pci device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because this device only works as part of VIA superio chips set user creatable to false. Since the class init method is common for UHCI variants introduce a flag in UHCIInfo for this. Signed-off-by: BALATON Zoltan Reviewed-by: Gerd Hoffmann Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-uhci.c | 3 +++ hw/usb/hcd-uhci.h | 1 + hw/usb/vt82c686-uhci-pci.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index c557566ec2..7d26e35194 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1282,6 +1282,9 @@ void uhci_data_class_init(ObjectClass *klass, void *data) } else { device_class_set_props(dc, uhci_properties_standalone); } + if (info->notuser) { + dc->user_creatable = false; + } u->info = *info; } diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h index e61d8fcb19..316693f80b 100644 --- a/hw/usb/hcd-uhci.h +++ b/hw/usb/hcd-uhci.h @@ -85,6 +85,7 @@ typedef struct UHCIInfo { uint8_t irq_pin; void (*realize)(PCIDevice *dev, Error **errp); bool unplug; + bool notuser; /* disallow user_creatable */ } UHCIInfo; void uhci_data_class_init(ObjectClass *klass, void *data); diff --git a/hw/usb/vt82c686-uhci-pci.c b/hw/usb/vt82c686-uhci-pci.c index b109c21603..ea262e6d70 100644 --- a/hw/usb/vt82c686-uhci-pci.c +++ b/hw/usb/vt82c686-uhci-pci.c @@ -25,6 +25,8 @@ static UHCIInfo uhci_info[] = { .irq_pin = 3, .realize = usb_uhci_vt82c686b_realize, .unplug = true, + /* Reason: only works as USB function of VT82xx superio chips */ + .notuser = true, } }; From e4f5b93986cdcc137487ebea6e84b9d9e82e0990 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 25 Oct 2021 13:33:49 +0200 Subject: [PATCH 1075/1334] usb/uhci: Replace pci_set_irq with qemu_set_irq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using pci_set_irq, store the irq in the device state and use it explicitly so variants having different interrupt handling can use their own. Signed-off-by: BALATON Zoltan Reviewed-by: Gerd Hoffmann Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-uhci.c | 4 +++- hw/usb/hcd-uhci.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 7d26e35194..d1b5657d72 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -31,6 +31,7 @@ #include "hw/usb/uhci-regs.h" #include "migration/vmstate.h" #include "hw/pci/pci.h" +#include "hw/irq.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/timer.h" @@ -299,7 +300,7 @@ static void uhci_update_irq(UHCIState *s) (s->status & UHCI_STS_HCPERR)) { level = 1; } - pci_set_irq(&s->dev, level); + qemu_set_irq(s->irq, level); } static void uhci_reset(DeviceState *dev) @@ -1170,6 +1171,7 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp) /* TODO: reset value should be 0. */ pci_conf[USB_SBRN] = USB_RELEASE_1; /* release number */ pci_config_set_interrupt_pin(pci_conf, u->info.irq_pin + 1); + s->irq = pci_allocate_irq(dev); if (s->masterbus) { USBPort *ports[NB_PORTS]; diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h index 316693f80b..c85ab7868e 100644 --- a/hw/usb/hcd-uhci.h +++ b/hw/usb/hcd-uhci.h @@ -60,7 +60,7 @@ typedef struct UHCIState { uint32_t frame_bandwidth; bool completions_only; UHCIPort ports[NB_PORTS]; - + qemu_irq irq; /* Interrupts that should be raised at the end of the current frame. */ uint32_t pending_int_mask; From 4f3b0a4d757f40cbd51987e4c7cf7cbd10951178 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 25 Oct 2021 13:33:49 +0200 Subject: [PATCH 1076/1334] hw/usb/vt82c686-uhci-pci: Use ISA instead of PCI interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This device is part of a superio/ISA bridge chip and IRQs from it are routed to an ISA interrupt set by the Interrupt Line PCI config register. Implement this in a vt82c686-uhci-pci specific irq handler Using via_isa_set_irq(). Signed-off-by: BALATON Zoltan Reviewed-by: Jiaxun Yang Reviewed-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Message-Id: <8d7ed385e33a847d8ddc669163a68b5ca57f82ce.1635161629.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/vt82c686-uhci-pci.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/usb/vt82c686-uhci-pci.c b/hw/usb/vt82c686-uhci-pci.c index ea262e6d70..0bf2b72ff0 100644 --- a/hw/usb/vt82c686-uhci-pci.c +++ b/hw/usb/vt82c686-uhci-pci.c @@ -1,6 +1,17 @@ #include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/isa/vt82c686.h" #include "hcd-uhci.h" +static void uhci_isa_set_irq(void *opaque, int irq_num, int level) +{ + UHCIState *s = opaque; + uint8_t irq = pci_get_byte(s->dev.config + PCI_INTERRUPT_LINE); + if (irq > 0 && irq < 15) { + via_isa_set_irq(pci_get_function_0(&s->dev), irq, level); + } +} + static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) { UHCIState *s = UHCI(dev); @@ -14,6 +25,8 @@ static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) pci_set_long(pci_conf + 0xc0, 0x00002000); usb_uhci_common_realize(dev, errp); + object_unref(s->irq); + s->irq = qemu_allocate_irq(uhci_isa_set_irq, s, 0); } static UHCIInfo uhci_info[] = { From 6f08c9c5316a80a049d4861eaac5844466ba3eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 1 Nov 2021 12:25:43 +0100 Subject: [PATCH 1077/1334] Revert "elf: Relax MIPS' elf_check_arch() to accept EM_NANOMIPS too" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the "P32 Porting Guide" (rev 1.2) [1], chapter 2: p32 ABI Overview ---------------- The Application Binary Interface, or ABI, is the set of rules that all binaries must follow in order to run on a nanoMIPS system. This includes, for example, object file format, instruction set, data layout, subroutine calling convention, and system call numbers. The ABI is one part of the mechanism that maintains binary compatibility across all nanoMIPS platforms. p32 improves on o32 to provide an ABI that is efficient in both code density and performance. p32 is required for the nanoMIPS architecture. So far QEMU only support the MIPS o32 / n32 / n64 ABIs. The p32 ABI is not implemented, therefore we can not run any nanoMIPS binary. Revert commit f72541f3a59 ("elf: Relax MIPS' elf_check_arch() to accept EM_NANOMIPS too"). See also the "ELF ABI Supplement" [2]. [1] http://codescape.mips.com/components/toolchain/nanomips/2019.03-01/docs/MIPS_nanoMIPS_p32_ABI_Porting_Guide_01_02_DN00184.pdf [2] http://codescape.mips.com/components/toolchain/nanomips/2019.03-01/docs/MIPS_nanoMIPS_ABI_supplement_01_03_DN00179.pdf Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211101114800.2692157-1-f4bug@amsat.org> --- linux-user/elfload.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f9b8261692..5da8c02d08 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -925,8 +925,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #endif #define ELF_ARCH EM_MIPS -#define elf_check_arch(x) ((x) == EM_MIPS || (x) == EM_NANOMIPS) - #ifdef TARGET_ABI_MIPSN32 #define elf_check_abi(x) ((x) & EF_MIPS_ABI2) #else From 4a613bd86256543b9735307674a84e167a585687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Oct 2021 18:49:27 +0200 Subject: [PATCH 1078/1334] block/nvme: Automatically free qemu_memalign() with QEMU_AUTO_VFREE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 4d324c0bf65 ("introduce QEMU_AUTO_VFREE") buffers allocated by qemu_memalign() can automatically freed when using the QEMU_AUTO_VFREE macro. Use it to simplify a bit. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211006164931.172349-2-philmd@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/nvme.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/block/nvme.c b/block/nvme.c index 1cc7b62bb4..fefcc04abe 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -514,10 +514,10 @@ static bool nvme_identify(BlockDriverState *bs, int namespace, Error **errp) { BDRVNVMeState *s = bs->opaque; bool ret = false; - union { + QEMU_AUTO_VFREE union { NvmeIdCtrl ctrl; NvmeIdNs ns; - } *id; + } *id = NULL; NvmeLBAF *lbaf; uint16_t oncs; int r; @@ -595,7 +595,6 @@ static bool nvme_identify(BlockDriverState *bs, int namespace, Error **errp) s->blkshift = lbaf->ds; out: qemu_vfio_dma_unmap(s->vfio, id); - qemu_vfree(id); return ret; } @@ -1219,7 +1218,7 @@ static int nvme_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, { BDRVNVMeState *s = bs->opaque; int r; - uint8_t *buf = NULL; + QEMU_AUTO_VFREE uint8_t *buf = NULL; QEMUIOVector local_qiov; size_t len = QEMU_ALIGN_UP(bytes, qemu_real_host_page_size); assert(QEMU_IS_ALIGNED(offset, s->page_size)); @@ -1246,7 +1245,6 @@ static int nvme_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, if (!r && !is_write) { qemu_iovec_from_buf(qiov, 0, buf, bytes); } - qemu_vfree(buf); return r; } @@ -1365,7 +1363,7 @@ static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs, BDRVNVMeState *s = bs->opaque; NVMeQueuePair *ioq = s->queues[INDEX_IO(0)]; NVMeRequest *req; - NvmeDsmRange *buf; + QEMU_AUTO_VFREE NvmeDsmRange *buf = NULL; QEMUIOVector local_qiov; int ret; @@ -1440,7 +1438,6 @@ static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs, trace_nvme_dsm_done(s, offset, bytes, ret); out: qemu_iovec_destroy(&local_qiov); - qemu_vfree(buf); return ret; } From 53cedeaaee9d585bd07eb921fa5557c64531f69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Oct 2021 18:49:28 +0200 Subject: [PATCH 1079/1334] block/nvme: Display CQ/SQ pointer in nvme_free_queue_pair() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For debugging purpose it is helpful to know the CQ/SQ pointers. We already have a trace event in nvme_free_queue_pair(), extend it to report these pointer addresses. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211006164931.172349-3-philmd@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/nvme.c | 2 +- block/trace-events | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/block/nvme.c b/block/nvme.c index fefcc04abe..0c94799a54 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -185,7 +185,7 @@ static bool nvme_init_queue(BDRVNVMeState *s, NVMeQueue *q, static void nvme_free_queue_pair(NVMeQueuePair *q) { - trace_nvme_free_queue_pair(q->index, q); + trace_nvme_free_queue_pair(q->index, q, &q->cq, &q->sq); if (q->completion_bh) { qemu_bh_delete(q->completion_bh); } diff --git a/block/trace-events b/block/trace-events index ab56edacb4..549090d453 100644 --- a/block/trace-events +++ b/block/trace-events @@ -157,7 +157,7 @@ nvme_dsm_done(void *s, int64_t offset, int64_t bytes, int ret) "s %p offset 0x%" nvme_dma_map_flush(void *s) "s %p" nvme_free_req_queue_wait(void *s, unsigned q_index) "s %p q #%u" nvme_create_queue_pair(unsigned q_index, void *q, size_t size, void *aio_context, int fd) "index %u q %p size %zu aioctx %p fd %d" -nvme_free_queue_pair(unsigned q_index, void *q) "index %u q %p" +nvme_free_queue_pair(unsigned q_index, void *q, void *cq, void *sq) "index %u q %p cq %p sq %p" nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" From a8951438946d72d74c9bdbdb38fce95aa2973a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Oct 2021 18:49:29 +0200 Subject: [PATCH 1080/1334] block/nvme: Extract nvme_free_queue() from nvme_free_queue_pair() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of duplicating code, extract the common helper to free a single queue. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211006164931.172349-4-philmd@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/nvme.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/block/nvme.c b/block/nvme.c index 0c94799a54..e4f336d79c 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -183,15 +183,20 @@ static bool nvme_init_queue(BDRVNVMeState *s, NVMeQueue *q, return r == 0; } +static void nvme_free_queue(NVMeQueue *q) +{ + qemu_vfree(q->queue); +} + static void nvme_free_queue_pair(NVMeQueuePair *q) { trace_nvme_free_queue_pair(q->index, q, &q->cq, &q->sq); if (q->completion_bh) { qemu_bh_delete(q->completion_bh); } + nvme_free_queue(&q->sq); + nvme_free_queue(&q->cq); qemu_vfree(q->prp_list_pages); - qemu_vfree(q->sq.queue); - qemu_vfree(q->cq.queue); qemu_mutex_destroy(&q->lock); g_free(q); } From b8c9ed15fd0515314a11baa9291ea051a6867f8f Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 4 Aug 2021 09:23:27 -0400 Subject: [PATCH 1081/1334] Makefile: Fix gtags generation We should use "-print" or otherwise all "-prone" is ignored. Signed-off-by: Peter Xu Message-Id: <20210804132328.41353-2-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fe9415ac64..1b36bf2af4 100644 --- a/Makefile +++ b/Makefile @@ -256,7 +256,7 @@ gtags: "GTAGS", "Remove old $@ files") $(call quiet-command, \ (cd $(SRC_PATH) && \ - $(find-src-path) | gtags -f -), \ + $(find-src-path) -print | gtags -f -), \ "GTAGS", "Re-index $(SRC_PATH)") .PHONY: TAGS From af7d106e3e57e0526d571063096e2ba3d7e6ab8a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 4 Aug 2021 09:23:28 -0400 Subject: [PATCH 1082/1334] Makefile: Fix cscope issues on MacOS and soft links This patch fixes actually two issues with 'make cscope'. Firstly, it fixes the command for MacOS "find" command as MacOS will append the full path of "$(SRC_PATH)/" before each found entry, then after the final "./" replacement trick it'll look like (e.g., "qapi/qmp-dispatch.c"): /qapi/qmp-dispatch.c Which will point to the root directory instead. Fix it by simply remove the "/" in "$(SRC_PATH)/" of "find-src-path", then it'll work for at least both Linux and MacOS. The other OS-independent issue is to start proactively ignoring soft links when generating tags, otherwise by default on master branch we'll see this error when "make cscope": cscope: cannot find file subprojects/libvhost-user/include/atomic.h This patch should fix the two issues altogether. Signed-off-by: Peter Xu Message-Id: <20210804132328.41353-3-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1b36bf2af4..74c5b46d38 100644 --- a/Makefile +++ b/Makefile @@ -235,7 +235,8 @@ distclean: clean rm -f linux-headers/asm rm -Rf .sdk -find-src-path = find "$(SRC_PATH)/" -path "$(SRC_PATH)/meson" -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \) +find-src-path = find "$(SRC_PATH)" -path "$(SRC_PATH)/meson" -prune -o \ + -type l -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \) .PHONY: ctags ctags: From ffd205ef2901bd65fcfbd09a98c0ff7cfcec5e4d Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Thu, 5 Aug 2021 20:25:45 +0100 Subject: [PATCH 1083/1334] Partially revert "build: -no-pie is no functional linker flag" This partially reverts commit bbd2d5a8120771ec59b86a80a1f51884e0a26e53. This commit was misguided and broke using --disable-pie on any distro that enables PIE by default in their compiler driver, including Debian and its derivatives. Whilst -no-pie is not a linker flag, it is a compiler driver flag that ensures -pie is not automatically passed by it to the linker. Without it, all compile_prog checks will fail as any code built with the explicit -fno-pie will fail to link with the implicit default -pie due to trying to use position-dependent relocations. The only bug that needed fixing was LDFLAGS_NOPIE being used as a flag for the linker itself in pc-bios/optionrom/Makefile. Note this does not reinstate exporting LDFLAGS_NOPIE, as it is unused, since the only previous use was the one that should not have existed. I have also updated the comment for the -fno-pie and -no-pie checks to reflect what they're actually needed for. Fixes: bbd2d5a8120771ec59b86a80a1f51884e0a26e53 Cc: Christian Ehrhardt Cc: Paolo Bonzini Cc: qemu-stable@nongnu.org Signed-off-by: Jessica Clarke Message-Id: <20210805192545.38279-1-jrtc27@jrtc27.com> Signed-off-by: Paolo Bonzini --- configure | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 039467c04b..07cb7b412a 100755 --- a/configure +++ b/configure @@ -1771,9 +1771,11 @@ static THREAD int tls_var; int main(void) { return tls_var; } EOF -# Check we support --no-pie first; we will need this for building ROMs. +# Check we support -fno-pie and -no-pie first; we will need the former for +# building ROMs, and both for everything if --disable-pie is passed. if compile_prog "-Werror -fno-pie" "-no-pie"; then CFLAGS_NOPIE="-fno-pie" + LDFLAGS_NOPIE="-no-pie" fi if test "$static" = "yes"; then @@ -1789,6 +1791,7 @@ if test "$static" = "yes"; then fi elif test "$pie" = "no"; then CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS" + CONFIGURE_LDFLAGS="$LDFLAGS_NOPIE $CONFIGURE_LDFLAGS" elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS" From 984099911275cd4b703e0d9c35b37dd52928acdd Mon Sep 17 00:00:00 2001 From: Helge Konetzka Date: Wed, 15 Sep 2021 12:56:34 +0200 Subject: [PATCH 1084/1334] configure/optionrom: Fix MSYS2 multiboot.bin issue This patch enables native builds on MSYS2 with symlinks disabled. Signed-off-by: Helge Konetzka Message-Id: <2b5ab039-8495-b55f-03f1-ecfd996907a9@zapateado.de> Signed-off-by: Paolo Bonzini --- configure | 4 ++++ pc-bios/optionrom/Makefile | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 07cb7b412a..03162008aa 100755 --- a/configure +++ b/configure @@ -3963,6 +3963,10 @@ for rom in seabios; do echo "RANLIB=$ranlib" >> $config_mak done +config_mak=pc-bios/optionrom/config.mak +echo "# Automatically generated by configure - do not modify" > $config_mak +echo "TOPSRC_DIR=$source_path" >> $config_mak + if test "$skip_meson" = no; then cross="config-meson.cross.new" meson_quote() { diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index 30771f8d17..3482508a86 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -1,6 +1,5 @@ -CURRENT_MAKEFILE := $(realpath $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) -SRC_DIR := $(dir $(CURRENT_MAKEFILE)) -TOPSRC_DIR := $(SRC_DIR)/../.. +include config.mak +SRC_DIR := $(TOPSRC_DIR)/pc-bios/optionrom VPATH = $(SRC_DIR) all: multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin From a89b34be5e2550949979c3184d00d5ab3e8dd707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Wed, 13 Oct 2021 20:27:13 +0200 Subject: [PATCH 1085/1334] util: Make some iova_tree parameters const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As qemu guidelines: Unless a pointer is used to modify the pointed-to storage, give it the "const" attribute. In the particular case of iova_tree_find it allows to enforce what is requested by its comment, since the compiler would shout in case of modifying or freeing the const-qualified returned pointer. Signed-off-by: Eugenio Pérez Acked-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211013182713.888753-2-eperezma@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/intel_iommu.c | 2 +- include/qemu/iova-tree.h | 8 ++++---- util/iova-tree.c | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 75f075547f..33a8af9191 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1105,7 +1105,7 @@ static int vtd_page_walk_one(IOMMUTLBEvent *event, vtd_page_walk_info *info) .translated_addr = entry->translated_addr, .perm = entry->perm, }; - DMAMap *mapped = iova_tree_find(as->iova_tree, &target); + const DMAMap *mapped = iova_tree_find(as->iova_tree, &target); if (event->type == IOMMU_NOTIFIER_UNMAP && !info->notify_unmap) { trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h index b66cf93c4b..8249edd764 100644 --- a/include/qemu/iova-tree.h +++ b/include/qemu/iova-tree.h @@ -59,7 +59,7 @@ IOVATree *iova_tree_new(void); * * Return: 0 if succeeded, or <0 if error. */ -int iova_tree_insert(IOVATree *tree, DMAMap *map); +int iova_tree_insert(IOVATree *tree, const DMAMap *map); /** * iova_tree_remove: @@ -74,7 +74,7 @@ int iova_tree_insert(IOVATree *tree, DMAMap *map); * * Return: 0 if succeeded, or <0 if error. */ -int iova_tree_remove(IOVATree *tree, DMAMap *map); +int iova_tree_remove(IOVATree *tree, const DMAMap *map); /** * iova_tree_find: @@ -92,7 +92,7 @@ int iova_tree_remove(IOVATree *tree, DMAMap *map); * user is responsible to make sure the pointer is valid (say, no * concurrent deletion in progress). */ -DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map); +const DMAMap *iova_tree_find(const IOVATree *tree, const DMAMap *map); /** * iova_tree_find_address: @@ -105,7 +105,7 @@ DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map); * * Return: same as iova_tree_find(). */ -DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova); +const DMAMap *iova_tree_find_address(const IOVATree *tree, hwaddr iova); /** * iova_tree_foreach: diff --git a/util/iova-tree.c b/util/iova-tree.c index 7990692cbd..23ea35b7a4 100644 --- a/util/iova-tree.c +++ b/util/iova-tree.c @@ -42,14 +42,14 @@ IOVATree *iova_tree_new(void) return iova_tree; } -DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map) +const DMAMap *iova_tree_find(const IOVATree *tree, const DMAMap *map) { return g_tree_lookup(tree->tree, map); } -DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova) +const DMAMap *iova_tree_find_address(const IOVATree *tree, hwaddr iova) { - DMAMap map = { .iova = iova, .size = 0 }; + const DMAMap map = { .iova = iova, .size = 0 }; return iova_tree_find(tree, &map); } @@ -60,7 +60,7 @@ static inline void iova_tree_insert_internal(GTree *gtree, DMAMap *range) g_tree_insert(gtree, range, range); } -int iova_tree_insert(IOVATree *tree, DMAMap *map) +int iova_tree_insert(IOVATree *tree, const DMAMap *map) { DMAMap *new; @@ -96,9 +96,9 @@ void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator) g_tree_foreach(tree->tree, iova_tree_traverse, iterator); } -int iova_tree_remove(IOVATree *tree, DMAMap *map) +int iova_tree_remove(IOVATree *tree, const DMAMap *map) { - DMAMap *overlap; + const DMAMap *overlap; while ((overlap = iova_tree_find(tree, map))) { g_tree_remove(tree->tree, overlap); From 0b09d44164b54505490f1771854d0b87441a3e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 12 Oct 2021 16:14:47 +0100 Subject: [PATCH 1086/1334] MAINTAINERS: update location of microvm docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: e8eee8d3d9 ("docs: Move microvm.rst into the system manual") Signed-off-by: Alex Bennée Message-Id: <20211012151447.4147923-1-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 310a9512ea..5455ff4179 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1609,7 +1609,7 @@ microvm M: Sergio Lopez M: Paolo Bonzini S: Maintained -F: docs/microvm.rst +F: docs/system/i386/microvm.rst F: hw/i386/microvm.c F: include/hw/i386/microvm.h F: pc-bios/bios-microvm.bin From f014c974595a1e3482c6afb57557a6e479b80328 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Oct 2021 14:48:10 +0200 Subject: [PATCH 1087/1334] target/i386: move linuxboot_dma_enabled to X86MachineState This removes a parameter from x86_load_linux, and will avoid code duplication between the linux and multiboot cases once multiboot starts to support DMA. Signed-off-by: Paolo Bonzini --- hw/i386/microvm.c | 5 ++++- hw/i386/pc.c | 5 ++--- hw/i386/pc_piix.c | 3 ++- hw/i386/pc_q35.c | 3 ++- hw/i386/x86.c | 5 +++-- include/hw/i386/pc.h | 3 --- include/hw/i386/x86.h | 5 +++-- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index f257ec5a0b..837bafb64a 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -331,7 +331,7 @@ static void microvm_memory_init(MicrovmMachineState *mms) rom_set_fw(fw_cfg); if (machine->kernel_filename != NULL) { - x86_load_linux(x86ms, fw_cfg, 0, true, true); + x86_load_linux(x86ms, fw_cfg, 0, true); } if (mms->option_roms) { @@ -667,6 +667,7 @@ static void microvm_machine_initfn(Object *obj) static void microvm_class_init(ObjectClass *oc, void *data) { + X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -697,6 +698,8 @@ static void microvm_class_init(ObjectClass *oc, void *data) hc->unplug_request = microvm_device_unplug_request_cb; hc->unplug = microvm_device_unplug_cb; + x86mc->fwcfg_dma_enabled = true; + object_class_property_add(oc, MICROVM_MACHINE_PIC, "OnOffAuto", microvm_machine_get_pic, microvm_machine_set_pic, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 86223acfd3..d203db7845 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -775,7 +775,7 @@ void xen_load_linux(PCMachineState *pcms) rom_set_fw(fw_cfg); x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size, - pcmc->pvh_enabled, pcmc->linuxboot_dma_enabled); + pcmc->pvh_enabled); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot.bin") || !strcmp(option_rom[i].name, "linuxboot_dma.bin") || @@ -927,7 +927,7 @@ void pc_memory_init(PCMachineState *pcms, if (linux_boot) { x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size, - pcmc->pvh_enabled, pcmc->linuxboot_dma_enabled); + pcmc->pvh_enabled); } for (i = 0; i < nb_option_roms; i++) { @@ -1664,7 +1664,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported * to be used at the moment, 32K should be enough for a while. */ pcmc->acpi_data_size = 0x20000 + 0x8000; - pcmc->linuxboot_dma_enabled = true; pcmc->pvh_enabled = true; pcmc->kvmclock_create_always = true; assert(!mc->get_hotplug_handler); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 6ad0d763c5..223dd3e05d 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -620,11 +620,12 @@ DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL, static void pc_i440fx_2_6_machine_options(MachineClass *m) { + X86MachineClass *x86mc = X86_MACHINE_CLASS(m); PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_7_machine_options(m); pcmc->legacy_cpu_hotplug = true; - pcmc->linuxboot_dma_enabled = false; + x86mc->fwcfg_dma_enabled = false; compat_props_add(m->compat_props, hw_compat_2_6, hw_compat_2_6_len); compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index fcc6e4eb2b..797e09500b 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -573,11 +573,12 @@ DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, static void pc_q35_2_6_machine_options(MachineClass *m) { + X86MachineClass *x86mc = X86_MACHINE_CLASS(m); PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_7_machine_options(m); pcmc->legacy_cpu_hotplug = true; - pcmc->linuxboot_dma_enabled = false; + x86mc->fwcfg_dma_enabled = false; compat_props_add(m->compat_props, hw_compat_2_6, hw_compat_2_6_len); compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 76de7e2265..a34498fe16 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -764,9 +764,9 @@ static bool load_elfboot(const char *kernel_filename, void x86_load_linux(X86MachineState *x86ms, FWCfgState *fw_cfg, int acpi_data_size, - bool pvh_enabled, - bool linuxboot_dma_enabled) + bool pvh_enabled) { + bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled; uint16_t protocol; int setup_size, kernel_size, cmdline_size; int dtb_size, setup_data_offset; @@ -1332,6 +1332,7 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; x86mc->compat_apic_id_mode = false; x86mc->save_tsc_khz = true; + x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; object_class_property_add(oc, X86_MACHINE_SMM, "OnOffAuto", diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 11426e26dc..9162aded21 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -116,9 +116,6 @@ struct PCMachineClass { /* generate legacy CPU hotplug AML */ bool legacy_cpu_hotplug; - /* use DMA capable linuxboot option rom */ - bool linuxboot_dma_enabled; - /* use PVH to load kernels that support this feature */ bool pvh_enabled; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 23267a3674..bb1cfb8896 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -38,6 +38,8 @@ struct X86MachineClass { bool save_tsc_khz; /* Enables contiguous-apic-ID mode */ bool compat_apic_id_mode; + /* use DMA capable linuxboot option rom */ + bool fwcfg_dma_enabled; }; struct X86MachineState { @@ -120,8 +122,7 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware, void x86_load_linux(X86MachineState *x86ms, FWCfgState *fw_cfg, int acpi_data_size, - bool pvh_enabled, - bool linuxboot_dma_enabled); + bool pvh_enabled); bool x86_machine_is_smm_enabled(const X86MachineState *x86ms); bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms); From 48972f8cad24eb4462c97ea68003e2dd35be0444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20H=C3=A4hnel?= Date: Wed, 20 Oct 2021 14:55:04 +0200 Subject: [PATCH 1088/1334] optionrom: add a DMA-enabled multiboot ROM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new option rom for the multiboot loader, using DMA transfers to copy data instead of "rep insb". This significantly lowers QEMU's startup latency by a factor of about 40, for example, going from 30sec to 0.8sec when loading modules of 120MB in size. Signed-off-by: Marcus Hähnel Signed-off-by: Adam Lackorzynski [Modified to keep the non-DMA code depending on #ifdef USE_FW_CFG_DMA; do not write below stack. - Paolo] Signed-off-by: Paolo Bonzini --- pc-bios/meson.build | 1 + pc-bios/multiboot_dma.bin | Bin 0 -> 1024 bytes pc-bios/optionrom/Makefile | 4 +- pc-bios/optionrom/multiboot.S | 4 +- pc-bios/optionrom/multiboot_dma.S | 2 + pc-bios/optionrom/optionrom.h | 66 ++++++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 pc-bios/multiboot_dma.bin create mode 100644 pc-bios/optionrom/multiboot_dma.S diff --git a/pc-bios/meson.build b/pc-bios/meson.build index a44c9bc127..b40ff3f2bd 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -63,6 +63,7 @@ blobs = files( 'petalogix-s3adsp1800.dtb', 'petalogix-ml605.dtb', 'multiboot.bin', + 'multiboot_dma.bin', 'linuxboot.bin', 'linuxboot_dma.bin', 'kvmvapic.bin', diff --git a/pc-bios/multiboot_dma.bin b/pc-bios/multiboot_dma.bin new file mode 100644 index 0000000000000000000000000000000000000000..c0e2c3102a3358207c61d3ae113524fb6007abc3 GIT binary patch literal 1024 zcmd^-v1=1i9LIlmUTlcNU1}{N5`u&{DAHSmq6iKdix;GHRxaNnR0AR{3q;@~8~^LMo;4s!p158nI!-tWur_ul*P<{!&% z=%3>xm5f`4Bq!!)`Rkwf;(oFd*qcAb=mhXX1)X>Bw-tylkUpR_ETXkH1AnO4L$nJ| zWJui_M1kmKmTID)P`mI^=HM`~{VB1t1l_Ye=Lor4X5{8Gd)zzw_o4<6zLQJ!drMolrKNZG2Z|kTOyaJIhYFc^ zFhhuQ9`r5fQLCrm#T4^_QylQ>8kgs;ZX9bIEiXb`8AIxu;?h~d%9izBkKht%WM1G* z^E{W9(OT9dt5in2qF}|dPH>dL>{=sV#-U1yFoWnOtw14D$&k*SUtsT%wS`ki@#&rUrnmtuDv`PtJm(Kg?H$Pdc0CL@YCy4R Date: Wed, 20 Oct 2021 15:59:44 +0200 Subject: [PATCH 1089/1334] target/i386: use DMA-enabled multiboot ROM for new-enough QEMU machine types As long as fw_cfg supports DMA, the new ROM can be used also on older machine types because it has the same size as the existing one. Signed-off-by: Paolo Bonzini --- hw/i386/multiboot.c | 10 ++++++++-- hw/i386/multiboot.h | 4 +++- hw/i386/pc.c | 3 ++- hw/i386/x86.c | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 9e7d69d470..0a10089f14 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -143,7 +143,8 @@ static void mb_add_mod(MultibootState *s, s->mb_mods_count++; } -int load_multiboot(FWCfgState *fw_cfg, +int load_multiboot(X86MachineState *x86ms, + FWCfgState *fw_cfg, FILE *f, const char *kernel_filename, const char *initrd_filename, @@ -151,6 +152,7 @@ int load_multiboot(FWCfgState *fw_cfg, int kernel_file_size, uint8_t *header) { + bool multiboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled; int i, is_multiboot = 0; uint32_t flags = 0; uint32_t mh_entry_addr; @@ -401,7 +403,11 @@ int load_multiboot(FWCfgState *fw_cfg, fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, mb_bootinfo_data, sizeof(bootinfo)); - option_rom[nb_option_roms].name = "multiboot.bin"; + if (multiboot_dma_enabled) { + option_rom[nb_option_roms].name = "multiboot_dma.bin"; + } else { + option_rom[nb_option_roms].name = "multiboot.bin"; + } option_rom[nb_option_roms].bootindex = 0; nb_option_roms++; diff --git a/hw/i386/multiboot.h b/hw/i386/multiboot.h index 60de309cd1..2b9182a8ea 100644 --- a/hw/i386/multiboot.h +++ b/hw/i386/multiboot.h @@ -2,8 +2,10 @@ #define QEMU_MULTIBOOT_H #include "hw/nvram/fw_cfg.h" +#include "hw/i386/x86.h" -int load_multiboot(FWCfgState *fw_cfg, +int load_multiboot(X86MachineState *x86ms, + FWCfgState *fw_cfg, FILE *f, const char *kernel_filename, const char *initrd_filename, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d203db7845..fcfb7f7139 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -780,7 +780,8 @@ void xen_load_linux(PCMachineState *pcms) assert(!strcmp(option_rom[i].name, "linuxboot.bin") || !strcmp(option_rom[i].name, "linuxboot_dma.bin") || !strcmp(option_rom[i].name, "pvh.bin") || - !strcmp(option_rom[i].name, "multiboot.bin")); + !strcmp(option_rom[i].name, "multiboot.bin") || + !strcmp(option_rom[i].name, "multiboot_dma.bin")); rom_add_option(option_rom[i].name, option_rom[i].bootindex); } x86ms->fw_cfg = fw_cfg; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index a34498fe16..b84840a1bb 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -814,7 +814,7 @@ void x86_load_linux(X86MachineState *x86ms, * PVH), so we try multiboot first since we check the multiboot magic * header before to load it. */ - if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename, + if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename, kernel_cmdline, kernel_size, header)) { return; } From 605a927198c439fcfecde94b48156575f83ed644 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Oct 2021 16:32:50 +0200 Subject: [PATCH 1090/1334] configure: remove useless NPTL probe Using a linuxthreads system with a recent QEMU will have bigger problems than just not having NPTL. Remove the unnecessary check. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- configure | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/configure b/configure index 03162008aa..b5c36347eb 100755 --- a/configure +++ b/configure @@ -1994,25 +1994,6 @@ if ! has "$pkg_config_exe"; then error_exit "pkg-config binary '$pkg_config_exe' not found" fi -########################################## -# NPTL probe - -if test "$linux_user" = "yes"; then - cat > $TMPC < -#include -int main(void) { -#if !defined(CLONE_SETTLS) || !defined(FUTEX_WAIT) -#error bork -#endif - return 0; -} -EOF - if ! compile_object ; then - feature_not_found "nptl" "Install glibc and linux kernel headers." - fi -fi - ########################################## # xen probe From ff66ee53690abef92c1ea5c22b79e857cfa2485a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Oct 2021 16:29:47 +0200 Subject: [PATCH 1091/1334] configure: do not duplicate CPU_CFLAGS into QEMU_LDFLAGS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 4dba2789084 ("configure, meson: move CPU_CFLAGS out of QEMU_CFLAGS"), CPU_CFLAGS is included in the link commands both during configure and (via config-meson.cross) during meson. It need not be added separately to QEMU_LDFLAGS. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- configure | 53 ++++++++++++++--------------------------------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/configure b/configure index b5c36347eb..c0018a304f 100755 --- a/configure +++ b/configure @@ -1263,45 +1263,20 @@ firmwarepath="${firmwarepath:-$datadir/qemu-firmware}" localedir="${localedir:-$datadir/locale}" case "$cpu" in - ppc) - CPU_CFLAGS="-m32" - QEMU_LDFLAGS="-m32 $QEMU_LDFLAGS" - ;; - ppc64) - CPU_CFLAGS="-m64" - QEMU_LDFLAGS="-m64 $QEMU_LDFLAGS" - ;; - sparc) - CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc" - QEMU_LDFLAGS="-m32 -mv8plus $QEMU_LDFLAGS" - ;; - sparc64) - CPU_CFLAGS="-m64 -mcpu=ultrasparc" - QEMU_LDFLAGS="-m64 $QEMU_LDFLAGS" - ;; - s390) - CPU_CFLAGS="-m31" - QEMU_LDFLAGS="-m31 $QEMU_LDFLAGS" - ;; - s390x) - CPU_CFLAGS="-m64" - QEMU_LDFLAGS="-m64 $QEMU_LDFLAGS" - ;; - i386) - CPU_CFLAGS="-m32" - QEMU_LDFLAGS="-m32 $QEMU_LDFLAGS" - ;; - x86_64) - # ??? Only extremely old AMD cpus do not have cmpxchg16b. - # If we truly care, we should simply detect this case at - # runtime and generate the fallback to serial emulation. - CPU_CFLAGS="-m64 -mcx16" - QEMU_LDFLAGS="-m64 $QEMU_LDFLAGS" - ;; - x32) - CPU_CFLAGS="-mx32" - QEMU_LDFLAGS="-mx32 $QEMU_LDFLAGS" - ;; + ppc) CPU_CFLAGS="-m32" ;; + ppc64) CPU_CFLAGS="-m64" ;; + sparc) CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc" ;; + sparc64) CPU_CFLAGS="-m64 -mcpu=ultrasparc" ;; + s390) CPU_CFLAGS="-m31" ;; + s390x) CPU_CFLAGS="-m64" ;; + i386) CPU_CFLAGS="-m32" ;; + x32) CPU_CFLAGS="-mx32" ;; + + # ??? Only extremely old AMD cpus do not have cmpxchg16b. + # If we truly care, we should simply detect this case at + # runtime and generate the fallback to serial emulation. + x86_64) CPU_CFLAGS="-m64 -mcx16" ;; + # No special flags required for other host CPUs esac From a443c3e225ea78f316f01a12a2fb1d55671e8256 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 25 Oct 2021 15:21:47 +0200 Subject: [PATCH 1092/1334] hvf: Avoid mapping regions < PAGE_SIZE as ram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HVF has generic memory listener code that adds all RAM regions as HVF RAM regions. However, HVF can only handle page aligned, page granule regions. So let's ignore regions that are not page aligned and sized. They will be trapped as MMIO instead. Signed-off-by: Alexander Graf Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211025132147.28308-1-agraf@csgraf.de> Signed-off-by: Paolo Bonzini --- accel/hvf/hvf-accel-ops.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 2b2c411076..54457c76c2 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -122,6 +122,7 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) MemoryRegion *area = section->mr; bool writeable = !area->readonly && !area->rom_device; hv_memory_flags_t flags; + uint64_t page_size = qemu_real_host_page_size; if (!memory_region_is_ram(area)) { if (writeable) { @@ -135,6 +136,12 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) } } + if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) || + !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) { + /* Not page aligned, so we can not map as RAM */ + add = false; + } + mem = hvf_find_overlap_slot( section->offset_within_address_space, int128_get64(section->size)); From 739b38630c45585cd9d372d44537f69c0b2b4346 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Mon, 25 Oct 2021 11:47:38 +0100 Subject: [PATCH 1093/1334] hw/i386: Rename default_bus_bypass_iommu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit d8fb7d0969d5 ("vl: switch -M parsing to keyval"), machine parameter definitions cannot use underscores, because keyval_dashify() transforms them to dashes and the parser doesn't find the parameter. This affects option default_bus_bypass_iommu which was introduced in the same release: $ qemu-system-x86_64 -M q35,default_bus_bypass_iommu=on qemu-system-x86_64: Property 'pc-q35-6.1-machine.default-bus-bypass-iommu' not found Rename the parameter to "default-bus-bypass-iommu". Passing "default_bus_bypass_iommu" is still valid since the underscore are transformed automatically. Fixes: c9e96b04fc19 ("hw/i386: Add a default_bus_bypass_iommu pc machine option") Reviewed-by: Eric Auger Reviewed-by: Philippe Mathieu-Daudé Tested-by: Eric Auger Signed-off-by: Jean-Philippe Brucker Message-Id: <20211025104737.1560274-1-jean-philippe@linaro.org> Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index fcfb7f7139..ac5af274cd 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1718,7 +1718,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, "hpet", pc_machine_get_hpet, pc_machine_set_hpet); - object_class_property_add_bool(oc, "default_bus_bypass_iommu", + object_class_property_add_bool(oc, "default-bus-bypass-iommu", pc_machine_get_default_bus_bypass_iommu, pc_machine_set_default_bus_bypass_iommu); From b10cb627526a19df97b56ac42381ad1104297d16 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 27 Oct 2021 14:34:53 +0200 Subject: [PATCH 1094/1334] watchdog: add information from -watchdog help to -device help List all watchdog devices in a separate category, and populate their descriptions. Signed-off-by: Paolo Bonzini --- hw/watchdog/sbsa_gwdt.c | 3 ++- hw/watchdog/wdt_aspeed.c | 3 ++- hw/watchdog/wdt_diag288.c | 3 ++- hw/watchdog/wdt_i6300esb.c | 3 ++- hw/watchdog/wdt_ib700.c | 3 ++- hw/watchdog/wdt_imx2.c | 4 ++-- include/hw/qdev-core.h | 1 + softmmu/qdev-monitor.c | 1 + 8 files changed, 14 insertions(+), 7 deletions(-) diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index d0998f8489..e49cacd0e2 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -273,8 +273,9 @@ static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data) dc->realize = wdt_sbsa_gwdt_realize; dc->reset = wdt_sbsa_gwdt_reset; dc->hotpluggable = false; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); + set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->vmsd = &vmstate_sbsa_gwdt; + dc->desc = "SBSA-compliant generic watchdog device"; } static const TypeInfo wdt_sbsa_gwdt_info = { diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 146ffcd713..6aa6f90b66 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -293,9 +293,10 @@ static void aspeed_wdt_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED Watchdog Controller"; dc->realize = aspeed_wdt_realize; dc->reset = aspeed_wdt_reset; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); + set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->vmsd = &vmstate_aspeed_wdt; device_class_set_props(dc, aspeed_wdt_properties); + dc->desc = "Aspeed watchdog device"; } static const TypeInfo aspeed_wdt_info = { diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c index e135a4de8b..9e8882a11c 100644 --- a/hw/watchdog/wdt_diag288.c +++ b/hw/watchdog/wdt_diag288.c @@ -122,9 +122,10 @@ static void wdt_diag288_class_init(ObjectClass *klass, void *data) dc->unrealize = wdt_diag288_unrealize; dc->reset = wdt_diag288_reset; dc->hotpluggable = false; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); + set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->vmsd = &vmstate_diag288; diag288->handle_timer = wdt_diag288_handle_timer; + dc->desc = "diag288 device for s390x platform"; } static const TypeInfo wdt_diag288_info = { diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index 4c52e3bb9e..f99a1c9d29 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -476,7 +476,8 @@ static void i6300esb_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SYSTEM_OTHER; dc->reset = i6300esb_reset; dc->vmsd = &vmstate_i6300esb; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); + set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); + dc->desc = "Intel 6300ESB"; } static const TypeInfo i6300esb_info = { diff --git a/hw/watchdog/wdt_ib700.c b/hw/watchdog/wdt_ib700.c index 177aaa503f..91d1bdc0da 100644 --- a/hw/watchdog/wdt_ib700.c +++ b/hw/watchdog/wdt_ib700.c @@ -140,7 +140,8 @@ static void wdt_ib700_class_init(ObjectClass *klass, void *data) dc->realize = wdt_ib700_realize; dc->reset = wdt_ib700_reset; dc->vmsd = &vmstate_ib700; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); + set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); + dc->desc = "iBASE 700"; } static const TypeInfo wdt_ib700_info = { diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index a5fb76308f..c3128370b5 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -280,8 +280,8 @@ static void imx2_wdt_class_init(ObjectClass *klass, void *data) dc->realize = imx2_wdt_realize; dc->reset = imx2_wdt_reset; dc->vmsd = &vmstate_imx2_wdt; - dc->desc = "i.MX watchdog timer"; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->desc = "i.MX2 watchdog timer"; + set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); } static const TypeInfo imx2_wdt_info = { diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 1bad07002d..72622bd337 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -26,6 +26,7 @@ typedef enum DeviceCategory { DEVICE_CATEGORY_SOUND, DEVICE_CATEGORY_MISC, DEVICE_CATEGORY_CPU, + DEVICE_CATEGORY_WATCHDOG, DEVICE_CATEGORY_MAX } DeviceCategory; diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index e49d9773d2..f8b3a4cd82 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -165,6 +165,7 @@ static void qdev_print_devinfos(bool show_no_user) [DEVICE_CATEGORY_SOUND] = "Sound", [DEVICE_CATEGORY_MISC] = "Misc", [DEVICE_CATEGORY_CPU] = "CPU", + [DEVICE_CATEGORY_WATCHDOG]= "Watchdog", [DEVICE_CATEGORY_MAX] = "Uncategorized", }; GSList *list, *elt; From d12b64eaebd9f0df03c70422336c669a44ed2937 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 27 Oct 2021 14:48:03 +0200 Subject: [PATCH 1095/1334] vl: deprecate -watchdog -watchdog is the same as -device except that it is case insensitive (and it allows only watchdog devices of course). Now that "-device help" can list as such the available watchdog devices, we can deprecate it. Note that even though -watchdog tries to be case insensitive, it fails at that: "-watchdog i6300xyz" fails with "Unknown -watchdog device", but "-watchdog i6300ESB" also fails (when the generated -device option is processed) with an error "'i6300ESB' is not a valid device model name". For this reason, the documentation update does not mention the case insensitivity of -watchdog. Signed-off-by: Paolo Bonzini --- docs/about/deprecated.rst | 5 +++++ softmmu/vl.c | 1 + 2 files changed, 6 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index be19317470..6155b32ee6 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -160,6 +160,11 @@ Use ``-display sdl`` instead. Use ``-display curses`` instead. +``-watchdog`` (since 6.2) +''''''''''''''''''''''''' + +Use ``-device`` instead. + ``-smp`` ("parameter=0" SMP configurations) (since 6.2) ''''''''''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/softmmu/vl.c b/softmmu/vl.c index af0c4cbd99..570120f5c4 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -3256,6 +3256,7 @@ void qemu_init(int argc, char **argv, char **envp) error_report("only one watchdog option may be given"); exit(1); } + warn_report("-watchdog is deprecated; use -device instead."); watchdog = optarg; break; case QEMU_OPTION_action: From 22afb46e7c6ed61bd41c199072cb4769d6ab14b2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 27 Oct 2021 15:03:04 +0200 Subject: [PATCH 1096/1334] watchdog: remove select_watchdog_action Instead of invoking select_watchdog_action from both HMP and command line, go directly from HMP to QMP and use QemuOpts as the intermediary for the command line. This makes -watchdog-action explicitly a shortcut for "-action watchdog", so that "-watchdog-action" and "-action watchdog" override each other based on the position on the command line; previously, "-action watchdog" always won. Signed-off-by: Paolo Bonzini --- hw/watchdog/watchdog.c | 14 -------------- include/sysemu/watchdog.h | 1 - monitor/misc.c | 15 ++++++++++++--- softmmu/vl.c | 10 +++++----- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index 0e98ffb73f..1437e6c5b6 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -76,20 +76,6 @@ int select_watchdog(const char *p) return 1; } -int select_watchdog_action(const char *p) -{ - int action; - char *qapi_value; - - qapi_value = g_ascii_strdown(p, -1); - action = qapi_enum_parse(&WatchdogAction_lookup, qapi_value, -1, NULL); - g_free(qapi_value); - if (action < 0) - return -1; - qmp_watchdog_set_action(action, &error_abort); - return 0; -} - WatchdogAction get_watchdog_action(void) { return watchdog_action; diff --git a/include/sysemu/watchdog.h b/include/sysemu/watchdog.h index a08d16380d..d2d4901dbb 100644 --- a/include/sysemu/watchdog.h +++ b/include/sysemu/watchdog.h @@ -37,7 +37,6 @@ typedef struct WatchdogTimerModel WatchdogTimerModel; /* in hw/watchdog.c */ int select_watchdog(const char *p); -int select_watchdog_action(const char *action); WatchdogAction get_watchdog_action(void); void watchdog_add_model(WatchdogTimerModel *model); void watchdog_perform_action(void); diff --git a/monitor/misc.c b/monitor/misc.c index c2d227a07c..1759d1e7f1 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -70,6 +70,7 @@ #include "qapi/qapi-commands-migration.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-qom.h" +#include "qapi/qapi-commands-run-state.h" #include "qapi/qapi-commands-trace.h" #include "qapi/qapi-init-commands.h" #include "qapi/error.h" @@ -471,10 +472,18 @@ static void hmp_gdbserver(Monitor *mon, const QDict *qdict) static void hmp_watchdog_action(Monitor *mon, const QDict *qdict) { - const char *action = qdict_get_str(qdict, "action"); - if (select_watchdog_action(action) == -1) { - monitor_printf(mon, "Unknown watchdog action '%s'\n", action); + Error *err = NULL; + WatchdogAction action; + char *qapi_value; + + qapi_value = g_ascii_strdown(qdict_get_str(qdict, "action"), -1); + action = qapi_enum_parse(&WatchdogAction_lookup, qapi_value, -1, &err); + g_free(qapi_value); + if (err) { + hmp_handle_error(mon, err); + return; } + qmp_watchdog_set_action(action, &error_abort); } static void monitor_printc(Monitor *mon, int c) diff --git a/softmmu/vl.c b/softmmu/vl.c index 570120f5c4..1159a64bce 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -3265,12 +3265,12 @@ void qemu_init(int argc, char **argv, char **envp) exit(1); } break; - case QEMU_OPTION_watchdog_action: - if (select_watchdog_action(optarg) == -1) { - error_report("unknown -watchdog-action parameter"); - exit(1); - } + case QEMU_OPTION_watchdog_action: { + QemuOpts *opts; + opts = qemu_opts_create(qemu_find_opts("action"), NULL, 0, &error_abort); + qemu_opt_set(opts, "watchdog", optarg, &error_abort); break; + } case QEMU_OPTION_parallel: add_device_config(DEV_PARALLEL, optarg); default_parallel = 0; From 6aedeb650e6978ee8165f05b79fbfab6ed486eff Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Tue, 26 Oct 2021 12:54:05 +0300 Subject: [PATCH 1097/1334] hw/i386: fix vmmouse registration According to the logic of vmmouse_update_handler function, vmmouse should be registered as an event handler when it's status is zero. vmmouse_read_id resets the status but does not register the handler. This patch adds vmmouse registration and activation when status is reset. Signed-off-by: Pavel Dovgalyuk Message-Id: <163524204515.1914131.16465061981774791228.stgit@pasha-ThinkPad-X280> Signed-off-by: Paolo Bonzini --- hw/i386/vmmouse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index df4798f502..3d66368286 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -158,6 +158,7 @@ static void vmmouse_read_id(VMMouseState *s) s->queue[s->nb_queue++] = VMMOUSE_VERSION; s->status = 0; + vmmouse_update_handler(s, s->absolute); } static void vmmouse_request_relative(VMMouseState *s) From cabf9862e42ffeab9de9b1bfa12cddaf125c53e8 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 1 Nov 2021 15:23:00 +0200 Subject: [PATCH 1098/1334] KVM: SVM: add migration support for nested TSC scaling Signed-off-by: Maxim Levitsky Message-Id: <20211101132300.192584-4-mlevitsk@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 5 +++++ target/i386/cpu.h | 4 ++++ target/i386/kvm/kvm.c | 15 +++++++++++++++ target/i386/machine.c | 22 ++++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 598d451dcf..53a23ca006 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5928,6 +5928,11 @@ static void x86_cpu_reset(DeviceState *dev) } x86_cpu_set_sgxlepubkeyhash(env); + + if (env->features[FEAT_SVM] & CPUID_SVM_TSCSCALE) { + env->amd_tsc_scale_msr = MSR_AMD64_TSC_RATIO_DEFAULT; + } + #endif } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 3edaad7688..04f2b790c9 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -499,6 +499,9 @@ typedef enum X86Seg { #define MSR_GSBASE 0xc0000101 #define MSR_KERNELGSBASE 0xc0000102 #define MSR_TSC_AUX 0xc0000103 +#define MSR_AMD64_TSC_RATIO 0xc0000104 + +#define MSR_AMD64_TSC_RATIO_DEFAULT 0x100000000ULL #define MSR_VM_HSAVE_PA 0xc0010117 @@ -1536,6 +1539,7 @@ typedef struct CPUX86State { uint32_t tsx_ctrl; uint64_t spec_ctrl; + uint64_t amd_tsc_scale_msr; uint64_t virt_ssbd; /* End of state preserved by INIT (dummy marker). */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 0eb7a0340c..5a698bde19 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -105,6 +105,7 @@ static bool has_msr_hv_reenlightenment; static bool has_msr_xss; static bool has_msr_umwait; static bool has_msr_spec_ctrl; +static bool has_tsc_scale_msr; static bool has_msr_tsx_ctrl; static bool has_msr_virt_ssbd; static bool has_msr_smi_count; @@ -2216,6 +2217,9 @@ static int kvm_get_supported_msrs(KVMState *s) case MSR_IA32_SPEC_CTRL: has_msr_spec_ctrl = true; break; + case MSR_AMD64_TSC_RATIO: + has_tsc_scale_msr = true; + break; case MSR_IA32_TSX_CTRL: has_msr_tsx_ctrl = true; break; @@ -2972,6 +2976,10 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_spec_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, env->spec_ctrl); } + if (has_tsc_scale_msr) { + kvm_msr_entry_add(cpu, MSR_AMD64_TSC_RATIO, env->amd_tsc_scale_msr); + } + if (has_msr_tsx_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_TSX_CTRL, env->tsx_ctrl); } @@ -3377,6 +3385,10 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_spec_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, 0); } + if (has_tsc_scale_msr) { + kvm_msr_entry_add(cpu, MSR_AMD64_TSC_RATIO, 0); + } + if (has_msr_tsx_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_TSX_CTRL, 0); } @@ -3788,6 +3800,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_SPEC_CTRL: env->spec_ctrl = msrs[i].data; break; + case MSR_AMD64_TSC_RATIO: + env->amd_tsc_scale_msr = msrs[i].data; + break; case MSR_IA32_TSX_CTRL: env->tsx_ctrl = msrs[i].data; break; diff --git a/target/i386/machine.c b/target/i386/machine.c index 4367931623..83c2b91529 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1280,6 +1280,27 @@ static const VMStateDescription vmstate_spec_ctrl = { } }; + +static bool amd_tsc_scale_msr_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return (env->features[FEAT_SVM] & CPUID_SVM_TSCSCALE); +} + +static const VMStateDescription amd_tsc_scale_msr_ctrl = { + .name = "cpu/amd_tsc_scale_msr", + .version_id = 1, + .minimum_version_id = 1, + .needed = amd_tsc_scale_msr_needed, + .fields = (VMStateField[]){ + VMSTATE_UINT64(env.amd_tsc_scale_msr, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + + static bool intel_pt_enable_needed(void *opaque) { X86CPU *cpu = opaque; @@ -1558,6 +1579,7 @@ const VMStateDescription vmstate_x86_cpu = { &vmstate_pkru, &vmstate_pkrs, &vmstate_spec_ctrl, + &amd_tsc_scale_msr_ctrl, &vmstate_mcg_ext_ctl, &vmstate_msr_intel_pt, &vmstate_msr_virt_ssbd, From de7e2cb15586a61d26fc3983ba2cbcbc5c234e15 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Mon, 1 Nov 2021 18:35:15 +0000 Subject: [PATCH 1099/1334] esp: ensure in-flight SCSI requests are always cancelled There is currently a check in esp_select() to cancel any in-flight SCSI requests to ensure that issuing multiple select commands without continuing through the rest of the ESP state machine ignores all but the last SCSI request. This is also enforced through the addition of assert()s in esp_transfer_data() and scsi_read_data(). The get_cmd() function does not call esp_select() when TC == 0 which means it is possible for a fuzzer to trigger these assert()s by sending a select command when TC == 0 immediately after a valid SCSI CDB has been submitted. Since esp_select() is only called from get_cmd(), hoist the check to cancel in-flight SCSI requests from esp_select() into get_cmd() to ensure it is always called when executing a select command to initiate a new SCSI request. Signed-off-by: Mark Cave-Ayland Closes: https://gitlab.com/qemu-project/qemu/-/issues/662 Closes: https://gitlab.com/qemu-project/qemu/-/issues/663 Message-Id: <20211101183516.8455-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 8454ed1773..84f935b549 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -204,11 +204,6 @@ static int esp_select(ESPState *s) s->ti_size = 0; fifo8_reset(&s->fifo); - if (s->current_req) { - /* Started a new command before the old one finished. Cancel it. */ - scsi_req_cancel(s->current_req); - } - s->current_dev = scsi_device_find(&s->bus, 0, target, 0); if (!s->current_dev) { /* No such drive */ @@ -235,6 +230,11 @@ static uint32_t get_cmd(ESPState *s, uint32_t maxlen) uint32_t dmalen, n; int target; + if (s->current_req) { + /* Started a new command before the old one finished. Cancel it. */ + scsi_req_cancel(s->current_req); + } + target = s->wregs[ESP_WBUSID] & BUSID_DID; if (s->dma) { dmalen = MIN(esp_get_tc(s), maxlen); From fccec5ce171b66850b5882488a64d6ffd1131d2e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Mon, 1 Nov 2021 18:35:16 +0000 Subject: [PATCH 1100/1334] qtest/am53c974-test: add test for cancelling in-flight requests Based upon the qtest reproducer posted to Gitlab issue #663 at https://gitlab.com/qemu-project/qemu/-/issues/663. Signed-off-by: Mark Cave-Ayland Message-Id: <20211101183516.8455-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- tests/qtest/am53c974-test.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/qtest/am53c974-test.c b/tests/qtest/am53c974-test.c index d996866cd4..9b1e4211bd 100644 --- a/tests/qtest/am53c974-test.c +++ b/tests/qtest/am53c974-test.c @@ -189,6 +189,40 @@ static void test_cancelled_request_ok(void) qtest_quit(s); } +static void test_inflight_cancel_ok(void) +{ + QTestState *s = qtest_init( + "-device am53c974,id=scsi " + "-device scsi-hd,drive=disk0 -drive " + "id=disk0,if=none,file=null-co://,format=raw -nodefaults"); + qtest_outl(s, 0xcf8, 0x80001000); + qtest_inw(s, 0xcfc); + qtest_outl(s, 0xcf8, 0x80001010); + qtest_outl(s, 0xcfc, 0xffffffff); + qtest_outl(s, 0xcf8, 0x80001010); + qtest_inl(s, 0xcfc); + qtest_outl(s, 0xcf8, 0x80001010); + qtest_outl(s, 0xcfc, 0xc001); + qtest_outl(s, 0xcf8, 0x80001004); + qtest_inw(s, 0xcfc); + qtest_outl(s, 0xcf8, 0x80001004); + qtest_outw(s, 0xcfc, 0x7); + qtest_outl(s, 0xcf8, 0x80001004); + qtest_inw(s, 0xcfc); + qtest_inb(s, 0xc000); + qtest_outb(s, 0xc008, 0x8); + qtest_outw(s, 0xc00b, 0x4100); + qtest_outb(s, 0xc009, 0x0); + qtest_outb(s, 0xc009, 0x0); + qtest_outw(s, 0xc00b, 0xc212); + qtest_outl(s, 0xc042, 0x2c2c5a88); + qtest_outw(s, 0xc00b, 0xc212); + qtest_outw(s, 0xc00b, 0x415a); + qtest_outl(s, 0xc03f, 0x3060303); + qtest_outl(s, 0xc00b, 0x5afa9054); + qtest_quit(s); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -212,6 +246,8 @@ int main(int argc, char **argv) test_fifo_underflow_on_write_ok); qtest_add_func("am53c974/test_cancelled_request_ok", test_cancelled_request_ok); + qtest_add_func("am53c974/test_inflight_cancel_ok", + test_inflight_cancel_ok); } return g_test_run(); From 6638cae5f65c5f6fb312ef6f1635b29d2fd056aa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 27 Oct 2021 15:18:48 +0200 Subject: [PATCH 1101/1334] meson: bump submodule to 0.59.3 This gains some bugfixes, especially: - it fixes the introspection of array options. While technically we still support Meson 0.58.2, this issue only appears when adding a new option and not if the user is just building QEMU. In the relatively rare case of a contributor using --meson to point to a 0.58 version, review can catch spurious changes to scripts/meson-buildoptions.sh easily. - it fixes "meson test" when it is not the process group leader. Make is the process group leader when "make check" invokes "meson test", so this is a requirement for using it as a test harness. Tested-by: Thomas Huth Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 2 +- meson | 2 +- scripts/meson-buildoptions.py | 16 ---------------- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/configure b/configure index c0018a304f..73af9a7b30 100755 --- a/configure +++ b/configure @@ -1500,7 +1500,7 @@ python_version=$($python -c 'import sys; print("%d.%d.%d" % (sys.version_info[0] python="$python -B" if test -z "$meson"; then - if test "$explicit_python" = no && has meson && version_ge "$(meson --version)" 0.59.2; then + if test "$explicit_python" = no && has meson && version_ge "$(meson --version)" 0.59.3; then meson=meson elif test $git_submodules_action != 'ignore' ; then meson=git diff --git a/meson b/meson index b25d94e7c7..12f9f04ba0 160000 --- a/meson +++ b/meson @@ -1 +1 @@ -Subproject commit b25d94e7c77fda05a7fdfe8afe562cf9760d69da +Subproject commit 12f9f04ba0decfda425dbbf9a501084c153a2d18 diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py index 256523c09d..96969d89ee 100755 --- a/scripts/meson-buildoptions.py +++ b/scripts/meson-buildoptions.py @@ -150,23 +150,7 @@ def print_parse(options): print("}") -def fixup_options(options): - # Meson <= 0.60 does not include the choices in array options, fix that up - for opt in options: - if opt["name"] == "trace_backends": - opt["choices"] = [ - "dtrace", - "ftrace", - "log", - "nop", - "simple", - "syslog", - "ust", - ] - - options = load_options(json.load(sys.stdin)) -fixup_options(options) print("# This file is generated by meson-buildoptions.py, do not edit!") print_help(options) print_parse(options) From ab486f165b882aa5b02d0b48d6fa242a2d3c10da Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 2 Nov 2021 11:58:22 +0100 Subject: [PATCH 1102/1334] meson.build: Allow to disable OSS again If sys/soundcard.h is available, it is currently not possible to disable OSS with the --disable-oss or --without-default-features configure switches. Improve the check in meson.build to fix this. Fixes: 87430d5b13 ("configure, meson: move audio driver detection to Meson") Signed-off-by: Thomas Huth Message-Id: <20211102105822.773131-1-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 85f1e43dfe..0ab9c43bf2 100644 --- a/meson.build +++ b/meson.build @@ -911,7 +911,7 @@ if liblzfse.found() and not cc.links(''' endif oss = not_found -if not get_option('oss').auto() or have_system +if have_system and not get_option('oss').disabled() if not cc.has_header('sys/soundcard.h') # not found elif targetos == 'netbsd' From 78cb330e91a9cb996b7e8f61a184a433f680bc78 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 2 Nov 2021 12:23:45 +0100 Subject: [PATCH 1103/1334] meson: remove pointless warnings Meson tests sometimes warn if the required libraries and headers are present but a test program fails to link. In the case of DirectSound and OSS, however, there is no test program so there is no need to warn. Reported-by: Peter Maydell Signed-off-by: Paolo Bonzini --- meson.build | 4 ---- 1 file changed, 4 deletions(-) diff --git a/meson.build b/meson.build index 0ab9c43bf2..d09e314e04 100644 --- a/meson.build +++ b/meson.build @@ -924,8 +924,6 @@ if have_system and not get_option('oss').disabled() if not oss.found() if get_option('oss').enabled() error('OSS not found') - else - warning('OSS not found, disabling') endif endif endif @@ -938,8 +936,6 @@ if not get_option('dsound').auto() or (targetos == 'windows' and have_system) if not dsound.found() if get_option('dsound').enabled() error('DirectSound not found') - else - warning('DirectSound not found, disabling') endif endif endif From 6ed3e1482b4e373195bea36bbeba59d6fe76a999 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 2 Nov 2021 12:24:39 +0100 Subject: [PATCH 1104/1334] meson: remove unnecessary coreaudio test program AudioGetCurrentHostTime has been present forever, so the test is not enforcing a specific version of macOS. In fact the test was broken since it was not linking against the coreaudio dependency; just remove it. Fixes: 87430d5b13 ("configure, meson: move audio driver detection to Meson", 2021-10-14) Reported-by: Peter Maydell Signed-off-by: Paolo Bonzini --- meson.build | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/meson.build b/meson.build index d09e314e04..30b06b12b4 100644 --- a/meson.build +++ b/meson.build @@ -944,22 +944,6 @@ coreaudio = not_found if not get_option('coreaudio').auto() or (targetos == 'darwin' and have_system) coreaudio = dependency('appleframeworks', modules: 'CoreAudio', required: get_option('coreaudio')) - if coreaudio.found() and not cc.links(''' - #include - int main(void) - { - return (int)AudioGetCurrentHostTime(); - }''') - coreaudio = not_found - endif - - if not coreaudio.found() - if get_option('coreaudio').enabled() - error('CoreAudio not found') - else - warning('CoreAudio not found, disabling') - endif - endif endif opengl = not_found From eea9453a016b622f74898809f53e0ca85961cd80 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 28 Oct 2021 20:59:08 +0200 Subject: [PATCH 1105/1334] Move the l2tpv3 test from configure to meson.build And while we're at it, also provide a proper entry for this feature in meson_options.txt, so that people who don't need it have a knob to disable this feature. Signed-off-by: Thomas Huth Message-Id: <20211028185910.1729744-3-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 17 ----------------- meson.build | 8 ++++++++ meson_options.txt | 2 ++ net/meson.build | 4 +++- scripts/meson-buildoptions.sh | 3 +++ 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/configure b/configure index 73af9a7b30..2268e5d0ab 100755 --- a/configure +++ b/configure @@ -1896,20 +1896,6 @@ if test -z "$want_tools"; then fi fi -########################################## -# L2TPV3 probe - -cat > $TMPC < -#include -int main(void) { return sizeof(struct mmsghdr); } -EOF -if compile_prog "" "" ; then - l2tpv3=yes -else - l2tpv3=no -fi - ######################################### # vhost interdependencies and host support @@ -3514,9 +3500,6 @@ if test "$slirp_smbd" = "yes" ; then echo "CONFIG_SLIRP_SMBD=y" >> $config_host_mak echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak fi -if test "$l2tpv3" = "yes" ; then - echo "CONFIG_L2TPV3=y" >> $config_host_mak -fi if test "$gprof" = "yes" ; then echo "CONFIG_GPROF=y" >> $config_host_mak fi diff --git a/meson.build b/meson.build index 30b06b12b4..e330438270 100644 --- a/meson.build +++ b/meson.build @@ -1658,6 +1658,13 @@ config_host_data.set('HAVE_MLOCKALL', cc.links(gnu_source_prefix + ''' return mlockall(MCL_FUTURE); }''')) +have_l2tpv3 = false +if not get_option('l2tpv3').disabled() and have_system + have_l2tpv3 = (cc.has_header_symbol('sys/socket.h', 'struct mmsghdr') + and cc.has_header('linux/ip.h')) +endif +config_host_data.set('CONFIG_L2TPV3', have_l2tpv3) + have_netmap = false if not get_option('netmap').disabled() and have_system have_netmap = cc.compiles(''' @@ -3377,6 +3384,7 @@ summary_info += {'JACK support': jack} summary_info += {'brlapi support': brlapi} summary_info += {'vde support': vde} summary_info += {'netmap support': have_netmap} +summary_info += {'l2tpv3 support': have_l2tpv3} summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} diff --git a/meson_options.txt b/meson_options.txt index 4ab4570125..e740dce2a5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -141,6 +141,8 @@ option('u2f', type : 'feature', value : 'auto', description: 'U2F emulation support') option('usb_redir', type : 'feature', value : 'auto', description: 'libusbredir support') +option('l2tpv3', type : 'feature', value : 'auto', + description: 'l2tpv3 network backend support') option('netmap', type : 'feature', value : 'auto', description: 'netmap network backend support') option('vde', type : 'feature', value : 'auto', diff --git a/net/meson.build b/net/meson.build index 94383e7460..847bc2ac85 100644 --- a/net/meson.build +++ b/net/meson.build @@ -18,7 +18,9 @@ softmmu_ss.add(files( softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) -softmmu_ss.add(when: 'CONFIG_L2TPV3', if_true: files('l2tpv3.c')) +if have_l2tpv3 + softmmu_ss.add(files('l2tpv3.c')) +endif softmmu_ss.add(when: slirp, if_true: files('slirp.c')) softmmu_ss.add(when: vde, if_true: files('vde.c')) if have_netmap diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index c795a13020..55b8a78560 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -49,6 +49,7 @@ meson_options_help() { printf "%s\n" ' iconv Font glyph conversion support' printf "%s\n" ' jack JACK sound support' printf "%s\n" ' kvm KVM acceleration support' + printf "%s\n" ' l2tpv3 l2tpv3 network backend support' printf "%s\n" ' libdaxctl libdaxctl support' printf "%s\n" ' libiscsi libiscsi userspace initiator' printf "%s\n" ' libnfs libnfs block device driver' @@ -166,6 +167,8 @@ _meson_option_parse() { --disable-jack) printf "%s" -Djack=disabled ;; --enable-kvm) printf "%s" -Dkvm=enabled ;; --disable-kvm) printf "%s" -Dkvm=disabled ;; + --enable-l2tpv3) printf "%s" -Dl2tpv3=enabled ;; + --disable-l2tpv3) printf "%s" -Dl2tpv3=disabled ;; --enable-libdaxctl) printf "%s" -Dlibdaxctl=enabled ;; --disable-libdaxctl) printf "%s" -Dlibdaxctl=disabled ;; --enable-libiscsi) printf "%s" -Dlibiscsi=enabled ;; From 96c372d853eca988b6af216496e19fd24bfb1b78 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 28 Oct 2021 20:59:07 +0200 Subject: [PATCH 1106/1334] configure: Remove the check for the __thread keyword We recently bumped our minimum required version of GCC to 7.4 and Clang to 6.0, and those compiler versions should support the __thread keyword already. Signed-off-by: Thomas Huth Message-Id: <20211028185910.1729744-2-thuth@redhat.com> Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- configure | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/configure b/configure index 2268e5d0ab..4c444e1750 100755 --- a/configure +++ b/configure @@ -1724,17 +1724,6 @@ if test "$static" = "yes" ; then fi fi -# Unconditional check for compiler __thread support - cat > $TMPC << EOF -static __thread int tls_var; -int main(void) { return tls_var; } -EOF - -if ! compile_prog "-Werror" "" ; then - error_exit "Your compiler does not support the __thread specifier for " \ - "Thread-Local Storage (TLS). Please upgrade to a version that does." -fi - cat > $TMPC << EOF #ifdef __linux__ From 16bfbc70f39e420b6b6cfe39ed8571606482b94a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 2 Nov 2021 14:56:22 +0100 Subject: [PATCH 1107/1334] configure: fix --audio-drv-list help message --audio-drv-list is now establishing which audio drivers to try if -audiodev is not used; drivers for -audiodev are configured with --enable/--disable options or possibly --without-default-features. Adjust the help message for --audio-drv-list. Reported-by: Peter Maydell Signed-off-by: Paolo Bonzini --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 4c444e1750..97fee368ee 100755 --- a/configure +++ b/configure @@ -1390,7 +1390,7 @@ Advanced options (experts only): --disable-strip disable stripping binaries --disable-werror disable compilation abort on warning --disable-stack-protector disable compiler-provided stack protection - --audio-drv-list=LIST set audio drivers list + --audio-drv-list=LIST set audio drivers to try if -audiodev is not used --block-drv-whitelist=L Same as --block-drv-rw-whitelist=L --block-drv-rw-whitelist=L set block driver read-write whitelist From 3e11e0b2dd5b0ed98d129aeacf46276f3671b5f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 10 Sep 2021 17:48:34 +0100 Subject: [PATCH 1108/1334] monitor: remove 'info ioapic' HMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This command was turned into a no-op four years ago in commit 0c8465440d50c18a7bb13d0a866748f0593e193a Author: Peter Xu Date: Fri Dec 29 15:31:04 2017 +0800 hmp: obsolete "info ioapic" Reviewed-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hmp-commands-info.hx | 15 --------------- include/monitor/hmp-target.h | 1 - target/i386/monitor.c | 6 ------ 3 files changed, 22 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 4c966e8a6b..24c478aead 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -127,21 +127,6 @@ SRST Show local APIC state ERST -#if defined(TARGET_I386) - { - .name = "ioapic", - .args_type = "", - .params = "", - .help = "show io apic state", - .cmd = hmp_info_io_apic, - }, -#endif - -SRST - ``info ioapic`` - Show io APIC state -ERST - { .name = "cpus", .args_type = "", diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h index 96956d0fc4..ffdc15a34b 100644 --- a/include/monitor/hmp-target.h +++ b/include/monitor/hmp-target.h @@ -48,7 +48,6 @@ void hmp_info_mem(Monitor *mon, const QDict *qdict); void hmp_info_tlb(Monitor *mon, const QDict *qdict); void hmp_mce(Monitor *mon, const QDict *qdict); void hmp_info_local_apic(Monitor *mon, const QDict *qdict); -void hmp_info_io_apic(Monitor *mon, const QDict *qdict); void hmp_info_sev(Monitor *mon, const QDict *qdict); void hmp_info_sgx(Monitor *mon, const QDict *qdict); diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 8166e17693..8e4b4d600c 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -667,9 +667,3 @@ void hmp_info_local_apic(Monitor *mon, const QDict *qdict) } x86_cpu_dump_local_apic_state(cs, CPU_DUMP_FPU); } - -void hmp_info_io_apic(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "This command is obsolete and will be " - "removed soon. Please use 'info pic' instead.\n"); -} From 0ca117a756a79c0f93c9030b5c5a92982e3b0ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 28 Oct 2021 16:18:25 +0100 Subject: [PATCH 1109/1334] monitor: make hmp_handle_error return a boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This turns the pattern if (err) { hmp_handle_error(mon, err); return; } into if (hmp_handle_error(mon, err)) { return; } Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hw/core/machine-hmp-cmds.c | 3 +-- include/monitor/hmp.h | 2 +- monitor/hmp-cmds.c | 28 +++++++++++----------------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 76b22b00d6..c356783ab9 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -53,8 +53,7 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict) HotpluggableCPUList *saved = l; CpuInstanceProperties *c; - if (err != NULL) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 6bc27639e0..a2cb002a3a 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -16,7 +16,7 @@ #include "qemu/readline.h" -void hmp_handle_error(Monitor *mon, Error *err); +bool hmp_handle_error(Monitor *mon, Error *err); void hmp_info_name(Monitor *mon, const QDict *qdict); void hmp_info_version(Monitor *mon, const QDict *qdict); diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index bcaa41350e..9031cea881 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -62,11 +62,13 @@ #include #endif -void hmp_handle_error(Monitor *mon, Error *err) +bool hmp_handle_error(Monitor *mon, Error *err) { if (err) { error_reportf_err(err, "Error: "); + return true; } + return false; } /* @@ -577,8 +579,7 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict) info2l = qmp_query_vnc_servers(&err); info2l_head = info2l; - if (err) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } if (!info2l) { @@ -693,8 +694,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict) Error *err = NULL; info = qmp_query_balloon(&err); - if (err) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } @@ -1065,8 +1065,7 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) int i; data = qmp_ringbuf_read(chardev, size, false, 0, &err); - if (err) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } @@ -1582,8 +1581,7 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, true, resume, &err); - if (err) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } @@ -1917,8 +1915,7 @@ void hmp_rocker(Monitor *mon, const QDict *qdict) Error *err = NULL; rocker = qmp_query_rocker(name, &err); - if (err != NULL) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } @@ -1936,8 +1933,7 @@ void hmp_rocker_ports(Monitor *mon, const QDict *qdict) Error *err = NULL; list = qmp_query_rocker_ports(name, &err); - if (err != NULL) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } @@ -1965,8 +1961,7 @@ void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) Error *err = NULL; list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err); - if (err != NULL) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } @@ -2115,8 +2110,7 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) Error *err = NULL; list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err); - if (err != NULL) { - hmp_handle_error(mon, err); + if (hmp_handle_error(mon, err)) { return; } From 0e33e3d2c40f68f71d628dfe57d460f47e416e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 9 Sep 2021 16:54:02 +0100 Subject: [PATCH 1110/1334] docs/devel: rename file for writing monitor commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file already covers writing HMP commands, in addition to the QMP commands, so it deserves a more general name. Reviewed-by: Eric Blake Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- docs/devel/index.rst | 2 +- ...riting-qmp-commands.rst => writing-monitor-commands.rst} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename docs/devel/{writing-qmp-commands.rst => writing-monitor-commands.rst} (99%) diff --git a/docs/devel/index.rst b/docs/devel/index.rst index f95df10b3e..7c25177c5d 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -44,4 +44,4 @@ modifying QEMU's source code. ebpf_rss vfio-migration qapi-code-gen - writing-qmp-commands + writing-monitor-commands diff --git a/docs/devel/writing-qmp-commands.rst b/docs/devel/writing-monitor-commands.rst similarity index 99% rename from docs/devel/writing-qmp-commands.rst rename to docs/devel/writing-monitor-commands.rst index 6a10a06c48..4a4c051624 100644 --- a/docs/devel/writing-qmp-commands.rst +++ b/docs/devel/writing-monitor-commands.rst @@ -1,8 +1,8 @@ -How to write QMP commands using the QAPI framework -================================================== +How to write monitor commands +============================= This document is a step-by-step guide on how to write new QMP commands using -the QAPI framework. It also shows how to implement new style HMP commands. +the QAPI framework and HMP commands. This document doesn't discuss QMP protocol level details, nor does it dive into the QAPI framework implementation. From fa2613afa1ebe50c59606f1c30ad6738fe6919a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 9 Sep 2021 16:56:53 +0100 Subject: [PATCH 1111/1334] docs/devel: tweak headings in monitor command docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new headings reflect the intended structure of the document and will better suit additions that follow. Reviewed-by: Markus Armbruster Signed-off-by: Daniel P. Berrangé --- docs/devel/writing-monitor-commands.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/devel/writing-monitor-commands.rst b/docs/devel/writing-monitor-commands.rst index 4a4c051624..a973c48f66 100644 --- a/docs/devel/writing-monitor-commands.rst +++ b/docs/devel/writing-monitor-commands.rst @@ -85,8 +85,8 @@ any data". Now you're ready to enter the QMP example commands as explained in the following sections. -Writing a command that doesn't return data ------------------------------------------- +Writing a simple command: hello-world +------------------------------------- That's the most simple QMP command that can be written. Usually, this kind of command carries some meaningful action in QEMU but here it will just print @@ -340,8 +340,8 @@ Please, check the "-monitor" command-line option to know how to open a user monitor. -Writing a command that returns data ------------------------------------ +Writing more complex commands +----------------------------- A QMP command is capable of returning any data the QAPI supports like integers, strings, booleans, enumerations and user defined types. From 6fa6b54f5b931e10e24f773d991a48da4f79e61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 28 Oct 2021 16:13:55 +0100 Subject: [PATCH 1112/1334] docs/devel: update error handling guidance for HMP commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Best practice is to use the 'hmp_handle_error' function, not 'monitor_printf' or 'error_report_err'. This ensures that the message always gets an 'Error: ' prefix, distinguishing it from normal command output. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Daniel P. Berrangé --- docs/devel/writing-monitor-commands.rst | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/docs/devel/writing-monitor-commands.rst b/docs/devel/writing-monitor-commands.rst index a973c48f66..a381b52024 100644 --- a/docs/devel/writing-monitor-commands.rst +++ b/docs/devel/writing-monitor-commands.rst @@ -293,9 +293,7 @@ Here's the implementation of the "hello-world" HMP command:: Error *err = NULL; qmp_hello_world(!!message, message, &err); - if (err) { - monitor_printf(mon, "%s\n", error_get_pretty(err)); - error_free(err); + if (hmp_handle_error(mon, err)) { return; } } @@ -307,9 +305,10 @@ There are three important points to be noticed: 1. The "mon" and "qdict" arguments are mandatory for all HMP functions. The former is the monitor object. The latter is how the monitor passes arguments entered by the user to the command implementation -2. hmp_hello_world() performs error checking. In this example we just print - the error description to the user, but we could do more, like taking - different actions depending on the error qmp_hello_world() returns +2. hmp_hello_world() performs error checking. In this example we just call + hmp_handle_error() which prints a message to the user, but we could do + more, like taking different actions depending on the error + qmp_hello_world() returns 3. The "err" variable must be initialized to NULL before performing the QMP call @@ -466,9 +465,7 @@ Here's the HMP counterpart of the query-alarm-clock command:: Error *err = NULL; clock = qmp_query_alarm_clock(&err); - if (err) { - monitor_printf(mon, "Could not query alarm clock information\n"); - error_free(err); + if (hmp_handle_error(mon, err)) { return; } @@ -607,9 +604,7 @@ has to traverse the list, it's shown below for reference:: Error *err = NULL; method_list = qmp_query_alarm_methods(&err); - if (err) { - monitor_printf(mon, "Could not query alarm methods\n"); - error_free(err); + if (hmp_handle_error(mon, err)) { return; } From f9429c6790ce0c9f737d318eeff5c4a24f641ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 8 Oct 2021 15:09:00 +0100 Subject: [PATCH 1113/1334] monitor: introduce HumanReadableText and HMP support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This provides a foundation on which to convert simple HMP commands to use QMP. The QMP implementation will generate formatted text targeted for human consumption, returning it in the HumanReadableText data type. The HMP command handler will simply print out the formatted string within the HumanReadableText data type. Since this will be an entirely formulaic action in the case of HMP commands taking no arguments, a custom command handler is provided. Thus instead of registering a 'cmd' callback for the HMP command, a 'cmd_info_hrt' callback is provided, which will simply be a pointer to the QMP implementation. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- include/monitor/hmp.h | 3 +++ include/monitor/monitor.h | 2 ++ include/qapi/type-helpers.h | 14 ++++++++++++++ monitor/hmp.c | 32 +++++++++++++++++++++++++++++--- monitor/misc.c | 18 +++++++++++++++++- monitor/monitor-internal.h | 7 +++++++ qapi/common.json | 11 +++++++++++ qapi/meson.build | 3 +++ qapi/qapi-type-helpers.c | 23 +++++++++++++++++++++++ 9 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 include/qapi/type-helpers.h create mode 100644 qapi/qapi-type-helpers.c diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index a2cb002a3a..96d014826a 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -15,6 +15,7 @@ #define HMP_H #include "qemu/readline.h" +#include "qapi/qapi-types-common.h" bool hmp_handle_error(Monitor *mon, Error *err); @@ -130,5 +131,7 @@ void hmp_replay_delete_break(Monitor *mon, const QDict *qdict); void hmp_replay_seek(Monitor *mon, const QDict *qdict); void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict); void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict); +void hmp_human_readable_text_helper(Monitor *mon, + HumanReadableText *(*qmp_handler)(Error **)); #endif diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 1a8a369b50..12d395d62d 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -53,5 +53,7 @@ int64_t monitor_fdset_dup_fd_find(int dup_fd); void monitor_register_hmp(const char *name, bool info, void (*cmd)(Monitor *mon, const QDict *qdict)); +void monitor_register_hmp_info_hrt(const char *name, + HumanReadableText *(*handler)(Error **errp)); #endif /* MONITOR_H */ diff --git a/include/qapi/type-helpers.h b/include/qapi/type-helpers.h new file mode 100644 index 0000000000..be1f181526 --- /dev/null +++ b/include/qapi/type-helpers.h @@ -0,0 +1,14 @@ +/* + * QAPI common helper functions + * + * This file provides helper functions related to types defined + * in the QAPI schema. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qapi-types-common.h" + +HumanReadableText *human_readable_text_from_str(GString *str); diff --git a/monitor/hmp.c b/monitor/hmp.c index d50c3124e1..b20737e63c 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -26,6 +26,7 @@ #include #include "hw/qdev-core.h" #include "monitor-internal.h" +#include "monitor/hmp.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" @@ -1061,6 +1062,31 @@ fail: return NULL; } +static void hmp_info_human_readable_text(Monitor *mon, + HumanReadableText *(*handler)(Error **)) +{ + Error *err = NULL; + g_autoptr(HumanReadableText) info = handler(&err); + + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "%s", info->human_readable_text); +} + +static void handle_hmp_command_exec(Monitor *mon, + const HMPCommand *cmd, + QDict *qdict) +{ + if (cmd->cmd_info_hrt) { + hmp_info_human_readable_text(mon, + cmd->cmd_info_hrt); + } else { + cmd->cmd(mon, qdict); + } +} + typedef struct HandleHmpCommandCo { Monitor *mon; const HMPCommand *cmd; @@ -1071,7 +1097,7 @@ typedef struct HandleHmpCommandCo { static void handle_hmp_command_co(void *opaque) { HandleHmpCommandCo *data = opaque; - data->cmd->cmd(data->mon, data->qdict); + handle_hmp_command_exec(data->mon, data->cmd, data->qdict); monitor_set_cur(qemu_coroutine_self(), NULL); data->done = true; } @@ -1089,7 +1115,7 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) return; } - if (!cmd->cmd) { + if (!cmd->cmd && !cmd->cmd_info_hrt) { /* FIXME: is it useful to try autoload modules here ??? */ monitor_printf(&mon->common, "Command \"%.*s\" is not available.\n", (int)(cmdline - cmd_start), cmd_start); @@ -1109,7 +1135,7 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) if (!cmd->coroutine) { /* old_mon is non-NULL when called from qmp_human_monitor_command() */ Monitor *old_mon = monitor_set_cur(qemu_coroutine_self(), &mon->common); - cmd->cmd(&mon->common, qdict); + handle_hmp_command_exec(&mon->common, cmd, qdict); monitor_set_cur(qemu_coroutine_self(), old_mon); } else { HandleHmpCommandCo data = { diff --git a/monitor/misc.c b/monitor/misc.c index c2d227a07c..0e124044d0 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -1964,7 +1964,7 @@ void monitor_register_hmp(const char *name, bool info, while (table->name != NULL) { if (strcmp(table->name, name) == 0) { - g_assert(table->cmd == NULL); + g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); table->cmd = cmd; return; } @@ -1973,6 +1973,22 @@ void monitor_register_hmp(const char *name, bool info, g_assert_not_reached(); } +void monitor_register_hmp_info_hrt(const char *name, + HumanReadableText *(*handler)(Error **errp)) +{ + HMPCommand *table = hmp_info_cmds; + + while (table->name != NULL) { + if (strcmp(table->name, name) == 0) { + g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); + table->cmd_info_hrt = handler; + return; + } + table++; + } + g_assert_not_reached(); +} + void monitor_init_globals(void) { monitor_init_globals_core(); diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 9c3a09cb01..3da3f86c6a 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -74,6 +74,13 @@ typedef struct HMPCommand { const char *help; const char *flags; /* p=preconfig */ void (*cmd)(Monitor *mon, const QDict *qdict); + /* + * If implementing a command that takes no arguments and simply + * prints formatted data, then leave @cmd NULL, and then set + * @cmd_info_hrt to the corresponding QMP handler that returns + * the formatted text. + */ + HumanReadableText *(*cmd_info_hrt)(Error **errp); bool coroutine; /* * @sub_table is a list of 2nd level of commands. If it does not exist, diff --git a/qapi/common.json b/qapi/common.json index 7c976296f0..412cc4f5ae 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -197,3 +197,14 @@ { 'enum': 'GrabToggleKeys', 'data': [ 'ctrl-ctrl', 'alt-alt', 'shift-shift','meta-meta', 'scrolllock', 'ctrl-scrolllock' ] } + +## +# @HumanReadableText: +# +# @human-readable-text: Formatted output intended for humans. +# +# Since: 6.2 +# +## +{ 'struct': 'HumanReadableText', + 'data': { 'human-readable-text': 'str' } } diff --git a/qapi/meson.build b/qapi/meson.build index c356a385e3..c0c49c15e4 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -10,6 +10,9 @@ util_ss.add(files( 'string-input-visitor.c', 'string-output-visitor.c', )) +if have_system + util_ss.add(files('qapi-type-helpers.c')) +endif if have_system or have_tools util_ss.add(files( 'qmp-dispatch.c', diff --git a/qapi/qapi-type-helpers.c b/qapi/qapi-type-helpers.c new file mode 100644 index 0000000000..f76b34f647 --- /dev/null +++ b/qapi/qapi-type-helpers.c @@ -0,0 +1,23 @@ +/* + * QAPI common helper functions + * + * This file provides helper functions related to types defined + * in the QAPI schema. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/type-helpers.h" + +HumanReadableText *human_readable_text_from_str(GString *str) +{ + HumanReadableText *ret = g_new0(HumanReadableText, 1); + + ret->human_readable_text = g_steal_pointer(&str->str); + + return ret; +} From f2de406f29e5fbdc2fcc5c9cb48d29293fba58a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 11:02:39 +0100 Subject: [PATCH 1114/1334] docs/devel: document expectations for QAPI data modelling for QMP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Traditionally we have required that newly added QMP commands will model any returned data using fine grained QAPI types. This is good for commands that are intended to be consumed by machines, where clear data representation is very important. Commands that don't satisfy this have generally been added to HMP only. In effect the decision of whether to add a new command to QMP vs HMP has been used as a proxy for the decision of whether the cost of designing a fine grained QAPI type is justified by the potential benefits. As a result the commands present in QMP and HMP are non-overlapping sets, although HMP comamnds can be accessed indirectly via the QMP command 'human-monitor-command'. One of the downsides of 'human-monitor-command' is that the QEMU monitor APIs remain tied into various internal parts of the QEMU code. For example any exclusively HMP command will need to use 'monitor_printf' to get data out. It would be desirable to be able to fully isolate the monitor implementation from QEMU internals, however, this is only possible if all commands are exclusively based on QAPI with direct QMP exposure. The way to achieve this desired end goal is to finese the requirements for QMP command design. For cases where the output of a command is only intended for human consumption, it is reasonable to want to simplify the implementation by returning a plain string containing formatted data instead of designing a fine grained QAPI data type. This can be permitted if-and-only-if the command is exposed under the 'x-' name prefix. This indicates that the command data format is liable to future change and that it is not following QAPI design best practice. The poster child example for this would be the 'info registers' HMP command which returns printf formatted data representing CPU state. This information varies enourmously across target architectures and changes relatively frequently as new CPU features are implemented. It is there as debugging data for human operators, and any machine usage would treat it as an opaque blob. It is thus reasonable to expose this in QMP as 'x-query-registers' returning a 'str' field. Reviewed-by: Markus Armbruster Signed-off-by: Daniel P. Berrangé --- docs/devel/writing-monitor-commands.rst | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/devel/writing-monitor-commands.rst b/docs/devel/writing-monitor-commands.rst index a381b52024..031e980bf5 100644 --- a/docs/devel/writing-monitor-commands.rst +++ b/docs/devel/writing-monitor-commands.rst @@ -349,6 +349,33 @@ In this section we will focus on user defined types. Please, check the QAPI documentation for information about the other types. +Modelling data in QAPI +~~~~~~~~~~~~~~~~~~~~~~ + +For a QMP command that to be considered stable and supported long term, +there is a requirement returned data should be explicitly modelled +using fine-grained QAPI types. As a general guide, a caller of the QMP +command should never need to parse individual returned data fields. If +a field appears to need parsing, then it should be split into separate +fields corresponding to each distinct data item. This should be the +common case for any new QMP command that is intended to be used by +machines, as opposed to exclusively human operators. + +Some QMP commands, however, are only intended as ad hoc debugging aids +for human operators. While they may return large amounts of formatted +data, it is not expected that machines will need to parse the result. +The overhead of defining a fine grained QAPI type for the data may not +be justified by the potential benefit. In such cases, it is permitted +to have a command return a simple string that contains formatted data, +however, it is mandatory for the command to use the 'x-' name prefix. +This indicates that the command is not guaranteed to be long term +stable / liable to change in future and is not following QAPI design +best practices. An example where this approach is taken is the QMP +command "x-query-registers". This returns a formatted dump of the +architecture specific CPU state. The way the data is formatted varies +across QEMU targets, is liable to change over time, and is only +intended to be consumed as an opaque string by machines. + User Defined Types ~~~~~~~~~~~~~~~~~~ From a45cfcbb0158af05716bc06fd77f93ba883b13e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 10 Sep 2021 14:50:59 +0100 Subject: [PATCH 1115/1334] docs/devel: add example of command returning unstructured text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This illustrates how to add a QMP command returning unstructured text, following the guidelines added in the previous patch. The example uses a simplified version of 'info roms'. Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrangé --- docs/devel/writing-monitor-commands.rst | 101 +++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/docs/devel/writing-monitor-commands.rst b/docs/devel/writing-monitor-commands.rst index 031e980bf5..b87992df91 100644 --- a/docs/devel/writing-monitor-commands.rst +++ b/docs/devel/writing-monitor-commands.rst @@ -374,7 +374,9 @@ best practices. An example where this approach is taken is the QMP command "x-query-registers". This returns a formatted dump of the architecture specific CPU state. The way the data is formatted varies across QEMU targets, is liable to change over time, and is only -intended to be consumed as an opaque string by machines. +intended to be consumed as an opaque string by machines. Refer to the +`Writing a debugging aid returning unstructured text`_ section for +an illustration. User Defined Types ~~~~~~~~~~~~~~~~~~ @@ -642,3 +644,100 @@ has to traverse the list, it's shown below for reference:: qapi_free_TimerAlarmMethodList(method_list); } + +Writing a debugging aid returning unstructured text +--------------------------------------------------- + +As discussed in section `Modelling data in QAPI`_, it is required that +commands expecting machine usage be using fine-grained QAPI data types. +The exception to this rule applies when the command is solely intended +as a debugging aid and allows for returning unstructured text. This is +commonly needed for query commands that report aspects of QEMU's +internal state that are useful to human operators. + +In this example we will consider a simplified variant of the HMP +command ``info roms``. Following the earlier rules, this command will +need to live under the ``x-`` name prefix, so its QMP implementation +will be called ``x-query-roms``. It will have no parameters and will +return a single text string:: + + { 'struct': 'HumanReadableText', + 'data': { 'human-readable-text': 'str' } } + + { 'command': 'x-query-roms', + 'returns': 'HumanReadableText' } + +The ``HumanReadableText`` struct is intended to be used for all +commands, under the ``x-`` name prefix that are returning unstructured +text targetted at humans. It should never be used for commands outside +the ``x-`` name prefix, as those should be using structured QAPI types. + +Implementing the QMP command +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The QMP implementation will typically involve creating a ``GString`` +object and printing formatted data into it:: + + HumanReadableText *qmp_x_query_roms(Error **errp) + { + g_autoptr(GString) buf = g_string_new(""); + Rom *rom; + + QTAILQ_FOREACH(rom, &roms, next) { + g_string_append_printf("%s size=0x%06zx name=\"%s\"\n", + memory_region_name(rom->mr), + rom->romsize, + rom->name); + } + + return human_readable_text_from_str(buf); + } + + +Implementing the HMP command +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that the QMP command is in place, we can also make it available in +the human monitor (HMP) as shown in previous examples. The HMP +implementations will all look fairly similar, as all they need do is +invoke the QMP command and then print the resulting text or error +message. Here's the implementation of the "info roms" HMP command:: + + void hmp_info_roms(Monitor *mon, const QDict *qdict) + { + Error err = NULL; + g_autoptr(HumanReadableText) info = qmp_x_query_roms(&err); + + if (hmp_handle_error(mon, err)) { + return; + } + monitor_printf(mon, "%s", info->human_readable_text); + } + +Also, you have to add the function's prototype to the hmp.h file. + +There's one last step to actually make the command available to +monitor users, we should add it to the hmp-commands-info.hx file:: + + { + .name = "roms", + .args_type = "", + .params = "", + .help = "show roms", + .cmd = hmp_info_roms, + }, + +The case of writing a HMP info handler that calls a no-parameter QMP query +command is quite common. To simplify the implementation there is a general +purpose HMP info handler for this scenario. All that is required to expose +a no-parameter QMP query command via HMP is to declare it using the +'.cmd_info_hrt' field to point to the QMP handler, and leave the '.cmd' +field NULL:: + + { + .name = "roms", + .args_type = "", + .params = "", + .help = "show roms", + .cmd_info_hrt = qmp_x_query_roms, + }, From 3d312f417d6c3baf7e2b3f321e17ed9d74471ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 9 Sep 2021 17:01:34 +0100 Subject: [PATCH 1116/1334] docs/devel: document expectations for HMP commands in the future MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We no longer wish to have commands implemented in HMP only. All commands should start with a QMP implementation and the HMP merely be a shim around this. To reduce the burden of implementing QMP commands where there is low expectation of machine usage, requirements for QAPI modelling are relaxed provided the command is under the "x-" name prefix. Reviewed-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: Daniel P. Berrangé --- docs/devel/writing-monitor-commands.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/devel/writing-monitor-commands.rst b/docs/devel/writing-monitor-commands.rst index b87992df91..b3e2c8481d 100644 --- a/docs/devel/writing-monitor-commands.rst +++ b/docs/devel/writing-monitor-commands.rst @@ -11,6 +11,14 @@ For an in-depth introduction to the QAPI framework, please refer to docs/devel/qapi-code-gen.txt. For documentation about the QMP protocol, start with docs/interop/qmp-intro.txt. +New commands may be implemented in QMP only. New HMP commands should be +implemented on top of QMP. The typical HMP command wraps around an +equivalent QMP command, but HMP convenience commands built from QMP +building blocks are also fine. The long term goal is to make all +existing HMP commands conform to this, to fully isolate HMP from the +internals of QEMU. Refer to the `Writing a debugging aid returning +unstructured text`_ section for further guidance on commands that +would have traditionally been HMP only. Overview -------- From dd98234c059e6bdb05a52998270df6d3d990332e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1117/1334] qapi: introduce x-query-roms QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info roms" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hmp-commands-info.hx | 2 +- hw/core/loader.c | 39 ++++++++++++++++++++++----------------- monitor/misc.c | 1 + qapi/machine.json | 12 ++++++++++++ 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 24c478aead..b6325d36ed 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -594,7 +594,7 @@ ERST .args_type = "", .params = "", .help = "show roms", - .cmd = hmp_info_roms, + .cmd_info_hrt = qmp_x_query_roms, }, SRST diff --git a/hw/core/loader.c b/hw/core/loader.c index c7f97fdce8..052a0fd719 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -46,6 +46,8 @@ #include "qemu-common.h" #include "qemu/datadir.h" #include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/type-helpers.h" #include "trace.h" #include "hw/hw.h" #include "disas/disas.h" @@ -1474,32 +1476,35 @@ void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size) return cbdata.rom; } -void hmp_info_roms(Monitor *mon, const QDict *qdict) +HumanReadableText *qmp_x_query_roms(Error **errp) { Rom *rom; + g_autoptr(GString) buf = g_string_new(""); QTAILQ_FOREACH(rom, &roms, next) { if (rom->mr) { - monitor_printf(mon, "%s" - " size=0x%06zx name=\"%s\"\n", - memory_region_name(rom->mr), - rom->romsize, - rom->name); + g_string_append_printf(buf, "%s" + " size=0x%06zx name=\"%s\"\n", + memory_region_name(rom->mr), + rom->romsize, + rom->name); } else if (!rom->fw_file) { - monitor_printf(mon, "addr=" TARGET_FMT_plx - " size=0x%06zx mem=%s name=\"%s\"\n", - rom->addr, rom->romsize, - rom->isrom ? "rom" : "ram", - rom->name); + g_string_append_printf(buf, "addr=" TARGET_FMT_plx + " size=0x%06zx mem=%s name=\"%s\"\n", + rom->addr, rom->romsize, + rom->isrom ? "rom" : "ram", + rom->name); } else { - monitor_printf(mon, "fw=%s/%s" - " size=0x%06zx name=\"%s\"\n", - rom->fw_dir, - rom->fw_file, - rom->romsize, - rom->name); + g_string_append_printf(buf, "fw=%s/%s" + " size=0x%06zx name=\"%s\"\n", + rom->fw_dir, + rom->fw_file, + rom->romsize, + rom->name); } } + + return human_readable_text_from_str(buf); } typedef enum HexRecord HexRecord; diff --git a/monitor/misc.c b/monitor/misc.c index 0e124044d0..c3efdf6336 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -71,6 +71,7 @@ #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-qom.h" #include "qapi/qapi-commands-trace.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-init-commands.h" #include "qapi/error.h" #include "qapi/qmp-event.h" diff --git a/qapi/machine.json b/qapi/machine.json index 5db54df298..26d4ef8195 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1411,3 +1411,15 @@ '*cores': 'int', '*threads': 'int', '*maxcpus': 'int' } } + +## +# @x-query-roms: +# +# Query information on the registered ROMS +# +# Returns: registered ROMs +# +# Since: 6.2 +## +{ 'command': 'x-query-roms', + 'returns': 'HumanReadableText' } From 37087fde0e6b81bdc6aefb1cd6289d90b2095ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1118/1334] qapi: introduce x-query-profile QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info profile" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hmp-commands-info.hx | 2 +- monitor/misc.c | 27 --------------------------- monitor/qmp-cmds.c | 32 ++++++++++++++++++++++++++++++++ qapi/machine.json | 12 ++++++++++++ tests/qtest/qmp-cmd-test.c | 3 +++ 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index b6325d36ed..84c5e0da10 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -363,7 +363,7 @@ ERST .args_type = "", .params = "", .help = "show profiling information", - .cmd = hmp_info_profile, + .cmd_info_hrt = qmp_x_query_profile, }, SRST diff --git a/monitor/misc.c b/monitor/misc.c index c3efdf6336..fabd25a241 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -931,33 +931,6 @@ static void hmp_info_mtree(Monitor *mon, const QDict *qdict) mtree_info(flatview, dispatch_tree, owner, disabled); } -#ifdef CONFIG_PROFILER - -int64_t dev_time; - -static void hmp_info_profile(Monitor *mon, const QDict *qdict) -{ - static int64_t last_cpu_exec_time; - int64_t cpu_exec_time; - int64_t delta; - - cpu_exec_time = tcg_cpu_exec_time(); - delta = cpu_exec_time - last_cpu_exec_time; - - monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n", - dev_time, dev_time / (double)NANOSECONDS_PER_SECOND); - monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n", - delta, delta / (double)NANOSECONDS_PER_SECOND); - last_cpu_exec_time = cpu_exec_time; - dev_time = 0; -} -#else -static void hmp_info_profile(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "Internal profiler not compiled\n"); -} -#endif - /* Capture support */ static QLIST_HEAD (capture_list_head, CaptureState) capture_head; diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 5c0d5e116b..6122ad18b6 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -36,6 +36,7 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-ui.h" +#include "qapi/type-helpers.h" #include "qapi/qmp/qerror.h" #include "hw/mem/memory-device.h" #include "hw/acpi/acpi_dev_interface.h" @@ -350,3 +351,34 @@ void qmp_display_reload(DisplayReloadOptions *arg, Error **errp) abort(); } } + +#ifdef CONFIG_PROFILER + +int64_t dev_time; + +HumanReadableText *qmp_x_query_profile(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + static int64_t last_cpu_exec_time; + int64_t cpu_exec_time; + int64_t delta; + + cpu_exec_time = tcg_cpu_exec_time(); + delta = cpu_exec_time - last_cpu_exec_time; + + g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n", + dev_time, dev_time / (double)NANOSECONDS_PER_SECOND); + g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n", + delta, delta / (double)NANOSECONDS_PER_SECOND); + last_cpu_exec_time = cpu_exec_time; + dev_time = 0; + + return human_readable_text_from_str(buf); +} +#else +HumanReadableText *qmp_x_query_profile(Error **errp) +{ + error_setg(errp, "Internal profiler not compiled"); + return NULL; +} +#endif diff --git a/qapi/machine.json b/qapi/machine.json index 26d4ef8195..73ff4bc168 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1412,6 +1412,18 @@ '*threads': 'int', '*maxcpus': 'int' } } +## +# @x-query-profile: +# +# Query TCG profiling information +# +# Returns: profile information +# +# Since: 6.2 +## +{ 'command': 'x-query-profile', + 'returns': 'HumanReadableText' } + ## # @x-query-roms: # diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 1af2f74c28..372c887eea 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -46,6 +46,9 @@ static int query_error_class(const char *cmd) { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, +#ifndef CONFIG_PROFILER + { "x-query-profile", ERROR_CLASS_GENERIC_ERROR }, +#endif { NULL, -1 } }; int i; From 1b8ae799d8916dd589cc31db697a82b4e86880d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1119/1334] qapi: introduce x-query-numa QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info numa" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hmp-commands-info.hx | 2 +- hw/core/machine-hmp-cmds.c | 35 --------------------------------- hw/core/machine-qmp-cmds.c | 40 ++++++++++++++++++++++++++++++++++++++ qapi/machine.json | 12 ++++++++++++ 4 files changed, 53 insertions(+), 36 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 84c5e0da10..f66b1ca986 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -325,7 +325,7 @@ ERST .args_type = "", .params = "", .help = "show NUMA information", - .cmd = hmp_info_numa, + .cmd_info_hrt = qmp_x_query_numa, }, SRST diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index c356783ab9..4e2f319aeb 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -130,38 +130,3 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) qapi_free_MemdevList(memdev_list); hmp_handle_error(mon, err); } - -void hmp_info_numa(Monitor *mon, const QDict *qdict) -{ - int i, nb_numa_nodes; - NumaNodeMem *node_mem; - CpuInfoFastList *cpu_list, *cpu; - MachineState *ms = MACHINE(qdev_get_machine()); - - nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0; - monitor_printf(mon, "%d nodes\n", nb_numa_nodes); - if (!nb_numa_nodes) { - return; - } - - cpu_list = qmp_query_cpus_fast(&error_abort); - node_mem = g_new0(NumaNodeMem, nb_numa_nodes); - - query_numa_node_mem(node_mem, ms); - for (i = 0; i < nb_numa_nodes; i++) { - monitor_printf(mon, "node %d cpus:", i); - for (cpu = cpu_list; cpu; cpu = cpu->next) { - if (cpu->value->has_props && cpu->value->props->has_node_id && - cpu->value->props->node_id == i) { - monitor_printf(mon, " %" PRIi64, cpu->value->cpu_index); - } - } - monitor_printf(mon, "\n"); - monitor_printf(mon, "node %d size: %" PRId64 " MB\n", i, - node_mem[i].node_mem >> 20); - monitor_printf(mon, "node %d plugged: %" PRId64 " MB\n", i, - node_mem[i].node_plugged_mem >> 20); - } - qapi_free_CpuInfoFastList(cpu_list); - g_free(node_mem); -} diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 216fdfaf3a..4f4ab30f8c 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -15,6 +15,7 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/qobject.h" #include "qapi/qobject-input-visitor.h" +#include "qapi/type-helpers.h" #include "qemu/main-loop.h" #include "qom/qom-qobject.h" #include "sysemu/hostmem.h" @@ -204,3 +205,42 @@ MemdevList *qmp_query_memdev(Error **errp) object_child_foreach(obj, query_memdev, &list); return list; } + +HumanReadableText *qmp_x_query_numa(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + int i, nb_numa_nodes; + NumaNodeMem *node_mem; + CpuInfoFastList *cpu_list, *cpu; + MachineState *ms = MACHINE(qdev_get_machine()); + + nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0; + g_string_append_printf(buf, "%d nodes\n", nb_numa_nodes); + if (!nb_numa_nodes) { + goto done; + } + + cpu_list = qmp_query_cpus_fast(&error_abort); + node_mem = g_new0(NumaNodeMem, nb_numa_nodes); + + query_numa_node_mem(node_mem, ms); + for (i = 0; i < nb_numa_nodes; i++) { + g_string_append_printf(buf, "node %d cpus:", i); + for (cpu = cpu_list; cpu; cpu = cpu->next) { + if (cpu->value->has_props && cpu->value->props->has_node_id && + cpu->value->props->node_id == i) { + g_string_append_printf(buf, " %" PRIi64, cpu->value->cpu_index); + } + } + g_string_append_printf(buf, "\n"); + g_string_append_printf(buf, "node %d size: %" PRId64 " MB\n", i, + node_mem[i].node_mem >> 20); + g_string_append_printf(buf, "node %d plugged: %" PRId64 " MB\n", i, + node_mem[i].node_plugged_mem >> 20); + } + qapi_free_CpuInfoFastList(cpu_list); + g_free(node_mem); + + done: + return human_readable_text_from_str(buf); +} diff --git a/qapi/machine.json b/qapi/machine.json index 73ff4bc168..3732f80a82 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1412,6 +1412,18 @@ '*threads': 'int', '*maxcpus': 'int' } } +## +# @x-query-numa: +# +# Query NUMA topology information +# +# Returns: topology information +# +# Since: 6.2 +## +{ 'command': 'x-query-numa', + 'returns': 'HumanReadableText' } + ## # @x-query-profile: # From fc30920731470a44c69c8359be45b72ac7314fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1120/1334] qapi: introduce x-query-usb QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info usb" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hmp-commands-info.hx | 2 +- hw/usb/bus.c | 24 +++++++++++++++--------- qapi/machine.json | 12 ++++++++++++ stubs/usb-dev-stub.c | 8 ++++++++ tests/qtest/qmp-cmd-test.c | 2 ++ 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index f66b1ca986..ef1bfe4f5a 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -338,7 +338,7 @@ ERST .args_type = "", .params = "", .help = "show guest USB devices", - .cmd = hmp_info_usb, + .cmd_info_hrt = qmp_x_query_usb, }, SRST diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 5d441a7065..92d6ed5626 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -2,6 +2,8 @@ #include "hw/qdev-properties.h" #include "hw/usb.h" #include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/type-helpers.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "sysemu/sysemu.h" @@ -631,15 +633,16 @@ static char *usb_get_fw_dev_path(DeviceState *qdev) return fw_path; } -void hmp_info_usb(Monitor *mon, const QDict *qdict) +HumanReadableText *qmp_x_query_usb(Error **errp) { + g_autoptr(GString) buf = g_string_new(""); USBBus *bus; USBDevice *dev; USBPort *port; if (QTAILQ_EMPTY(&busses)) { - monitor_printf(mon, "USB support not enabled\n"); - return; + error_setg(errp, "USB support not enabled"); + return NULL; } QTAILQ_FOREACH(bus, &busses, next) { @@ -647,14 +650,17 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict) dev = port->dev; if (!dev) continue; - monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, " - "Product %s%s%s\n", - bus->busnr, dev->addr, port->path, - usb_speed(dev->speed), dev->product_desc, - dev->qdev.id ? ", ID: " : "", - dev->qdev.id ?: ""); + g_string_append_printf(buf, + " Device %d.%d, Port %s, Speed %s Mb/s, " + "Product %s%s%s\n", + bus->busnr, dev->addr, port->path, + usb_speed(dev->speed), dev->product_desc, + dev->qdev.id ? ", ID: " : "", + dev->qdev.id ?: ""); } } + + return human_readable_text_from_str(buf); } /* handle legacy -usbdevice cmd line option */ diff --git a/qapi/machine.json b/qapi/machine.json index 3732f80a82..15b6c98597 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1447,3 +1447,15 @@ ## { 'command': 'x-query-roms', 'returns': 'HumanReadableText' } + +## +# @x-query-usb: +# +# Query information on the USB devices +# +# Returns: USB device information +# +# Since: 6.2 +## +{ 'command': 'x-query-usb', + 'returns': 'HumanReadableText' } diff --git a/stubs/usb-dev-stub.c b/stubs/usb-dev-stub.c index b1adeeb454..aa557692b7 100644 --- a/stubs/usb-dev-stub.c +++ b/stubs/usb-dev-stub.c @@ -8,6 +8,8 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" #include "sysemu/sysemu.h" #include "monitor/monitor.h" #include "hw/usb.h" @@ -19,6 +21,12 @@ USBDevice *usbdevice_create(const char *driver) return NULL; } +HumanReadableText *qmp_x_query_usb(Error **errp) +{ + error_setg(errp, "Support for USB devices not built-in"); + return NULL; +} + void hmp_info_usb(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "Support for USB devices not built-in\n"); diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 372c887eea..0d52ea6c4b 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -49,6 +49,8 @@ static int query_error_class(const char *cmd) #ifndef CONFIG_PROFILER { "x-query-profile", ERROR_CLASS_GENERIC_ERROR }, #endif + /* Only valid with a USB bus added */ + { "x-query-usb", ERROR_CLASS_GENERIC_ERROR }, { NULL, -1 } }; int i; From 8dbbca5c056842d53498f643a15cac8593d51424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1121/1334] qapi: introduce x-query-rdma QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info rdma" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hmp-commands-info.hx | 2 +- hw/rdma/rdma_rm.c | 102 +++++++++++++++++++------------------- hw/rdma/rdma_rm.h | 2 +- hw/rdma/vmw/pvrdma_main.c | 31 ++++++------ include/hw/rdma/rdma.h | 2 +- monitor/hmp-cmds.c | 27 ---------- monitor/qmp-cmds.c | 32 ++++++++++++ qapi/machine.json | 12 +++++ 8 files changed, 114 insertions(+), 96 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index ef1bfe4f5a..d9af216473 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -185,7 +185,7 @@ ERST .args_type = "", .params = "", .help = "show RDMA state", - .cmd = hmp_info_rdma, + .cmd_info_hrt = qmp_x_query_rdma, }, SRST diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c index 49141d4074..cfd85de3e6 100644 --- a/hw/rdma/rdma_rm.c +++ b/hw/rdma/rdma_rm.c @@ -27,58 +27,58 @@ #define PG_DIR_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } #define PG_TBL_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } -void rdma_dump_device_counters(Monitor *mon, RdmaDeviceResources *dev_res) +void rdma_format_device_counters(RdmaDeviceResources *dev_res, GString *buf) { - monitor_printf(mon, "\ttx : %" PRId64 "\n", - dev_res->stats.tx); - monitor_printf(mon, "\ttx_len : %" PRId64 "\n", - dev_res->stats.tx_len); - monitor_printf(mon, "\ttx_err : %" PRId64 "\n", - dev_res->stats.tx_err); - monitor_printf(mon, "\trx_bufs : %" PRId64 "\n", - dev_res->stats.rx_bufs); - monitor_printf(mon, "\trx_srq : %" PRId64 "\n", - dev_res->stats.rx_srq); - monitor_printf(mon, "\trx_bufs_len : %" PRId64 "\n", - dev_res->stats.rx_bufs_len); - monitor_printf(mon, "\trx_bufs_err : %" PRId64 "\n", - dev_res->stats.rx_bufs_err); - monitor_printf(mon, "\tcomps : %" PRId64 "\n", - dev_res->stats.completions); - monitor_printf(mon, "\tmissing_comps : %" PRId32 "\n", - dev_res->stats.missing_cqe); - monitor_printf(mon, "\tpoll_cq (bk) : %" PRId64 "\n", - dev_res->stats.poll_cq_from_bk); - monitor_printf(mon, "\tpoll_cq_ppoll_to : %" PRId64 "\n", - dev_res->stats.poll_cq_ppoll_to); - monitor_printf(mon, "\tpoll_cq (fe) : %" PRId64 "\n", - dev_res->stats.poll_cq_from_guest); - monitor_printf(mon, "\tpoll_cq_empty : %" PRId64 "\n", - dev_res->stats.poll_cq_from_guest_empty); - monitor_printf(mon, "\tmad_tx : %" PRId64 "\n", - dev_res->stats.mad_tx); - monitor_printf(mon, "\tmad_tx_err : %" PRId64 "\n", - dev_res->stats.mad_tx_err); - monitor_printf(mon, "\tmad_rx : %" PRId64 "\n", - dev_res->stats.mad_rx); - monitor_printf(mon, "\tmad_rx_err : %" PRId64 "\n", - dev_res->stats.mad_rx_err); - monitor_printf(mon, "\tmad_rx_bufs : %" PRId64 "\n", - dev_res->stats.mad_rx_bufs); - monitor_printf(mon, "\tmad_rx_bufs_err : %" PRId64 "\n", - dev_res->stats.mad_rx_bufs_err); - monitor_printf(mon, "\tPDs : %" PRId32 "\n", - dev_res->pd_tbl.used); - monitor_printf(mon, "\tMRs : %" PRId32 "\n", - dev_res->mr_tbl.used); - monitor_printf(mon, "\tUCs : %" PRId32 "\n", - dev_res->uc_tbl.used); - monitor_printf(mon, "\tQPs : %" PRId32 "\n", - dev_res->qp_tbl.used); - monitor_printf(mon, "\tCQs : %" PRId32 "\n", - dev_res->cq_tbl.used); - monitor_printf(mon, "\tCEQ_CTXs : %" PRId32 "\n", - dev_res->cqe_ctx_tbl.used); + g_string_append_printf(buf, "\ttx : %" PRId64 "\n", + dev_res->stats.tx); + g_string_append_printf(buf, "\ttx_len : %" PRId64 "\n", + dev_res->stats.tx_len); + g_string_append_printf(buf, "\ttx_err : %" PRId64 "\n", + dev_res->stats.tx_err); + g_string_append_printf(buf, "\trx_bufs : %" PRId64 "\n", + dev_res->stats.rx_bufs); + g_string_append_printf(buf, "\trx_srq : %" PRId64 "\n", + dev_res->stats.rx_srq); + g_string_append_printf(buf, "\trx_bufs_len : %" PRId64 "\n", + dev_res->stats.rx_bufs_len); + g_string_append_printf(buf, "\trx_bufs_err : %" PRId64 "\n", + dev_res->stats.rx_bufs_err); + g_string_append_printf(buf, "\tcomps : %" PRId64 "\n", + dev_res->stats.completions); + g_string_append_printf(buf, "\tmissing_comps : %" PRId32 "\n", + dev_res->stats.missing_cqe); + g_string_append_printf(buf, "\tpoll_cq (bk) : %" PRId64 "\n", + dev_res->stats.poll_cq_from_bk); + g_string_append_printf(buf, "\tpoll_cq_ppoll_to : %" PRId64 "\n", + dev_res->stats.poll_cq_ppoll_to); + g_string_append_printf(buf, "\tpoll_cq (fe) : %" PRId64 "\n", + dev_res->stats.poll_cq_from_guest); + g_string_append_printf(buf, "\tpoll_cq_empty : %" PRId64 "\n", + dev_res->stats.poll_cq_from_guest_empty); + g_string_append_printf(buf, "\tmad_tx : %" PRId64 "\n", + dev_res->stats.mad_tx); + g_string_append_printf(buf, "\tmad_tx_err : %" PRId64 "\n", + dev_res->stats.mad_tx_err); + g_string_append_printf(buf, "\tmad_rx : %" PRId64 "\n", + dev_res->stats.mad_rx); + g_string_append_printf(buf, "\tmad_rx_err : %" PRId64 "\n", + dev_res->stats.mad_rx_err); + g_string_append_printf(buf, "\tmad_rx_bufs : %" PRId64 "\n", + dev_res->stats.mad_rx_bufs); + g_string_append_printf(buf, "\tmad_rx_bufs_err : %" PRId64 "\n", + dev_res->stats.mad_rx_bufs_err); + g_string_append_printf(buf, "\tPDs : %" PRId32 "\n", + dev_res->pd_tbl.used); + g_string_append_printf(buf, "\tMRs : %" PRId32 "\n", + dev_res->mr_tbl.used); + g_string_append_printf(buf, "\tUCs : %" PRId32 "\n", + dev_res->uc_tbl.used); + g_string_append_printf(buf, "\tQPs : %" PRId32 "\n", + dev_res->qp_tbl.used); + g_string_append_printf(buf, "\tCQs : %" PRId32 "\n", + dev_res->cq_tbl.used); + g_string_append_printf(buf, "\tCEQ_CTXs : %" PRId32 "\n", + dev_res->cqe_ctx_tbl.used); } static inline void res_tbl_init(const char *name, RdmaRmResTbl *tbl, diff --git a/hw/rdma/rdma_rm.h b/hw/rdma/rdma_rm.h index e8639909cd..d69a917795 100644 --- a/hw/rdma/rdma_rm.h +++ b/hw/rdma/rdma_rm.h @@ -92,6 +92,6 @@ static inline union ibv_gid *rdma_rm_get_gid(RdmaDeviceResources *dev_res, { return &dev_res->port.gid_tbl[sgid_idx].gid; } -void rdma_dump_device_counters(Monitor *mon, RdmaDeviceResources *dev_res); +void rdma_format_device_counters(RdmaDeviceResources *dev_res, GString *buf); #endif diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index 7c0c3551a8..91206dbb8e 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -58,24 +58,25 @@ static Property pvrdma_dev_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void pvrdma_print_statistics(Monitor *mon, RdmaProvider *obj) +static void pvrdma_format_statistics(RdmaProvider *obj, GString *buf) { PVRDMADev *dev = PVRDMA_DEV(obj); PCIDevice *pdev = PCI_DEVICE(dev); - monitor_printf(mon, "%s, %x.%x\n", pdev->name, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn)); - monitor_printf(mon, "\tcommands : %" PRId64 "\n", - dev->stats.commands); - monitor_printf(mon, "\tregs_reads : %" PRId64 "\n", - dev->stats.regs_reads); - monitor_printf(mon, "\tregs_writes : %" PRId64 "\n", - dev->stats.regs_writes); - monitor_printf(mon, "\tuar_writes : %" PRId64 "\n", - dev->stats.uar_writes); - monitor_printf(mon, "\tinterrupts : %" PRId64 "\n", - dev->stats.interrupts); - rdma_dump_device_counters(mon, &dev->rdma_dev_res); + g_string_append_printf(buf, "%s, %x.%x\n", + pdev->name, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + g_string_append_printf(buf, "\tcommands : %" PRId64 "\n", + dev->stats.commands); + g_string_append_printf(buf, "\tregs_reads : %" PRId64 "\n", + dev->stats.regs_reads); + g_string_append_printf(buf, "\tregs_writes : %" PRId64 "\n", + dev->stats.regs_writes); + g_string_append_printf(buf, "\tuar_writes : %" PRId64 "\n", + dev->stats.uar_writes); + g_string_append_printf(buf, "\tinterrupts : %" PRId64 "\n", + dev->stats.interrupts); + rdma_format_device_counters(&dev->rdma_dev_res, buf); } static void free_dev_ring(PCIDevice *pci_dev, PvrdmaRing *ring, @@ -699,7 +700,7 @@ static void pvrdma_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, pvrdma_dev_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - ir->print_statistics = pvrdma_print_statistics; + ir->format_statistics = pvrdma_format_statistics; } static const TypeInfo pvrdma_info = { diff --git a/include/hw/rdma/rdma.h b/include/hw/rdma/rdma.h index e77e43a170..80b2e531c4 100644 --- a/include/hw/rdma/rdma.h +++ b/include/hw/rdma/rdma.h @@ -31,7 +31,7 @@ typedef struct RdmaProvider RdmaProvider; struct RdmaProviderClass { InterfaceClass parent; - void (*print_statistics)(Monitor *mon, RdmaProvider *obj); + void (*format_statistics)(RdmaProvider *obj, GString *buf); }; #endif diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 9031cea881..9d221622d7 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -54,7 +54,6 @@ #include "qemu/error-report.h" #include "exec/ramlist.h" #include "hw/intc/intc.h" -#include "hw/rdma/rdma.h" #include "migration/snapshot.h" #include "migration/misc.h" @@ -850,32 +849,6 @@ void hmp_info_pic(Monitor *mon, const QDict *qdict) hmp_info_pic_foreach, mon); } -static int hmp_info_rdma_foreach(Object *obj, void *opaque) -{ - RdmaProvider *rdma; - RdmaProviderClass *k; - Monitor *mon = opaque; - - if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { - rdma = RDMA_PROVIDER(obj); - k = RDMA_PROVIDER_GET_CLASS(obj); - if (k->print_statistics) { - k->print_statistics(mon, rdma); - } else { - monitor_printf(mon, "RDMA statistics not available for %s.\n", - object_get_typename(obj)); - } - } - - return 0; -} - -void hmp_info_rdma(Monitor *mon, const QDict *qdict) -{ - object_child_foreach_recursive(object_get_root(), - hmp_info_rdma_foreach, mon); -} - void hmp_info_pci(Monitor *mon, const QDict *qdict) { PciInfoList *info_list, *info; diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 6122ad18b6..0a9ba7595c 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -40,6 +40,7 @@ #include "qapi/qmp/qerror.h" #include "hw/mem/memory-device.h" #include "hw/acpi/acpi_dev_interface.h" +#include "hw/rdma/rdma.h" NameInfo *qmp_query_name(Error **errp) { @@ -382,3 +383,34 @@ HumanReadableText *qmp_x_query_profile(Error **errp) return NULL; } #endif + +static int qmp_x_query_rdma_foreach(Object *obj, void *opaque) +{ + RdmaProvider *rdma; + RdmaProviderClass *k; + GString *buf = opaque; + + if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { + rdma = RDMA_PROVIDER(obj); + k = RDMA_PROVIDER_GET_CLASS(obj); + if (k->format_statistics) { + k->format_statistics(rdma, buf); + } else { + g_string_append_printf(buf, + "RDMA statistics not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +HumanReadableText *qmp_x_query_rdma(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), + qmp_x_query_rdma_foreach, buf); + + return human_readable_text_from_str(buf); +} diff --git a/qapi/machine.json b/qapi/machine.json index 15b6c98597..1b2748c77a 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1436,6 +1436,18 @@ { 'command': 'x-query-profile', 'returns': 'HumanReadableText' } +## +# @x-query-rdma: +# +# Query RDMA state +# +# Returns: RDMA state +# +# Since: 6.2 +## +{ 'command': 'x-query-rdma', + 'returns': 'HumanReadableText' } + ## # @x-query-roms: # From ca411b7c8a230af7e83bcebda88b265335f0e4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1122/1334] qapi: introduce x-query-ramblock QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info ramblock" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hmp-commands-info.hx | 2 +- include/exec/ramlist.h | 2 +- monitor/hmp-cmds.c | 6 ------ monitor/qmp-cmds.c | 8 ++++++++ qapi/machine.json | 12 ++++++++++++ softmmu/physmem.c | 19 +++++++++++-------- 6 files changed, 33 insertions(+), 16 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index d9af216473..c2d7275bf5 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -772,7 +772,7 @@ ERST .args_type = "", .params = "", .help = "Display system ramblock information", - .cmd = hmp_info_ramblock, + .cmd_info_hrt = qmp_x_query_ramblock, }, SRST diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h index ece6497ee2..2ad2a81acc 100644 --- a/include/exec/ramlist.h +++ b/include/exec/ramlist.h @@ -80,6 +80,6 @@ void ram_block_notify_add(void *host, size_t size, size_t max_size); void ram_block_notify_remove(void *host, size_t size, size_t max_size); void ram_block_notify_resize(void *host, size_t old_size, size_t new_size); -void ram_block_dump(Monitor *mon); +GString *ram_block_format(void); #endif /* RAMLIST_H */ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 9d221622d7..90f9a64573 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -52,7 +52,6 @@ #include "ui/console.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "exec/ramlist.h" #include "hw/intc/intc.h" #include "migration/snapshot.h" #include "migration/misc.h" @@ -2176,11 +2175,6 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) qapi_free_RockerOfDpaGroupList(list); } -void hmp_info_ramblock(Monitor *mon, const QDict *qdict) -{ - ram_block_dump(mon); -} - void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict) { Error *err = NULL; diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 0a9ba7595c..a9766fa38d 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -38,6 +38,7 @@ #include "qapi/qapi-commands-ui.h" #include "qapi/type-helpers.h" #include "qapi/qmp/qerror.h" +#include "exec/ramlist.h" #include "hw/mem/memory-device.h" #include "hw/acpi/acpi_dev_interface.h" #include "hw/rdma/rdma.h" @@ -414,3 +415,10 @@ HumanReadableText *qmp_x_query_rdma(Error **errp) return human_readable_text_from_str(buf); } + +HumanReadableText *qmp_x_query_ramblock(Error **errp) +{ + g_autoptr(GString) buf = ram_block_format(); + + return human_readable_text_from_str(buf); +} diff --git a/qapi/machine.json b/qapi/machine.json index 1b2748c77a..be81170c2b 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1436,6 +1436,18 @@ { 'command': 'x-query-profile', 'returns': 'HumanReadableText' } +## +# @x-query-ramblock: +# +# Query system ramblock information +# +# Returns: system ramblock information +# +# Since: 6.2 +## +{ 'command': 'x-query-ramblock', + 'returns': 'HumanReadableText' } + ## # @x-query-rdma: # diff --git a/softmmu/physmem.c b/softmmu/physmem.c index b9a8c1d1f4..314f8b439c 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1296,23 +1296,26 @@ void qemu_mutex_unlock_ramlist(void) qemu_mutex_unlock(&ram_list.mutex); } -void ram_block_dump(Monitor *mon) +GString *ram_block_format(void) { RAMBlock *block; char *psize; + GString *buf = g_string_new(""); RCU_READ_LOCK_GUARD(); - monitor_printf(mon, "%24s %8s %18s %18s %18s\n", - "Block Name", "PSize", "Offset", "Used", "Total"); + g_string_append_printf(buf, "%24s %8s %18s %18s %18s\n", + "Block Name", "PSize", "Offset", "Used", "Total"); RAMBLOCK_FOREACH(block) { psize = size_to_str(block->page_size); - monitor_printf(mon, "%24s %8s 0x%016" PRIx64 " 0x%016" PRIx64 - " 0x%016" PRIx64 "\n", block->idstr, psize, - (uint64_t)block->offset, - (uint64_t)block->used_length, - (uint64_t)block->max_length); + g_string_append_printf(buf, "%24s %8s 0x%016" PRIx64 " 0x%016" PRIx64 + " 0x%016" PRIx64 "\n", block->idstr, psize, + (uint64_t)block->offset, + (uint64_t)block->used_length, + (uint64_t)block->max_length); g_free(psize); } + + return buf; } #ifdef __linux__ From 91f2fa7045e3f6c298b14ea7c66c486c27fe3b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1123/1334] qapi: introduce x-query-irq QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info irq" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- hmp-commands-info.hx | 2 +- monitor/hmp-cmds.c | 38 -------------------------------------- monitor/qmp-cmds.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ qapi/machine.json | 12 ++++++++++++ 4 files changed, 57 insertions(+), 39 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index c2d7275bf5..407a1da800 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -159,7 +159,7 @@ ERST .args_type = "", .params = "", .help = "show the interrupts statistics (if available)", - .cmd = hmp_info_irq, + .cmd_info_hrt = qmp_x_query_irq, }, SRST diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 90f9a64573..8ef605e29a 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -784,44 +784,6 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev) } } -static int hmp_info_irq_foreach(Object *obj, void *opaque) -{ - InterruptStatsProvider *intc; - InterruptStatsProviderClass *k; - Monitor *mon = opaque; - - if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { - intc = INTERRUPT_STATS_PROVIDER(obj); - k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); - uint64_t *irq_counts; - unsigned int nb_irqs, i; - if (k->get_statistics && - k->get_statistics(intc, &irq_counts, &nb_irqs)) { - if (nb_irqs > 0) { - monitor_printf(mon, "IRQ statistics for %s:\n", - object_get_typename(obj)); - for (i = 0; i < nb_irqs; i++) { - if (irq_counts[i] > 0) { - monitor_printf(mon, "%2d: %" PRId64 "\n", i, - irq_counts[i]); - } - } - } - } else { - monitor_printf(mon, "IRQ statistics not available for %s.\n", - object_get_typename(obj)); - } - } - - return 0; -} - -void hmp_info_irq(Monitor *mon, const QDict *qdict) -{ - object_child_foreach_recursive(object_get_root(), - hmp_info_irq_foreach, mon); -} - static int hmp_info_pic_foreach(Object *obj, void *opaque) { InterruptStatsProvider *intc; diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index a9766fa38d..343353e27a 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -41,6 +41,7 @@ #include "exec/ramlist.h" #include "hw/mem/memory-device.h" #include "hw/acpi/acpi_dev_interface.h" +#include "hw/intc/intc.h" #include "hw/rdma/rdma.h" NameInfo *qmp_query_name(Error **errp) @@ -422,3 +423,46 @@ HumanReadableText *qmp_x_query_ramblock(Error **errp) return human_readable_text_from_str(buf); } + +static int qmp_x_query_irq_foreach(Object *obj, void *opaque) +{ + InterruptStatsProvider *intc; + InterruptStatsProviderClass *k; + GString *buf = opaque; + + if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { + intc = INTERRUPT_STATS_PROVIDER(obj); + k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); + uint64_t *irq_counts; + unsigned int nb_irqs, i; + if (k->get_statistics && + k->get_statistics(intc, &irq_counts, &nb_irqs)) { + if (nb_irqs > 0) { + g_string_append_printf(buf, "IRQ statistics for %s:\n", + object_get_typename(obj)); + for (i = 0; i < nb_irqs; i++) { + if (irq_counts[i] > 0) { + g_string_append_printf(buf, "%2d: %" PRId64 "\n", i, + irq_counts[i]); + } + } + } + } else { + g_string_append_printf(buf, + "IRQ statistics not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +HumanReadableText *qmp_x_query_irq(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), + qmp_x_query_irq_foreach, buf); + + return human_readable_text_from_str(buf); +} diff --git a/qapi/machine.json b/qapi/machine.json index be81170c2b..ca49358292 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1412,6 +1412,18 @@ '*threads': 'int', '*maxcpus': 'int' } } +## +# @x-query-irq: +# +# Query interrupt statistics +# +# Returns: interrupt statistics +# +# Since: 6.2 +## +{ 'command': 'x-query-irq', + 'returns': 'HumanReadableText' } + ## # @x-query-numa: # From 3a841ab53f165910224dc4bebabf1a8f1d04200c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1124/1334] qapi: introduce x-query-jit QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info jit" command. It is being added with an "x-" prefix because this QMP command is intended as an ad hoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- accel/tcg/cpu-exec.c | 37 ++++++++++++---- accel/tcg/hmp.c | 15 ++----- accel/tcg/translate-all.c | 80 ++++++++++++++++++---------------- include/exec/cpu-all.h | 4 +- include/tcg/tcg.h | 2 +- qapi/machine.json | 13 ++++++ tcg/tcg.c | 88 ++++++++++++++++++++------------------ tests/qtest/qmp-cmd-test.c | 2 + 8 files changed, 140 insertions(+), 101 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c9764c1325..4212645cb6 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -20,6 +20,9 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/qemu-print.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/type-helpers.h" #include "hw/core/tcg-cpu-ops.h" #include "trace.h" #include "disas/disas.h" @@ -38,6 +41,7 @@ #include "exec/cpu-all.h" #include "sysemu/cpu-timers.h" #include "sysemu/replay.h" +#include "sysemu/tcg.h" #include "exec/helper-proto.h" #include "tb-hash.h" #include "tb-context.h" @@ -1028,23 +1032,38 @@ void tcg_exec_unrealizefn(CPUState *cpu) #ifndef CONFIG_USER_ONLY -void dump_drift_info(void) +void dump_drift_info(GString *buf) { if (!icount_enabled()) { return; } - qemu_printf("Host - Guest clock %"PRIi64" ms\n", - (cpu_get_clock() - icount_get()) / SCALE_MS); + g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n", + (cpu_get_clock() - icount_get()) / SCALE_MS); if (icount_align_option) { - qemu_printf("Max guest delay %"PRIi64" ms\n", - -max_delay / SCALE_MS); - qemu_printf("Max guest advance %"PRIi64" ms\n", - max_advance / SCALE_MS); + g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n", + -max_delay / SCALE_MS); + g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n", + max_advance / SCALE_MS); } else { - qemu_printf("Max guest delay NA\n"); - qemu_printf("Max guest advance NA\n"); + g_string_append_printf(buf, "Max guest delay NA\n"); + g_string_append_printf(buf, "Max guest advance NA\n"); } } +HumanReadableText *qmp_x_query_jit(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + if (!tcg_enabled()) { + error_setg(errp, "JIT information is only available with accel=tcg"); + return NULL; + } + + dump_exec_info(buf); + dump_drift_info(buf); + + return human_readable_text_from_str(buf); +} + #endif /* !CONFIG_USER_ONLY */ diff --git a/accel/tcg/hmp.c b/accel/tcg/hmp.c index a6e72fdb3e..01c767a464 100644 --- a/accel/tcg/hmp.c +++ b/accel/tcg/hmp.c @@ -1,20 +1,11 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" #include "exec/exec-all.h" #include "monitor/monitor.h" #include "sysemu/tcg.h" -static void hmp_info_jit(Monitor *mon, const QDict *qdict) -{ - if (!tcg_enabled()) { - error_report("JIT information is only available with accel=tcg"); - return; - } - - dump_exec_info(); - dump_drift_info(); -} - static void hmp_info_opcount(Monitor *mon, const QDict *qdict) { dump_opcount_info(); @@ -22,7 +13,7 @@ static void hmp_info_opcount(Monitor *mon, const QDict *qdict) static void hmp_tcg_register(void) { - monitor_register_hmp("jit", true, hmp_info_jit); + monitor_register_hmp_info_hrt("jit", qmp_x_query_jit); monitor_register_hmp("opcount", true, hmp_info_opcount); } diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index fb9ebfad9e..8f17a91858 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1991,7 +1991,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) cpu_loop_exit_noexc(cpu); } -static void print_qht_statistics(struct qht_stats hst) +static void print_qht_statistics(struct qht_stats hst, GString *buf) { uint32_t hgram_opts; size_t hgram_bins; @@ -2000,9 +2000,11 @@ static void print_qht_statistics(struct qht_stats hst) if (!hst.head_buckets) { return; } - qemu_printf("TB hash buckets %zu/%zu (%0.2f%% head buckets used)\n", - hst.used_head_buckets, hst.head_buckets, - (double)hst.used_head_buckets / hst.head_buckets * 100); + g_string_append_printf(buf, "TB hash buckets %zu/%zu " + "(%0.2f%% head buckets used)\n", + hst.used_head_buckets, hst.head_buckets, + (double)hst.used_head_buckets / + hst.head_buckets * 100); hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT; @@ -2010,8 +2012,9 @@ static void print_qht_statistics(struct qht_stats hst) hgram_opts |= QDIST_PR_NODECIMAL; } hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); - qemu_printf("TB hash occupancy %0.2f%% avg chain occ. Histogram: %s\n", - qdist_avg(&hst.occupancy) * 100, hgram); + g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. " + "Histogram: %s\n", + qdist_avg(&hst.occupancy) * 100, hgram); g_free(hgram); hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; @@ -2023,8 +2026,9 @@ static void print_qht_statistics(struct qht_stats hst) hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; } hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); - qemu_printf("TB hash avg chain %0.3f buckets. Histogram: %s\n", - qdist_avg(&hst.chain), hgram); + g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " + "Histogram: %s\n", + qdist_avg(&hst.chain), hgram); g_free(hgram); } @@ -2061,7 +2065,7 @@ static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) return false; } -void dump_exec_info(void) +void dump_exec_info(GString *buf) { struct tb_tree_stats tst = {}; struct qht_stats hst; @@ -2070,44 +2074,48 @@ void dump_exec_info(void) tcg_tb_foreach(tb_tree_stats_iter, &tst); nb_tbs = tst.nb_tbs; /* XXX: avoid using doubles ? */ - qemu_printf("Translation buffer state:\n"); + g_string_append_printf(buf, "Translation buffer state:\n"); /* * Report total code size including the padding and TB structs; * otherwise users might think "-accel tcg,tb-size" is not honoured. * For avg host size we use the precise numbers from tb_tree_stats though. */ - qemu_printf("gen code size %zu/%zu\n", - tcg_code_size(), tcg_code_capacity()); - qemu_printf("TB count %zu\n", nb_tbs); - qemu_printf("TB avg target size %zu max=%zu bytes\n", - nb_tbs ? tst.target_size / nb_tbs : 0, - tst.max_target_size); - qemu_printf("TB avg host size %zu bytes (expansion ratio: %0.1f)\n", - nb_tbs ? tst.host_size / nb_tbs : 0, - tst.target_size ? (double)tst.host_size / tst.target_size : 0); - qemu_printf("cross page TB count %zu (%zu%%)\n", tst.cross_page, - nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); - qemu_printf("direct jump count %zu (%zu%%) (2 jumps=%zu %zu%%)\n", - tst.direct_jmp_count, - nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, - tst.direct_jmp2_count, - nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); + g_string_append_printf(buf, "gen code size %zu/%zu\n", + tcg_code_size(), tcg_code_capacity()); + g_string_append_printf(buf, "TB count %zu\n", nb_tbs); + g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n", + nb_tbs ? tst.target_size / nb_tbs : 0, + tst.max_target_size); + g_string_append_printf(buf, "TB avg host size %zu bytes " + "(expansion ratio: %0.1f)\n", + nb_tbs ? tst.host_size / nb_tbs : 0, + tst.target_size ? + (double)tst.host_size / tst.target_size : 0); + g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n", + tst.cross_page, + nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); + g_string_append_printf(buf, "direct jump count %zu (%zu%%) " + "(2 jumps=%zu %zu%%)\n", + tst.direct_jmp_count, + nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, + tst.direct_jmp2_count, + nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); qht_statistics_init(&tb_ctx.htable, &hst); - print_qht_statistics(hst); + print_qht_statistics(hst, buf); qht_statistics_destroy(&hst); - qemu_printf("\nStatistics:\n"); - qemu_printf("TB flush count %u\n", - qatomic_read(&tb_ctx.tb_flush_count)); - qemu_printf("TB invalidate count %u\n", - qatomic_read(&tb_ctx.tb_phys_invalidate_count)); + g_string_append_printf(buf, "\nStatistics:\n"); + g_string_append_printf(buf, "TB flush count %u\n", + qatomic_read(&tb_ctx.tb_flush_count)); + g_string_append_printf(buf, "TB invalidate count %u\n", + qatomic_read(&tb_ctx.tb_phys_invalidate_count)); tlb_flush_counts(&flush_full, &flush_part, &flush_elide); - qemu_printf("TLB full flushes %zu\n", flush_full); - qemu_printf("TLB partial flushes %zu\n", flush_part); - qemu_printf("TLB elided flushes %zu\n", flush_elide); - tcg_dump_info(); + g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); + g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); + g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); + tcg_dump_info(buf); } void dump_opcount_info(void) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 32cfb634c6..d92f6fa7a9 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -429,9 +429,9 @@ static inline bool tlb_hit(target_ulong tlb_addr, target_ulong addr) #ifdef CONFIG_TCG /* accel/tcg/cpu-exec.c */ -void dump_drift_info(void); +void dump_drift_info(GString *buf); /* accel/tcg/translate-all.c */ -void dump_exec_info(void); +void dump_exec_info(GString *buf); void dump_opcount_info(void); #endif /* CONFIG_TCG */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 7069a401f1..7c8019a9b2 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -937,7 +937,7 @@ int tcg_check_temp_count(void); #endif int64_t tcg_cpu_exec_time(void); -void tcg_dump_info(void); +void tcg_dump_info(GString *buf); void tcg_dump_op_count(void); #define TCG_CT_CONST 1 /* any constant of register size */ diff --git a/qapi/machine.json b/qapi/machine.json index ca49358292..422a44661f 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1424,6 +1424,19 @@ { 'command': 'x-query-irq', 'returns': 'HumanReadableText' } +## +# @x-query-jit: +# +# Query TCG compiler statistics +# +# Returns: TCG compiler statistics +# +# Since: 6.2 +## +{ 'command': 'x-query-jit', + 'returns': 'HumanReadableText', + 'if': 'CONFIG_TCG' } + ## # @x-query-numa: # diff --git a/tcg/tcg.c b/tcg/tcg.c index 6332cdceca..f9ede8e62e 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -4383,7 +4383,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) } #ifdef CONFIG_PROFILER -void tcg_dump_info(void) +void tcg_dump_info(GString *buf) { TCGProfile prof = {}; const TCGProfile *s; @@ -4397,53 +4397,59 @@ void tcg_dump_info(void) tb_div_count = tb_count ? tb_count : 1; tot = s->interm_time + s->code_time; - qemu_printf("JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n", - tot, tot / 2.4e9); - qemu_printf("translated TBs %" PRId64 " (aborted=%" PRId64 - " %0.1f%%)\n", - tb_count, s->tb_count1 - tb_count, - (double)(s->tb_count1 - s->tb_count) - / (s->tb_count1 ? s->tb_count1 : 1) * 100.0); - qemu_printf("avg ops/TB %0.1f max=%d\n", - (double)s->op_count / tb_div_count, s->op_count_max); - qemu_printf("deleted ops/TB %0.2f\n", - (double)s->del_op_count / tb_div_count); - qemu_printf("avg temps/TB %0.2f max=%d\n", - (double)s->temp_count / tb_div_count, s->temp_count_max); - qemu_printf("avg host code/TB %0.1f\n", - (double)s->code_out_len / tb_div_count); - qemu_printf("avg search data/TB %0.1f\n", - (double)s->search_out_len / tb_div_count); + g_string_append_printf(buf, "JIT cycles %" PRId64 + " (%0.3f s at 2.4 GHz)\n", + tot, tot / 2.4e9); + g_string_append_printf(buf, "translated TBs %" PRId64 + " (aborted=%" PRId64 " %0.1f%%)\n", + tb_count, s->tb_count1 - tb_count, + (double)(s->tb_count1 - s->tb_count) + / (s->tb_count1 ? s->tb_count1 : 1) * 100.0); + g_string_append_printf(buf, "avg ops/TB %0.1f max=%d\n", + (double)s->op_count / tb_div_count, s->op_count_max); + g_string_append_printf(buf, "deleted ops/TB %0.2f\n", + (double)s->del_op_count / tb_div_count); + g_string_append_printf(buf, "avg temps/TB %0.2f max=%d\n", + (double)s->temp_count / tb_div_count, + s->temp_count_max); + g_string_append_printf(buf, "avg host code/TB %0.1f\n", + (double)s->code_out_len / tb_div_count); + g_string_append_printf(buf, "avg search data/TB %0.1f\n", + (double)s->search_out_len / tb_div_count); - qemu_printf("cycles/op %0.1f\n", - s->op_count ? (double)tot / s->op_count : 0); - qemu_printf("cycles/in byte %0.1f\n", - s->code_in_len ? (double)tot / s->code_in_len : 0); - qemu_printf("cycles/out byte %0.1f\n", - s->code_out_len ? (double)tot / s->code_out_len : 0); - qemu_printf("cycles/search byte %0.1f\n", - s->search_out_len ? (double)tot / s->search_out_len : 0); + g_string_append_printf(buf, "cycles/op %0.1f\n", + s->op_count ? (double)tot / s->op_count : 0); + g_string_append_printf(buf, "cycles/in byte %0.1f\n", + s->code_in_len ? (double)tot / s->code_in_len : 0); + g_string_append_printf(buf, "cycles/out byte %0.1f\n", + s->code_out_len ? (double)tot / s->code_out_len : 0); + g_string_append_printf(buf, "cycles/search byte %0.1f\n", + s->search_out_len ? + (double)tot / s->search_out_len : 0); if (tot == 0) { tot = 1; } - qemu_printf(" gen_interm time %0.1f%%\n", - (double)s->interm_time / tot * 100.0); - qemu_printf(" gen_code time %0.1f%%\n", - (double)s->code_time / tot * 100.0); - qemu_printf("optim./code time %0.1f%%\n", - (double)s->opt_time / (s->code_time ? s->code_time : 1) - * 100.0); - qemu_printf("liveness/code time %0.1f%%\n", - (double)s->la_time / (s->code_time ? s->code_time : 1) * 100.0); - qemu_printf("cpu_restore count %" PRId64 "\n", - s->restore_count); - qemu_printf(" avg cycles %0.1f\n", - s->restore_count ? (double)s->restore_time / s->restore_count : 0); + g_string_append_printf(buf, " gen_interm time %0.1f%%\n", + (double)s->interm_time / tot * 100.0); + g_string_append_printf(buf, " gen_code time %0.1f%%\n", + (double)s->code_time / tot * 100.0); + g_string_append_printf(buf, "optim./code time %0.1f%%\n", + (double)s->opt_time / (s->code_time ? + s->code_time : 1) + * 100.0); + g_string_append_printf(buf, "liveness/code time %0.1f%%\n", + (double)s->la_time / (s->code_time ? + s->code_time : 1) * 100.0); + g_string_append_printf(buf, "cpu_restore count %" PRId64 "\n", + s->restore_count); + g_string_append_printf(buf, " avg cycles %0.1f\n", + s->restore_count ? + (double)s->restore_time / s->restore_count : 0); } #else -void tcg_dump_info(void) +void tcg_dump_info(GString *buf) { - qemu_printf("[TCG profiler not compiled]\n"); + g_string_append_printf(buf, "[TCG profiler not compiled]\n"); } #endif diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 0d52ea6c4b..ea24fde1a3 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -51,6 +51,8 @@ static int query_error_class(const char *cmd) #endif /* Only valid with a USB bus added */ { "x-query-usb", ERROR_CLASS_GENERIC_ERROR }, + /* Only valid with accel=tcg */ + { "x-query-jit", ERROR_CLASS_GENERIC_ERROR }, { NULL, -1 } }; int i; From b6a7f3e0d28248861cf46f59521129b179e8748d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 8 Sep 2021 10:35:43 +0100 Subject: [PATCH 1125/1334] qapi: introduce x-query-opcount QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info opcount" command. It is being added with an "x-" prefix because this QMP command is intended as an ad hoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- accel/tcg/cpu-exec.c | 14 ++++++++++++++ accel/tcg/hmp.c | 7 +------ accel/tcg/translate-all.c | 4 ++-- include/exec/cpu-all.h | 2 +- include/tcg/tcg.h | 2 +- qapi/machine.json | 13 +++++++++++++ tcg/tcg.c | 10 +++++----- tests/qtest/qmp-cmd-test.c | 1 + 8 files changed, 38 insertions(+), 15 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 4212645cb6..c0620b4cc7 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -1066,4 +1066,18 @@ HumanReadableText *qmp_x_query_jit(Error **errp) return human_readable_text_from_str(buf); } +HumanReadableText *qmp_x_query_opcount(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + if (!tcg_enabled()) { + error_setg(errp, "Opcode count information is only available with accel=tcg"); + return NULL; + } + + dump_opcount_info(buf); + + return human_readable_text_from_str(buf); +} + #endif /* !CONFIG_USER_ONLY */ diff --git a/accel/tcg/hmp.c b/accel/tcg/hmp.c index 01c767a464..d2ea352655 100644 --- a/accel/tcg/hmp.c +++ b/accel/tcg/hmp.c @@ -6,15 +6,10 @@ #include "monitor/monitor.h" #include "sysemu/tcg.h" -static void hmp_info_opcount(Monitor *mon, const QDict *qdict) -{ - dump_opcount_info(); -} - static void hmp_tcg_register(void) { monitor_register_hmp_info_hrt("jit", qmp_x_query_jit); - monitor_register_hmp("opcount", true, hmp_info_opcount); + monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount); } type_init(hmp_tcg_register); diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 8f17a91858..bd0bb81d08 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -2118,9 +2118,9 @@ void dump_exec_info(GString *buf) tcg_dump_info(buf); } -void dump_opcount_info(void) +void dump_opcount_info(GString *buf) { - tcg_dump_op_count(); + tcg_dump_op_count(buf); } #else /* CONFIG_USER_ONLY */ diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index d92f6fa7a9..3c8e24292b 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -432,7 +432,7 @@ static inline bool tlb_hit(target_ulong tlb_addr, target_ulong addr) void dump_drift_info(GString *buf); /* accel/tcg/translate-all.c */ void dump_exec_info(GString *buf); -void dump_opcount_info(void); +void dump_opcount_info(GString *buf); #endif /* CONFIG_TCG */ #endif /* !CONFIG_USER_ONLY */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 7c8019a9b2..42f5b500ed 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -938,7 +938,7 @@ int tcg_check_temp_count(void); int64_t tcg_cpu_exec_time(void); void tcg_dump_info(GString *buf); -void tcg_dump_op_count(void); +void tcg_dump_op_count(GString *buf); #define TCG_CT_CONST 1 /* any constant of register size */ diff --git a/qapi/machine.json b/qapi/machine.json index 422a44661f..17794ef681 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1449,6 +1449,19 @@ { 'command': 'x-query-numa', 'returns': 'HumanReadableText' } +## +# @x-query-opcount: +# +# Query TCG opcode counters +# +# Returns: TCG opcode counters +# +# Since: 6.2 +## +{ 'command': 'x-query-opcount', + 'returns': 'HumanReadableText', + 'if': 'CONFIG_TCG' } + ## # @x-query-profile: # diff --git a/tcg/tcg.c b/tcg/tcg.c index f9ede8e62e..57f17a4649 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -4116,15 +4116,15 @@ static void tcg_profile_snapshot_table(TCGProfile *prof) tcg_profile_snapshot(prof, false, true); } -void tcg_dump_op_count(void) +void tcg_dump_op_count(GString *buf) { TCGProfile prof = {}; int i; tcg_profile_snapshot_table(&prof); for (i = 0; i < NB_OPS; i++) { - qemu_printf("%s %" PRId64 "\n", tcg_op_defs[i].name, - prof.table_op_count[i]); + g_string_append_printf(buf, "%s %" PRId64 "\n", tcg_op_defs[i].name, + prof.table_op_count[i]); } } @@ -4143,9 +4143,9 @@ int64_t tcg_cpu_exec_time(void) return ret; } #else -void tcg_dump_op_count(void) +void tcg_dump_op_count(GString *buf) { - qemu_printf("[TCG profiler not compiled]\n"); + g_string_append_printf(buf, "[TCG profiler not compiled]\n"); } int64_t tcg_cpu_exec_time(void) diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index ea24fde1a3..7f103ea3fd 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -53,6 +53,7 @@ static int query_error_class(const char *cmd) { "x-query-usb", ERROR_CLASS_GENERIC_ERROR }, /* Only valid with accel=tcg */ { "x-query-jit", ERROR_CLASS_GENERIC_ERROR }, + { "x-query-opcount", ERROR_CLASS_GENERIC_ERROR }, { NULL, -1 } }; int i; From 2863bd565c89c4b03cb935eae03b7de57fa602b7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sat, 30 Oct 2021 08:21:06 +0200 Subject: [PATCH 1126/1334] MAINTAINERS: Add myself as a reviewer for SDL audio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've got some experience with the SDL library, so I can help reviewing patches here. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211030062106.46024-1-huth@tuxfamily.org> Signed-off-by: Gerd Hoffmann --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 310a9512ea..99618e6d99 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2315,6 +2315,7 @@ F: audio/paaudio.c SDL Audio backend M: Gerd Hoffmann +R: Thomas Huth S: Odd Fixes F: audio/sdlaudio.c From 584ab347ce9e65617f5981d209756db83a824813 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Sat, 4 Sep 2021 15:13:46 +0200 Subject: [PATCH 1127/1334] MAINTAINERS: add myself as partial audio reviewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Volunteering as reviewer for some of the audio backends; namely ALSA, CoreAudio and JACK. Signed-off-by: Christian Schoenebeck Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Gerd Hoffmann --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 99618e6d99..9ddba68701 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2285,11 +2285,13 @@ F: qapi/audio.json ALSA Audio backend M: Gerd Hoffmann +R: Christian Schoenebeck S: Odd Fixes F: audio/alsaaudio.c Core Audio framework backend M: Gerd Hoffmann +R: Christian Schoenebeck S: Odd Fixes F: audio/coreaudio.c @@ -2300,6 +2302,7 @@ F: audio/dsound* JACK Audio Connection Kit backend M: Gerd Hoffmann +R: Christian Schoenebeck S: Odd Fixes F: audio/jackaudio.c From f5918a992839454c6e87234760bdf7aee804aa33 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 14 Oct 2021 21:36:17 +0200 Subject: [PATCH 1128/1334] microvm: add device tree support. Allows edk2 detect virtio-mmio devices and pcie ecam. See comment in hw/i386/microvm-dt.c for more details. Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Message-Id: <20211014193617.2475578-1-kraxel@redhat.com> --- .gitlab-ci.d/buildtest.yml | 1 - configs/targets/i386-softmmu.mak | 1 + configs/targets/x86_64-softmmu.mak | 1 + hw/i386/meson.build | 2 +- hw/i386/microvm-dt.c | 341 +++++++++++++++++++++++++++++ hw/i386/microvm-dt.h | 8 + hw/i386/microvm.c | 2 + include/hw/i386/microvm.h | 4 + 8 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 hw/i386/microvm-dt.c create mode 100644 hw/i386/microvm-dt.h diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 5c378e35f9..6c1301e912 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -575,7 +575,6 @@ build-without-default-features: CONFIGURE_ARGS: --without-default-features --disable-capstone - --disable-fdt --disable-pie --disable-qom-cast-debug --disable-slirp diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index 5babf71895..6b3c99fc86 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=i386 TARGET_SUPPORTS_MTTCG=y +TARGET_NEED_FDT=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index 75e42bc840..197817c943 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_SUPPORTS_MTTCG=y +TARGET_NEED_FDT=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/hw/i386/meson.build b/hw/i386/meson.build index c502965219..213e2e82b3 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -11,7 +11,7 @@ i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), if_false: files('x86-iommu-stub.c')) i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) -i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c')) +i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) diff --git a/hw/i386/microvm-dt.c b/hw/i386/microvm-dt.c new file mode 100644 index 0000000000..875ba91963 --- /dev/null +++ b/hw/i386/microvm-dt.c @@ -0,0 +1,341 @@ +/* + * microvm device tree support + * + * This generates an device tree for microvm and exports it via fw_cfg + * as "etc/fdt" to the firmware (edk2 specifically). + * + * The use case is to allow edk2 find the pcie ecam and the virtio + * devices, without adding an ACPI parser, reusing the fdt parser + * which is needed anyway for the arm platform. + * + * Note 1: The device tree is incomplete. CPUs and memory is missing + * for example, those can be detected using other fw_cfg files. + * Also pci ecam irq routing is not there, edk2 doesn't use + * interrupts. + * + * Note 2: This is for firmware only. OSes should use the more + * complete ACPI tables for hardware discovery. + * + * ---------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "sysemu/device_tree.h" +#include "hw/char/serial.h" +#include "hw/i386/fw_cfg.h" +#include "hw/rtc/mc146818rtc.h" +#include "hw/sysbus.h" +#include "hw/virtio/virtio-mmio.h" +#include "hw/usb/xhci.h" + +#include "microvm-dt.h" + +static bool debug; + +static void dt_add_microvm_irq(MicrovmMachineState *mms, + const char *nodename, uint32_t irq) +{ + int index = 0; + + if (irq >= IO_APIC_SECONDARY_IRQBASE) { + irq -= IO_APIC_SECONDARY_IRQBASE; + index++; + } + + qemu_fdt_setprop_cell(mms->fdt, nodename, "interrupt-parent", + mms->ioapic_phandle[index]); + qemu_fdt_setprop_cells(mms->fdt, nodename, "interrupts", irq, 0); +} + +static void dt_add_virtio(MicrovmMachineState *mms, VirtIOMMIOProxy *mmio) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(mmio); + VirtioBusState *mmio_virtio_bus = &mmio->bus; + BusState *mmio_bus = &mmio_virtio_bus->parent_obj; + char *nodename; + + if (QTAILQ_EMPTY(&mmio_bus->children)) { + return; + } + + hwaddr base = dev->mmio[0].addr; + hwaddr size = 512; + unsigned index = (base - VIRTIO_MMIO_BASE) / size; + uint32_t irq = mms->virtio_irq_base + index; + + nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base); + qemu_fdt_add_subnode(mms->fdt, nodename); + qemu_fdt_setprop_string(mms->fdt, nodename, "compatible", "virtio,mmio"); + qemu_fdt_setprop_sized_cells(mms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop(mms->fdt, nodename, "dma-coherent", NULL, 0); + dt_add_microvm_irq(mms, nodename, irq); + g_free(nodename); +} + +static void dt_add_xhci(MicrovmMachineState *mms) +{ + const char compat[] = "generic-xhci"; + uint32_t irq = MICROVM_XHCI_IRQ; + hwaddr base = MICROVM_XHCI_BASE; + hwaddr size = XHCI_LEN_REGS; + char *nodename; + + nodename = g_strdup_printf("/usb@%" PRIx64, base); + qemu_fdt_add_subnode(mms->fdt, nodename); + qemu_fdt_setprop(mms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(mms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop(mms->fdt, nodename, "dma-coherent", NULL, 0); + dt_add_microvm_irq(mms, nodename, irq); + g_free(nodename); +} + +static void dt_add_pcie(MicrovmMachineState *mms) +{ + hwaddr base = PCIE_MMIO_BASE; + int nr_pcie_buses; + char *nodename; + + nodename = g_strdup_printf("/pcie@%" PRIx64, base); + qemu_fdt_add_subnode(mms->fdt, nodename); + qemu_fdt_setprop_string(mms->fdt, nodename, + "compatible", "pci-host-ecam-generic"); + qemu_fdt_setprop_string(mms->fdt, nodename, "device_type", "pci"); + qemu_fdt_setprop_cell(mms->fdt, nodename, "#address-cells", 3); + qemu_fdt_setprop_cell(mms->fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_cell(mms->fdt, nodename, "linux,pci-domain", 0); + qemu_fdt_setprop(mms->fdt, nodename, "dma-coherent", NULL, 0); + + qemu_fdt_setprop_sized_cells(mms->fdt, nodename, "reg", + 2, PCIE_ECAM_BASE, 2, PCIE_ECAM_SIZE); + if (mms->gpex.mmio64.size) { + qemu_fdt_setprop_sized_cells(mms->fdt, nodename, "ranges", + + 1, FDT_PCI_RANGE_MMIO, + 2, mms->gpex.mmio32.base, + 2, mms->gpex.mmio32.base, + 2, mms->gpex.mmio32.size, + + 1, FDT_PCI_RANGE_MMIO_64BIT, + 2, mms->gpex.mmio64.base, + 2, mms->gpex.mmio64.base, + 2, mms->gpex.mmio64.size); + } else { + qemu_fdt_setprop_sized_cells(mms->fdt, nodename, "ranges", + + 1, FDT_PCI_RANGE_MMIO, + 2, mms->gpex.mmio32.base, + 2, mms->gpex.mmio32.base, + 2, mms->gpex.mmio32.size); + } + + nr_pcie_buses = PCIE_ECAM_SIZE / PCIE_MMCFG_SIZE_MIN; + qemu_fdt_setprop_cells(mms->fdt, nodename, "bus-range", 0, + nr_pcie_buses - 1); +} + +static void dt_add_ioapic(MicrovmMachineState *mms, SysBusDevice *dev) +{ + hwaddr base = dev->mmio[0].addr; + char *nodename; + uint32_t ph; + int index; + + switch (base) { + case IO_APIC_DEFAULT_ADDRESS: + index = 0; + break; + case IO_APIC_SECONDARY_ADDRESS: + index = 1; + break; + default: + fprintf(stderr, "unknown ioapic @ %" PRIx64 "\n", base); + return; + } + + nodename = g_strdup_printf("/ioapic%d@%" PRIx64, index + 1, base); + qemu_fdt_add_subnode(mms->fdt, nodename); + qemu_fdt_setprop_string(mms->fdt, nodename, + "compatible", "intel,ce4100-ioapic"); + qemu_fdt_setprop(mms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(mms->fdt, nodename, "#interrupt-cells", 0x2); + qemu_fdt_setprop_cell(mms->fdt, nodename, "#address-cells", 0x2); + qemu_fdt_setprop_sized_cells(mms->fdt, nodename, "reg", + 2, base, 2, 0x1000); + + ph = qemu_fdt_alloc_phandle(mms->fdt); + qemu_fdt_setprop_cell(mms->fdt, nodename, "phandle", ph); + qemu_fdt_setprop_cell(mms->fdt, nodename, "linux,phandle", ph); + mms->ioapic_phandle[index] = ph; + + g_free(nodename); +} + +static void dt_add_isa_serial(MicrovmMachineState *mms, ISADevice *dev) +{ + const char compat[] = "ns16550"; + uint32_t irq = object_property_get_int(OBJECT(dev), "irq", NULL); + hwaddr base = object_property_get_int(OBJECT(dev), "iobase", NULL); + hwaddr size = 8; + char *nodename; + + nodename = g_strdup_printf("/serial@%" PRIx64, base); + qemu_fdt_add_subnode(mms->fdt, nodename); + qemu_fdt_setprop(mms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(mms->fdt, nodename, "reg", 2, base, 2, size); + dt_add_microvm_irq(mms, nodename, irq); + + if (base == 0x3f8 /* com1 */) { + qemu_fdt_setprop_string(mms->fdt, "/chosen", "stdout-path", nodename); + } + + g_free(nodename); +} + +static void dt_add_isa_rtc(MicrovmMachineState *mms, ISADevice *dev) +{ + const char compat[] = "motorola,mc146818"; + uint32_t irq = RTC_ISA_IRQ; + hwaddr base = RTC_ISA_BASE; + hwaddr size = 8; + char *nodename; + + nodename = g_strdup_printf("/rtc@%" PRIx64, base); + qemu_fdt_add_subnode(mms->fdt, nodename); + qemu_fdt_setprop(mms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(mms->fdt, nodename, "reg", 2, base, 2, size); + dt_add_microvm_irq(mms, nodename, irq); + g_free(nodename); +} + +static void dt_setup_isa_bus(MicrovmMachineState *mms, DeviceState *bridge) +{ + BusState *bus = qdev_get_child_bus(bridge, "isa.0"); + BusChild *kid; + Object *obj; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + + /* serial */ + obj = object_dynamic_cast(OBJECT(dev), TYPE_ISA_SERIAL); + if (obj) { + dt_add_isa_serial(mms, ISA_DEVICE(obj)); + continue; + } + + /* rtc */ + obj = object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC); + if (obj) { + dt_add_isa_rtc(mms, ISA_DEVICE(obj)); + continue; + } + + if (debug) { + fprintf(stderr, "%s: unhandled: %s\n", __func__, + object_get_typename(OBJECT(dev))); + } + } +} + +static void dt_setup_sys_bus(MicrovmMachineState *mms) +{ + BusState *bus; + BusChild *kid; + Object *obj; + + /* sysbus devices */ + bus = sysbus_get_default(); + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + + /* ioapic */ + obj = object_dynamic_cast(OBJECT(dev), TYPE_IOAPIC); + if (obj) { + dt_add_ioapic(mms, SYS_BUS_DEVICE(obj)); + continue; + } + } + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + + /* virtio */ + obj = object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO); + if (obj) { + dt_add_virtio(mms, VIRTIO_MMIO(obj)); + continue; + } + + /* xhci */ + obj = object_dynamic_cast(OBJECT(dev), TYPE_XHCI_SYSBUS); + if (obj) { + dt_add_xhci(mms); + continue; + } + + /* pcie */ + obj = object_dynamic_cast(OBJECT(dev), TYPE_GPEX_HOST); + if (obj) { + dt_add_pcie(mms); + continue; + } + + /* isa */ + obj = object_dynamic_cast(OBJECT(dev), "isabus-bridge"); + if (obj) { + dt_setup_isa_bus(mms, DEVICE(obj)); + continue; + } + + if (debug) { + obj = object_dynamic_cast(OBJECT(dev), TYPE_IOAPIC); + if (obj) { + /* ioapic already added in first pass */ + continue; + } + fprintf(stderr, "%s: unhandled: %s\n", __func__, + object_get_typename(OBJECT(dev))); + } + } +} + +void dt_setup_microvm(MicrovmMachineState *mms) +{ + X86MachineState *x86ms = X86_MACHINE(mms); + int size = 0; + + mms->fdt = create_device_tree(&size); + + /* root node */ + qemu_fdt_setprop_string(mms->fdt, "/", "compatible", "linux,microvm"); + qemu_fdt_setprop_cell(mms->fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(mms->fdt, "/", "#size-cells", 0x2); + + qemu_fdt_add_subnode(mms->fdt, "/chosen"); + dt_setup_sys_bus(mms); + + /* add to fw_cfg */ + fprintf(stderr, "%s: add etc/fdt to fw_cfg\n", __func__); + fw_cfg_add_file(x86ms->fw_cfg, "etc/fdt", mms->fdt, size); + + if (debug) { + fprintf(stderr, "%s: writing microvm.fdt\n", __func__); + g_file_set_contents("microvm.fdt", mms->fdt, size, NULL); + int ret = system("dtc -I dtb -O dts microvm.fdt"); + if (ret != 0) { + fprintf(stderr, "%s: oops, dtc not installed?\n", __func__); + } + } +} diff --git a/hw/i386/microvm-dt.h b/hw/i386/microvm-dt.h new file mode 100644 index 0000000000..77c79cbdd9 --- /dev/null +++ b/hw/i386/microvm-dt.h @@ -0,0 +1,8 @@ +#ifndef HW_I386_MICROVM_DT_H +#define HW_I386_MICROVM_DT_H + +#include "hw/i386/microvm.h" + +void dt_setup_microvm(MicrovmMachineState *mms); + +#endif diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index f257ec5a0b..8d6ebea341 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -28,6 +28,7 @@ #include "sysemu/reset.h" #include "sysemu/runstate.h" #include "acpi-microvm.h" +#include "microvm-dt.h" #include "hw/loader.h" #include "hw/irq.h" @@ -626,6 +627,7 @@ static void microvm_machine_done(Notifier *notifier, void *data) machine_done); acpi_setup_microvm(mms); + dt_setup_microvm(mms); } static void microvm_powerdown_req(Notifier *notifier, void *data) diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index f25f837441..4d9c732d4b 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -104,6 +104,10 @@ struct MicrovmMachineState { Notifier machine_done; Notifier powerdown_req; struct GPEXConfig gpex; + + /* device tree */ + void *fdt; + uint32_t ioapic_phandle[2]; }; #define TYPE_MICROVM_MACHINE MACHINE_TYPE_NAME("microvm") From 760deab30e41a98bb09f29944e977a4cda55f3c7 Mon Sep 17 00:00:00 2001 From: Nikola Pavlica Date: Sun, 24 Oct 2021 16:31:10 +0200 Subject: [PATCH 1129/1334] ui/gtk: Update the refresh rate for gl-area too This is a bugfix that stretches all the way back to January 2020, where I initially introduced this problem and potential solutions. A quick recap of the issue: QEMU did not sync up with the monitors refresh rate causing the VM to render frames that were NOT displayed to the user. That "fix" allowed QEMU to obtain the screen refreshrate information from the system using GDK API's and was for GTK only. Well, I'm back with the same issue again. But this time on Wayland. And I did NOT realize there was YET another screen refresh rate function, this time for Wayland specifically. Thankfully the fix was simple and without much hassle. Thanks, Nikola PS: It seems that my patch has gone missing from the mailing list, hence I'm sending it again. Sorry for any inconveniences. Signed-off-by: Nikola Pavlica Message-Id: <20211024143110.704296-1-pavlica.nikola@gmail.com> Signed-off-by: Gerd Hoffmann --- ui/gtk-gl-area.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index b23523748e..afcb29f658 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -112,6 +112,9 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + vc->gfx.dcl.update_interval = gd_monitor_update_interval( + vc->window ? vc->window : vc->gfx.drawing_area); + if (!vc->gfx.gls) { if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; From 55f4b767f64ee0fec397c7ceebfea1473b725963 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Fri, 24 Sep 2021 15:51:05 -0700 Subject: [PATCH 1130/1334] ui/gtk: skip any extra draw of same guest scanout blob res Any extra draw call for the same blob resource representing guest scanout before the previous drawing is not finished can break synchronous draw sequence. To prevent this, drawing is now done only once for each draw submission (when draw_submitted == true). v2: - removed mutex - updated commit msg Cc: Gerd Hoffmann Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20210924225105.24930-1-dongwon.kim@intel.com> Signed-off-by: Gerd Hoffmann --- hw/display/virtio-gpu-udmabuf.c | 2 +- include/ui/console.h | 1 + ui/gtk-egl.c | 40 ++++++++++++++++++--------- ui/gtk-gl-area.c | 49 ++++++++++++++++++++------------- 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index c6f7f58784..60ea7f8f49 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -186,7 +186,7 @@ static VGPUDMABuf dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format); dmabuf->buf.fd = res->dmabuf_fd; dmabuf->buf.allow_fences = true; - + dmabuf->buf.draw_submitted = false; dmabuf->scanout_id = scanout_id; QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next); diff --git a/include/ui/console.h b/include/ui/console.h index 244664d727..b6bedc5f41 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -171,6 +171,7 @@ typedef struct QemuDmaBuf { void *sync; int fence_fd; bool allow_fences; + bool draw_submitted; } QemuDmaBuf; typedef struct DisplayState DisplayState; diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 72ce5e1f8f..e912b20075 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -63,6 +63,9 @@ void gd_egl_init(VirtualConsole *vc) void gd_egl_draw(VirtualConsole *vc) { GdkWindow *window; +#ifdef CONFIG_GBM + QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; +#endif int ww, wh; if (!vc->gfx.gls) { @@ -74,10 +77,31 @@ void gd_egl_draw(VirtualConsole *vc) wh = gdk_window_get_height(window); if (vc->gfx.scanout_mode) { +#ifdef CONFIG_GBM + if (dmabuf) { + if (!dmabuf->draw_submitted) { + return; + } else { + dmabuf->draw_submitted = false; + } + } +#endif gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); vc->gfx.scale_x = (double)ww / vc->gfx.w; vc->gfx.scale_y = (double)wh / vc->gfx.h; + + glFlush(); +#ifdef CONFIG_GBM + if (dmabuf) { + egl_dmabuf_create_fence(dmabuf); + if (dmabuf->fence_fd > 0) { + qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); + return; + } + graphic_hw_gl_block(vc->gfx.dcl.con, false); + } +#endif } else { if (!vc->gfx.ds) { return; @@ -92,21 +116,10 @@ void gd_egl_draw(VirtualConsole *vc) vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); + + glFlush(); } - glFlush(); -#ifdef CONFIG_GBM - if (vc->gfx.guest_fb.dmabuf) { - QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; - - egl_dmabuf_create_fence(dmabuf); - if (dmabuf->fence_fd > 0) { - qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); - return; - } - graphic_hw_gl_block(vc->gfx.dcl.con, false); - } -#endif graphic_hw_gl_flushed(vc->gfx.dcl.con); } @@ -317,6 +330,7 @@ void gd_egl_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf) { graphic_hw_gl_block(vc->gfx.dcl.con, true); + vc->gfx.guest_fb.dmabuf->draw_submitted = true; gtk_widget_queue_draw_area(area, x, y, w, h); return; } diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index afcb29f658..461da7712f 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -38,6 +38,9 @@ static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout) void gd_gl_area_draw(VirtualConsole *vc) { +#ifdef CONFIG_GBM + QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; +#endif int ww, wh, y1, y2; if (!vc->gfx.gls) { @@ -53,6 +56,16 @@ void gd_gl_area_draw(VirtualConsole *vc) return; } +#ifdef CONFIG_GBM + if (dmabuf) { + if (!dmabuf->draw_submitted) { + return; + } else { + dmabuf->draw_submitted = false; + } + } +#endif + glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer); /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ @@ -62,6 +75,22 @@ void gd_gl_area_draw(VirtualConsole *vc) glBlitFramebuffer(0, y1, vc->gfx.w, y2, 0, 0, ww, wh, GL_COLOR_BUFFER_BIT, GL_NEAREST); +#ifdef CONFIG_GBM + if (dmabuf) { + egl_dmabuf_create_sync(dmabuf); + } +#endif + glFlush(); +#ifdef CONFIG_GBM + if (dmabuf) { + egl_dmabuf_create_fence(dmabuf); + if (dmabuf->fence_fd > 0) { + qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); + return; + } + graphic_hw_gl_block(vc->gfx.dcl.con, false); + } +#endif } else { if (!vc->gfx.ds) { return; @@ -72,25 +101,6 @@ void gd_gl_area_draw(VirtualConsole *vc) surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); } -#ifdef CONFIG_GBM - if (vc->gfx.guest_fb.dmabuf) { - egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf); - } -#endif - - glFlush(); -#ifdef CONFIG_GBM - if (vc->gfx.guest_fb.dmabuf) { - QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; - - egl_dmabuf_create_fence(dmabuf); - if (dmabuf->fence_fd > 0) { - qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); - return; - } - graphic_hw_gl_block(vc->gfx.dcl.con, false); - } -#endif graphic_hw_gl_flushed(vc->gfx.dcl.con); } @@ -237,6 +247,7 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf) { graphic_hw_gl_block(vc->gfx.dcl.con, true); + vc->gfx.guest_fb.dmabuf->draw_submitted = true; } gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); } From 0c9d0641ac5ecea5c9a8b6bf9961dbecd36bdb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 16 Sep 2021 21:22:36 +0200 Subject: [PATCH 1131/1334] ui/console: replace QEMUFIFO with Fifo8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One of the two FIFO implementations QEMUFIFO and Fifo8 is redundant. Replace QEMUFIFO with Fifo8. Signed-off-by: Volker Rümelin Reviewed-by: Marc-André Lureau Message-Id: <20210916192239.18742-1-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- ui/console.c | 86 ++++++++++++---------------------------------------- 1 file changed, 20 insertions(+), 66 deletions(-) diff --git a/ui/console.c b/ui/console.c index eabbbc951c..d2433c0636 100644 --- a/ui/console.c +++ b/ui/console.c @@ -27,6 +27,7 @@ #include "hw/qdev-core.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" +#include "qemu/fifo8.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/timer.h" @@ -62,57 +63,6 @@ enum TTYState { TTY_STATE_CSI, }; -typedef struct QEMUFIFO { - uint8_t *buf; - int buf_size; - int count, wptr, rptr; -} QEMUFIFO; - -static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1) -{ - int l, len; - - l = f->buf_size - f->count; - if (len1 > l) - len1 = l; - len = len1; - while (len > 0) { - l = f->buf_size - f->wptr; - if (l > len) - l = len; - memcpy(f->buf + f->wptr, buf, l); - f->wptr += l; - if (f->wptr >= f->buf_size) - f->wptr = 0; - buf += l; - len -= l; - } - f->count += len1; - return len1; -} - -static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1) -{ - int l, len; - - if (len1 > f->count) - len1 = f->count; - len = len1; - while (len > 0) { - l = f->buf_size - f->rptr; - if (l > len) - l = len; - memcpy(buf, f->buf + f->rptr, l); - f->rptr += l; - if (f->rptr >= f->buf_size) - f->rptr = 0; - buf += l; - len -= l; - } - f->count -= len1; - return len1; -} - typedef enum { GRAPHIC_CONSOLE, TEXT_CONSOLE, @@ -165,8 +115,7 @@ struct QemuConsole { Chardev *chr; /* fifo for key pressed */ - QEMUFIFO out_fifo; - uint8_t out_fifo_buf[16]; + Fifo8 out_fifo; QEMUTimer *kbd_timer; CoQueue dump_queue; @@ -1160,21 +1109,25 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) static void kbd_send_chars(void *opaque) { QemuConsole *s = opaque; - int len; - uint8_t buf[16]; + uint32_t len, avail; len = qemu_chr_be_can_write(s->chr); - if (len > s->out_fifo.count) - len = s->out_fifo.count; - if (len > 0) { - if (len > sizeof(buf)) - len = sizeof(buf); - qemu_fifo_read(&s->out_fifo, buf, len); - qemu_chr_be_write(s->chr, buf, len); + avail = fifo8_num_used(&s->out_fifo); + if (len > avail) { + len = avail; + } + while (len > 0) { + const uint8_t *buf; + uint32_t size; + + buf = fifo8_pop_buf(&s->out_fifo, len, &size); + qemu_chr_be_write(s->chr, (uint8_t *)buf, size); + len -= size; + avail -= size; } /* characters are pending: we send them a bit later (XXX: horrible, should change char device API) */ - if (s->out_fifo.count > 0) { + if (avail > 0) { timer_mod(s->kbd_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1); } } @@ -1185,6 +1138,7 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym) uint8_t buf[16], *q; CharBackend *be; int c; + uint32_t num_free; if (!s || (s->console_type == GRAPHIC_CONSOLE)) return; @@ -1228,7 +1182,8 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym) } be = s->chr->be; if (be && be->chr_read) { - qemu_fifo_write(&s->out_fifo, buf, q - buf); + num_free = fifo8_num_free(&s->out_fifo); + fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); kbd_send_chars(s); } break; @@ -2233,8 +2188,7 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds) int g_width = 80 * FONT_WIDTH; int g_height = 24 * FONT_HEIGHT; - s->out_fifo.buf = s->out_fifo_buf; - s->out_fifo.buf_size = sizeof(s->out_fifo_buf); + fifo8_create(&s->out_fifo, 16); s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s); s->ds = ds; From ec222519046bb6296bd1acc5a467c791d803d56c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 16 Sep 2021 21:22:37 +0200 Subject: [PATCH 1132/1334] ui/console: replace kbd_timer with chr_accept_input callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's a ChardevClass chr_accept_input() callback function that can replace the write retry timer. Reviewed-by: Marc-André Lureau Signed-off-by: Volker Rümelin Message-Id: <20210916192239.18742-2-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- ui/console.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/ui/console.c b/ui/console.c index d2433c0636..dda1e6861d 100644 --- a/ui/console.c +++ b/ui/console.c @@ -116,7 +116,6 @@ struct QemuConsole { Chardev *chr; /* fifo for key pressed */ Fifo8 out_fifo; - QEMUTimer *kbd_timer; CoQueue dump_queue; QTAILQ_ENTRY(QemuConsole) next; @@ -1106,30 +1105,21 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) return len; } -static void kbd_send_chars(void *opaque) +static void kbd_send_chars(QemuConsole *s) { - QemuConsole *s = opaque; uint32_t len, avail; len = qemu_chr_be_can_write(s->chr); avail = fifo8_num_used(&s->out_fifo); - if (len > avail) { - len = avail; - } - while (len > 0) { + while (len > 0 && avail > 0) { const uint8_t *buf; uint32_t size; - buf = fifo8_pop_buf(&s->out_fifo, len, &size); + buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size); qemu_chr_be_write(s->chr, (uint8_t *)buf, size); - len -= size; + len = qemu_chr_be_can_write(s->chr); avail -= size; } - /* characters are pending: we send them a bit later (XXX: - horrible, should change char device API) */ - if (avail > 0) { - timer_mod(s->kbd_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1); - } } /* called when an ascii key is pressed */ @@ -2141,6 +2131,14 @@ int qemu_console_get_height(QemuConsole *con, int fallback) return con ? surface_height(con->surface) : fallback; } +static void vc_chr_accept_input(Chardev *chr) +{ + VCChardev *drv = VC_CHARDEV(chr); + QemuConsole *s = drv->console; + + kbd_send_chars(s); +} + static void vc_chr_set_echo(Chardev *chr, bool echo) { VCChardev *drv = VC_CHARDEV(chr); @@ -2189,7 +2187,6 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds) int g_height = 24 * FONT_HEIGHT; fifo8_create(&s->out_fifo, 16); - s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s); s->ds = ds; s->y_displayed = 0; @@ -2439,6 +2436,7 @@ static void char_vc_class_init(ObjectClass *oc, void *data) cc->parse = qemu_chr_parse_vc; cc->open = vc_chr_open; cc->chr_write = vc_chr_write; + cc->chr_accept_input = vc_chr_accept_input; cc->chr_set_echo = vc_chr_set_echo; } From 014b00cc0a6b975fd67d7a1d5d49588c4d325a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 16 Sep 2021 21:22:38 +0200 Subject: [PATCH 1133/1334] ui/console: remove chardev frontend connected test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test if the chardev frontend is connected in kbd_put_keysym_console() is redundant, because the call to qemu_chr_be_can_write() in kbd_send_chars() tests the connected condition again. Remove the redundant test whether the chardev frontend is connected. Reviewed-by: Marc-André Lureau Signed-off-by: Volker Rümelin Message-Id: <20210916192239.18742-3-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- ui/console.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ui/console.c b/ui/console.c index dda1e6861d..29a3e3f0f5 100644 --- a/ui/console.c +++ b/ui/console.c @@ -28,10 +28,11 @@ #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" #include "qemu/fifo8.h" +#include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/timer.h" -#include "chardev/char-fe.h" +#include "chardev/char.h" #include "trace.h" #include "exec/memory.h" #include "io/channel-file.h" @@ -1126,7 +1127,6 @@ static void kbd_send_chars(QemuConsole *s) void kbd_put_keysym_console(QemuConsole *s, int keysym) { uint8_t buf[16], *q; - CharBackend *be; int c; uint32_t num_free; @@ -1170,12 +1170,9 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym) if (s->echo) { vc_chr_write(s->chr, buf, q - buf); } - be = s->chr->be; - if (be && be->chr_read) { - num_free = fifo8_num_free(&s->out_fifo); - fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); - kbd_send_chars(s); - } + num_free = fifo8_num_free(&s->out_fifo); + fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); + kbd_send_chars(s); break; } } From 7c8d295b274f18f582b931c59dba0cba94f6ca7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 9 Sep 2021 13:32:19 +0100 Subject: [PATCH 1134/1334] hw/misc: deprecate the 'sga' device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is obsolete since SeaBIOS 1.11.0 introduced native support for sending messages to the serial console. The new support can be activated using -machine graphics=off on x86 targets. Signed-off-by: Daniel P. Berrangé Reviewed-by: Gerd Hoffmann Message-Id: <20210909123219.862652-1-berrange@redhat.com> Signed-off-by: Gerd Hoffmann --- docs/about/deprecated.rst | 10 ++++++++++ hw/misc/sga.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index be19317470..25b7ec8d92 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -313,6 +313,16 @@ full SCSI support. Use virtio-scsi instead when SCSI passthrough is required. Note this also applies to ``-device virtio-blk-pci,scsi=on|off``, which is an alias. +``-device sga`` (since 6.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``sga`` device loads an option ROM for x86 targets which enables +SeaBIOS to send messages to the serial console. SeaBIOS 1.11.0 onwards +contains native support for this feature and thus use of the option +ROM approach is obsolete. The native SeaBIOS support can be activated +by using ``-machine graphics=off``. + + Block device options '''''''''''''''''''' diff --git a/hw/misc/sga.c b/hw/misc/sga.c index 4dbe6d78f9..1d04672b01 100644 --- a/hw/misc/sga.c +++ b/hw/misc/sga.c @@ -30,6 +30,7 @@ #include "hw/loader.h" #include "qemu/module.h" #include "qom/object.h" +#include "qemu/error-report.h" #define SGABIOS_FILENAME "sgabios.bin" @@ -42,6 +43,7 @@ struct ISASGAState { static void sga_realizefn(DeviceState *dev, Error **errp) { + warn_report("-device sga is deprecated, use -machine graphics=off"); rom_add_vga(SGABIOS_FILENAME); } From 58d7d4c7869cb3addb0714aa7b6bd88f2b6b7edf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 6 Sep 2021 06:55:23 +0200 Subject: [PATCH 1135/1334] usb-storage: tag usb_msd_csw as packed struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this the struct has the wrong size: sizeof() evaluates to 16 instead of 13. In most cases the bug is hidden by the fact that guests submits a buffer which is exactly 13 bytes long, so the padding added by the compiler is simply ignored. But sometimes guests submit a larger buffer and expect a short transfer, which does not work properly with the wrong struct size. Cc: vintagepc404@protonmail.com Signed-off-by: Gerd Hoffmann Fixes: a917d384ac0 ("SCSI TCQ support.") Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210906045523.1259629-1-kraxel@redhat.com> --- include/hw/usb/msd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/usb/msd.h b/include/hw/usb/msd.h index 7538c54569..54e9f38bda 100644 --- a/include/hw/usb/msd.h +++ b/include/hw/usb/msd.h @@ -17,7 +17,7 @@ enum USBMSDMode { USB_MSDM_CSW /* Command Status. */ }; -struct usb_msd_csw { +struct QEMU_PACKED usb_msd_csw { uint32_t sig; uint32_t tag; uint32_t residue; From 8092b51849499be97c42c0f1a832ade969e38724 Mon Sep 17 00:00:00 2001 From: Shengtan Mao Date: Thu, 7 Oct 2021 17:26:24 -0700 Subject: [PATCH 1136/1334] hw/sd: add nuvoton MMC Signed-off-by: Shengtan Mao Signed-off-by: Hao Wu Reviewed-by: Hao Wu Reviewed-by: Chris Rauer Reviewed-by: Tyrone Ting Reviewed-by: Peter Maydell Message-Id: <20211008002628.1958285-2-wuhaotsh@google.com> [rth: Fix typos of "nonexistent"] Signed-off-by: Richard Henderson --- hw/sd/meson.build | 1 + hw/sd/npcm7xx_sdhci.c | 182 ++++++++++++++++++++++++++++++++++ include/hw/sd/npcm7xx_sdhci.h | 65 ++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 hw/sd/npcm7xx_sdhci.c create mode 100644 include/hw/sd/npcm7xx_sdhci.h diff --git a/hw/sd/meson.build b/hw/sd/meson.build index f1ce357a3b..807ca07b7c 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -9,4 +9,5 @@ softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_mmci.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_sdhost.c')) softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) +softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_sdhci.c')) softmmu_ss.add(when: 'CONFIG_CADENCE_SDHCI', if_true: files('cadence_sdhci.c')) diff --git a/hw/sd/npcm7xx_sdhci.c b/hw/sd/npcm7xx_sdhci.c new file mode 100644 index 0000000000..ef503365df --- /dev/null +++ b/hw/sd/npcm7xx_sdhci.c @@ -0,0 +1,182 @@ +/* + * NPCM7xx SD-3.0 / eMMC-4.51 Host Controller + * + * Copyright (c) 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" + +#include "hw/sd/npcm7xx_sdhci.h" +#include "migration/vmstate.h" +#include "sdhci-internal.h" +#include "qemu/log.h" + +static uint64_t npcm7xx_sdhci_read(void *opaque, hwaddr addr, unsigned int size) +{ + NPCM7xxSDHCIState *s = opaque; + uint64_t val = 0; + + switch (addr) { + case NPCM7XX_PRSTVALS_0: + case NPCM7XX_PRSTVALS_1: + case NPCM7XX_PRSTVALS_2: + case NPCM7XX_PRSTVALS_3: + case NPCM7XX_PRSTVALS_4: + case NPCM7XX_PRSTVALS_5: + val = s->regs.prstvals[(addr - NPCM7XX_PRSTVALS_0) / 2]; + break; + case NPCM7XX_BOOTTOCTRL: + val = s->regs.boottoctrl; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "SDHCI read of nonexistent reg: 0x%02" + HWADDR_PRIx, addr); + break; + } + + return val; +} + +static void npcm7xx_sdhci_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + NPCM7xxSDHCIState *s = opaque; + + switch (addr) { + case NPCM7XX_BOOTTOCTRL: + s->regs.boottoctrl = val; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "SDHCI write of nonexistent reg: 0x%02" + HWADDR_PRIx, addr); + break; + } +} + +static bool npcm7xx_sdhci_check_mem_op(void *opaque, hwaddr addr, + unsigned size, bool is_write, + MemTxAttrs attrs) +{ + switch (addr) { + case NPCM7XX_PRSTVALS_0: + case NPCM7XX_PRSTVALS_1: + case NPCM7XX_PRSTVALS_2: + case NPCM7XX_PRSTVALS_3: + case NPCM7XX_PRSTVALS_4: + case NPCM7XX_PRSTVALS_5: + /* RO Word */ + return !is_write && size == 2; + case NPCM7XX_BOOTTOCTRL: + /* R/W Dword */ + return size == 4; + default: + return false; + } +} + +static const MemoryRegionOps npcm7xx_sdhci_ops = { + .read = npcm7xx_sdhci_read, + .write = npcm7xx_sdhci_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + .unaligned = false, + .accepts = npcm7xx_sdhci_check_mem_op, + }, +}; + +static void npcm7xx_sdhci_realize(DeviceState *dev, Error **errp) +{ + NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SysBusDevice *sbd_sdhci = SYS_BUS_DEVICE(&s->sdhci); + + memory_region_init(&s->container, OBJECT(s), + "npcm7xx.sdhci-container", 0x1000); + sysbus_init_mmio(sbd, &s->container); + + memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_sdhci_ops, s, + TYPE_NPCM7XX_SDHCI, NPCM7XX_SDHCI_REGSIZE); + memory_region_add_subregion_overlap(&s->container, NPCM7XX_PRSTVALS, + &s->iomem, 1); + + sysbus_realize(sbd_sdhci, errp); + memory_region_add_subregion(&s->container, 0, + sysbus_mmio_get_region(sbd_sdhci, 0)); + + /* propagate irq and "sd-bus" from generic-sdhci */ + sysbus_pass_irq(sbd, sbd_sdhci); + s->bus = qdev_get_child_bus(DEVICE(sbd_sdhci), "sd-bus"); + + /* Set the read only preset values. */ + memset(s->regs.prstvals, 0, sizeof(s->regs.prstvals)); + s->regs.prstvals[0] = NPCM7XX_PRSTVALS_0_RESET; + s->regs.prstvals[1] = NPCM7XX_PRSTVALS_1_RESET; + s->regs.prstvals[3] = NPCM7XX_PRSTVALS_3_RESET; +} + +static void npcm7xx_sdhci_reset(DeviceState *dev) +{ + NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev); + device_cold_reset(DEVICE(&s->sdhci)); + s->regs.boottoctrl = 0; + + s->sdhci.prnsts = NPCM7XX_PRSNTS_RESET; + s->sdhci.blkgap = NPCM7XX_BLKGAP_RESET; + s->sdhci.capareg = NPCM7XX_CAPAB_RESET; + s->sdhci.maxcurr = NPCM7XX_MAXCURR_RESET; + s->sdhci.version = NPCM7XX_HCVER_RESET; +} + +static const VMStateDescription vmstate_npcm7xx_sdhci = { + .name = TYPE_NPCM7XX_SDHCI, + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(regs.boottoctrl, NPCM7xxSDHCIState), + VMSTATE_END_OF_LIST(), + }, +}; + +static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(classp); + + dc->desc = "NPCM7xx SD/eMMC Host Controller"; + dc->realize = npcm7xx_sdhci_realize; + dc->reset = npcm7xx_sdhci_reset; + dc->vmsd = &vmstate_npcm7xx_sdhci; +} + +static void npcm7xx_sdhci_instance_init(Object *obj) +{ + NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(obj); + + object_initialize_child(OBJECT(s), "generic-sdhci", &s->sdhci, + TYPE_SYSBUS_SDHCI); +} + +static TypeInfo npcm7xx_sdhci_info = { + .name = TYPE_NPCM7XX_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCM7xxSDHCIState), + .instance_init = npcm7xx_sdhci_instance_init, + .class_init = npcm7xx_sdhci_class_init, +}; + +static void npcm7xx_sdhci_register_types(void) +{ + type_register_static(&npcm7xx_sdhci_info); +} + +type_init(npcm7xx_sdhci_register_types) diff --git a/include/hw/sd/npcm7xx_sdhci.h b/include/hw/sd/npcm7xx_sdhci.h new file mode 100644 index 0000000000..d728f0a40d --- /dev/null +++ b/include/hw/sd/npcm7xx_sdhci.h @@ -0,0 +1,65 @@ +/* + * NPCM7xx SD-3.0 / eMMC-4.51 Host Controller + * + * Copyright (c) 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef NPCM7XX_SDHCI_H +#define NPCM7XX_SDHCI_H + +#include "hw/sd/sdhci.h" +#include "qom/object.h" + +#define TYPE_NPCM7XX_SDHCI "npcm7xx.sdhci" +#define NPCM7XX_PRSTVALS_SIZE 6 +#define NPCM7XX_PRSTVALS 0x60 +#define NPCM7XX_PRSTVALS_0 0x0 +#define NPCM7XX_PRSTVALS_1 0x2 +#define NPCM7XX_PRSTVALS_2 0x4 +#define NPCM7XX_PRSTVALS_3 0x6 +#define NPCM7XX_PRSTVALS_4 0x8 +#define NPCM7XX_PRSTVALS_5 0xA +#define NPCM7XX_BOOTTOCTRL 0x10 +#define NPCM7XX_SDHCI_REGSIZE 0x20 + +#define NPCM7XX_PRSNTS_RESET 0x04A00000 +#define NPCM7XX_BLKGAP_RESET 0x80 +#define NPCM7XX_CAPAB_RESET 0x0100200161EE0399 +#define NPCM7XX_MAXCURR_RESET 0x0000000000000005 +#define NPCM7XX_HCVER_RESET 0x1002 + +#define NPCM7XX_PRSTVALS_0_RESET 0x0040 +#define NPCM7XX_PRSTVALS_1_RESET 0x0001 +#define NPCM7XX_PRSTVALS_3_RESET 0x0001 + +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxSDHCIState, NPCM7XX_SDHCI) + +typedef struct NPCM7xxRegs { + /* Preset Values Register Field, read-only */ + uint16_t prstvals[NPCM7XX_PRSTVALS_SIZE]; + /* Boot Timeout Control Register, read-write */ + uint32_t boottoctrl; +} NPCM7xxRegisters; + +typedef struct NPCM7xxSDHCIState { + SysBusDevice parent; + + MemoryRegion container; + MemoryRegion iomem; + BusState *bus; + NPCM7xxRegisters regs; + + SDHCIState sdhci; +} NPCM7xxSDHCIState; + +#endif /* NPCM7XX_SDHCI_H */ From 0a9df6cb9fb8f98258e51797e59ecaf9ef6b79a2 Mon Sep 17 00:00:00 2001 From: Shengtan Mao Date: Thu, 7 Oct 2021 17:26:25 -0700 Subject: [PATCH 1137/1334] hw/arm: Add Nuvoton SD module to board Signed-off-by: Shengtan Mao Signed-off-by: Hao Wu Reviewed-by: Hao Wu Reviewed-by: Chris Rauer Reviewed-by: Tyrone Ting Reviewed-by: Peter Maydell Message-Id: <20211008002628.1958285-3-wuhaotsh@google.com> Signed-off-by: Richard Henderson --- hw/arm/npcm7xx.c | 12 +++++++++++- include/hw/arm/npcm7xx.h | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 2ab0080e0b..878c2208e0 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -63,6 +63,8 @@ #define NPCM7XX_ROM_BA (0xffff0000) #define NPCM7XX_ROM_SZ (64 * KiB) +/* SDHCI Modules */ +#define NPCM7XX_MMC_BA (0xf0842000) /* Clock configuration values to be fixed up when bypassing bootloader */ @@ -83,6 +85,7 @@ enum NPCM7xxInterrupt { NPCM7XX_UART3_IRQ, NPCM7XX_EMC1RX_IRQ = 15, NPCM7XX_EMC1TX_IRQ, + NPCM7XX_MMC_IRQ = 26, NPCM7XX_TIMER0_IRQ = 32, /* Timer Module 0 */ NPCM7XX_TIMER1_IRQ, NPCM7XX_TIMER2_IRQ, @@ -443,6 +446,8 @@ static void npcm7xx_init(Object *obj) for (i = 0; i < ARRAY_SIZE(s->emc); i++) { object_initialize_child(obj, "emc[*]", &s->emc[i], TYPE_NPCM7XX_EMC); } + + object_initialize_child(obj, "mmc", &s->mmc, TYPE_NPCM7XX_SDHCI); } static void npcm7xx_realize(DeviceState *dev, Error **errp) @@ -707,6 +712,12 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) &error_abort); memory_region_add_subregion(get_system_memory(), NPCM7XX_ROM_BA, &s->irom); + /* SDHCI */ + sysbus_realize(SYS_BUS_DEVICE(&s->mmc), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc), 0, NPCM7XX_MMC_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc), 0, + npcm7xx_irq(s, NPCM7XX_MMC_IRQ)); + create_unimplemented_device("npcm7xx.shm", 0xc0001000, 4 * KiB); create_unimplemented_device("npcm7xx.vdmx", 0xe0800000, 4 * KiB); create_unimplemented_device("npcm7xx.pcierc", 0xe1000000, 64 * KiB); @@ -736,7 +747,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm7xx.usbd[8]", 0xf0838000, 4 * KiB); create_unimplemented_device("npcm7xx.usbd[9]", 0xf0839000, 4 * KiB); create_unimplemented_device("npcm7xx.sd", 0xf0840000, 8 * KiB); - create_unimplemented_device("npcm7xx.mmc", 0xf0842000, 8 * KiB); create_unimplemented_device("npcm7xx.pcimbx", 0xf0848000, 512 * KiB); create_unimplemented_device("npcm7xx.aes", 0xf0858000, 4 * KiB); create_unimplemented_device("npcm7xx.des", 0xf0859000, 4 * KiB); diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index 61ecc57ab9..ce593235d9 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -35,6 +35,7 @@ #include "hw/usb/hcd-ehci.h" #include "hw/usb/hcd-ohci.h" #include "target/arm/cpu.h" +#include "hw/sd/npcm7xx_sdhci.h" #define NPCM7XX_MAX_NUM_CPUS (2) @@ -103,6 +104,7 @@ typedef struct NPCM7xxState { OHCISysBusState ohci; NPCM7xxFIUState fiu[2]; NPCM7xxEMCState emc[2]; + NPCM7xxSDHCIState mmc; } NPCM7xxState; #define TYPE_NPCM7XX "npcm7xx" From 2cb06d492da9f7054eaa9ecbff1e0538b2cf49f8 Mon Sep 17 00:00:00 2001 From: Shengtan Mao Date: Thu, 7 Oct 2021 17:26:26 -0700 Subject: [PATCH 1138/1334] hw/arm: Attach MMC to quanta-gbs-bmc Signed-off-by: Shengtan Mao Signed-off-by: Hao Wu Reviewed-by: Hao Wu Reviewed-by: Tyrone Ting Reviewed-by: Peter Maydell Message-Id: <20211008002628.1958285-4-wuhaotsh@google.com> Signed-off-by: Richard Henderson --- hw/arm/npcm7xx_boards.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index a656169f61..dec7d16ae5 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -27,6 +27,9 @@ #include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" +#include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" #define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7 #define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff @@ -81,6 +84,22 @@ static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram) &error_abort); } +static void sdhci_attach_drive(SDHCIState *sdhci) +{ + DriveInfo *di = drive_get_next(IF_SD); + BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; + + BusState *bus = qdev_get_child_bus(DEVICE(sdhci), "sd-bus"); + if (bus == NULL) { + error_report("No SD bus found in SOC object"); + exit(1); + } + + DeviceState *carddev = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); + qdev_realize_and_unref(carddev, bus, &error_fatal); +} + static NPCM7xxState *npcm7xx_create_soc(MachineState *machine, uint32_t hw_straps) { @@ -355,6 +374,7 @@ static void quanta_gbs_init(MachineState *machine) drive_get(IF_MTD, 0, 0)); quanta_gbs_i2c_init(soc); + sdhci_attach_drive(&soc->mmc.sdhci); npcm7xx_load_kernel(machine, soc); } From da2f02b360f405d7badc4a3028276e07db58026c Mon Sep 17 00:00:00 2001 From: Shengtan Mao Date: Thu, 7 Oct 2021 17:26:27 -0700 Subject: [PATCH 1139/1334] tests/qtest/libqos: add SDHCI commands Signed-off-by: Shengtan Mao Signed-off-by: Hao Wu Reviewed-by: Hao Wu Reviewed-by: Chris Rauer Reviewed-by: Tyrone Ting Reviewed-by: Peter Maydell Message-Id: <20211008002628.1958285-5-wuhaotsh@google.com> Signed-off-by: Richard Henderson --- tests/qtest/libqos/meson.build | 1 + tests/qtest/libqos/sdhci-cmd.c | 116 +++++++++++++++++++++++++++++++++ tests/qtest/libqos/sdhci-cmd.h | 70 ++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 tests/qtest/libqos/sdhci-cmd.c create mode 100644 tests/qtest/libqos/sdhci-cmd.h diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 1f5c8f1053..4af1f04787 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -5,6 +5,7 @@ libqos_srcs = files('../libqtest.c', 'fw_cfg.c', 'malloc.c', 'libqos.c', + 'sdhci-cmd.c', # spapr 'malloc-spapr.c', diff --git a/tests/qtest/libqos/sdhci-cmd.c b/tests/qtest/libqos/sdhci-cmd.c new file mode 100644 index 0000000000..2d9e518341 --- /dev/null +++ b/tests/qtest/libqos/sdhci-cmd.c @@ -0,0 +1,116 @@ +/* + * MMC Host Controller Commands + * + * Copyright (c) 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "sdhci-cmd.h" +#include "libqtest.h" + +static ssize_t read_fifo(QTestState *qts, uint64_t reg, char *msg, size_t count) +{ + uint32_t mask = 0xff; + size_t index = 0; + uint32_t msg_frag; + int size; + while (index < count) { + size = count - index; + if (size > 4) { + size = 4; + } + msg_frag = qtest_readl(qts, reg); + while (size > 0) { + msg[index] = msg_frag & mask; + if (msg[index++] == 0) { + return index; + } + msg_frag >>= 8; + --size; + } + } + return index; +} + +static void write_fifo(QTestState *qts, uint64_t reg, const char *msg, + size_t count) +{ + size_t index = 0; + uint32_t msg_frag; + int size; + int frag_i; + while (index < count) { + size = count - index; + if (size > 4) { + size = 4; + } + msg_frag = 0; + frag_i = 0; + while (frag_i < size) { + msg_frag |= ((uint32_t)msg[index++]) << (frag_i * 8); + ++frag_i; + } + qtest_writel(qts, reg, msg_frag); + } +} + +static void fill_block(QTestState *qts, uint64_t reg, int count) +{ + while (--count >= 0) { + qtest_writel(qts, reg, 0); + } +} + +void sdhci_cmd_regs(QTestState *qts, uint64_t base_addr, uint16_t blksize, + uint16_t blkcnt, uint32_t argument, uint16_t trnmod, + uint16_t cmdreg) +{ + qtest_writew(qts, base_addr + SDHC_BLKSIZE, blksize); + qtest_writew(qts, base_addr + SDHC_BLKCNT, blkcnt); + qtest_writel(qts, base_addr + SDHC_ARGUMENT, argument); + qtest_writew(qts, base_addr + SDHC_TRNMOD, trnmod); + qtest_writew(qts, base_addr + SDHC_CMDREG, cmdreg); +} + +ssize_t sdhci_read_cmd(QTestState *qts, uint64_t base_addr, char *msg, + size_t count) +{ + sdhci_cmd_regs(qts, base_addr, count, 1, 0, + SDHC_TRNS_MULTI | SDHC_TRNS_READ | SDHC_TRNS_BLK_CNT_EN, + SDHC_READ_MULTIPLE_BLOCK | SDHC_CMD_DATA_PRESENT); + + /* read sd fifo_buffer */ + ssize_t bytes_read = read_fifo(qts, base_addr + SDHC_BDATA, msg, count); + + sdhci_cmd_regs(qts, base_addr, 0, 0, 0, + SDHC_TRNS_MULTI | SDHC_TRNS_READ | SDHC_TRNS_BLK_CNT_EN, + SDHC_STOP_TRANSMISSION); + + return bytes_read; +} + +void sdhci_write_cmd(QTestState *qts, uint64_t base_addr, const char *msg, + size_t count, size_t blksize) +{ + sdhci_cmd_regs(qts, base_addr, blksize, 1, 0, + SDHC_TRNS_MULTI | SDHC_TRNS_WRITE | SDHC_TRNS_BLK_CNT_EN, + SDHC_WRITE_MULTIPLE_BLOCK | SDHC_CMD_DATA_PRESENT); + + /* write to sd fifo_buffer */ + write_fifo(qts, base_addr + SDHC_BDATA, msg, count); + fill_block(qts, base_addr + SDHC_BDATA, (blksize - count) / 4); + + sdhci_cmd_regs(qts, base_addr, 0, 0, 0, + SDHC_TRNS_MULTI | SDHC_TRNS_WRITE | SDHC_TRNS_BLK_CNT_EN, + SDHC_STOP_TRANSMISSION); +} diff --git a/tests/qtest/libqos/sdhci-cmd.h b/tests/qtest/libqos/sdhci-cmd.h new file mode 100644 index 0000000000..64763c5a2a --- /dev/null +++ b/tests/qtest/libqos/sdhci-cmd.h @@ -0,0 +1,70 @@ +/* + * MMC Host Controller Commands + * + * Copyright (c) 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "libqtest.h" + +/* more details at hw/sd/sdhci-internal.h */ +#define SDHC_BLKSIZE 0x04 +#define SDHC_BLKCNT 0x06 +#define SDHC_ARGUMENT 0x08 +#define SDHC_TRNMOD 0x0C +#define SDHC_CMDREG 0x0E +#define SDHC_BDATA 0x20 +#define SDHC_PRNSTS 0x24 +#define SDHC_BLKGAP 0x2A +#define SDHC_CLKCON 0x2C +#define SDHC_SWRST 0x2F +#define SDHC_CAPAB 0x40 +#define SDHC_MAXCURR 0x48 +#define SDHC_HCVER 0xFE + +/* TRNSMOD Reg */ +#define SDHC_TRNS_BLK_CNT_EN 0x0002 +#define SDHC_TRNS_READ 0x0010 +#define SDHC_TRNS_WRITE 0x0000 +#define SDHC_TRNS_MULTI 0x0020 + +/* CMD Reg */ +#define SDHC_CMD_DATA_PRESENT (1 << 5) +#define SDHC_ALL_SEND_CID (2 << 8) +#define SDHC_SEND_RELATIVE_ADDR (3 << 8) +#define SDHC_SELECT_DESELECT_CARD (7 << 8) +#define SDHC_SEND_CSD (9 << 8) +#define SDHC_STOP_TRANSMISSION (12 << 8) +#define SDHC_READ_MULTIPLE_BLOCK (18 << 8) +#define SDHC_WRITE_MULTIPLE_BLOCK (25 << 8) +#define SDHC_APP_CMD (55 << 8) + +/* SWRST Reg */ +#define SDHC_RESET_ALL 0x01 + +/* CLKCTRL Reg */ +#define SDHC_CLOCK_INT_EN 0x0001 +#define SDHC_CLOCK_INT_STABLE 0x0002 +#define SDHC_CLOCK_SDCLK_EN (1 << 2) + +/* Set registers needed to send commands to SD */ +void sdhci_cmd_regs(QTestState *qts, uint64_t base_addr, uint16_t blksize, + uint16_t blkcnt, uint32_t argument, uint16_t trnmod, + uint16_t cmdreg); + +/* Read at most 1 block of SD using non-DMA */ +ssize_t sdhci_read_cmd(QTestState *qts, uint64_t base_addr, char *msg, + size_t count); + +/* Write at most 1 block of SD using non-DMA */ +void sdhci_write_cmd(QTestState *qts, uint64_t base_addr, const char *msg, + size_t count, size_t blksize); From dbd9e08476f09cf3556b9b8a306bf277172841a9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 1 Nov 2021 16:08:14 +0000 Subject: [PATCH 1140/1334] target/arm: Advertise MVE to gdb when present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cortex-M CPUs with MVE should advertise this fact to gdb, using the org.gnu.gdb.arm.m-profile-mve XML feature, which defines the VPR register. Presence of this feature also tells gdb to create pseudo-registers Q0..Q7, so we do not need to tell gdb about them separately. Note that unless you have a very recent GDB that includes this fix: http://patches-tcwg.linaro.org/patch/58133/ gdb will mis-print the individual fields of the VPR register as zero (but showing the whole thing as hex, eg with "print /x $vpr" will give the correct value). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211101160814.5103-1-peter.maydell@linaro.org> Signed-off-by: Richard Henderson --- configs/targets/aarch64-softmmu.mak | 2 +- configs/targets/arm-linux-user.mak | 2 +- configs/targets/arm-softmmu.mak | 2 +- configs/targets/armeb-linux-user.mak | 2 +- gdb-xml/arm-m-profile-mve.xml | 19 +++++++++++++++++++ target/arm/gdbstub.c | 25 +++++++++++++++++++++++++ 6 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 gdb-xml/arm-m-profile-mve.xml diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index 13d40b55e6..d489e6da83 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -1,5 +1,5 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_NEED_FDT=y diff --git a/configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak index acecc339e3..3e10d6b15d 100644 --- a/configs/targets/arm-linux-user.mak +++ b/configs/targets/arm-linux-user.mak @@ -1,6 +1,6 @@ TARGET_ARCH=arm TARGET_SYSTBL_ABI=common,oabi TARGET_SYSTBL=syscall.tbl -TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak index f6c95ba07a..92c8349b96 100644 --- a/configs/targets/arm-softmmu.mak +++ b/configs/targets/arm-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=arm TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_NEED_FDT=y diff --git a/configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak index 662c73d8fb..f81e5bf1fe 100644 --- a/configs/targets/armeb-linux-user.mak +++ b/configs/targets/armeb-linux-user.mak @@ -2,6 +2,6 @@ TARGET_ARCH=arm TARGET_SYSTBL_ABI=common,oabi TARGET_SYSTBL=syscall.tbl TARGET_WORDS_BIGENDIAN=y -TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/gdb-xml/arm-m-profile-mve.xml b/gdb-xml/arm-m-profile-mve.xml new file mode 100644 index 0000000000..cba664c4c5 --- /dev/null +++ b/gdb-xml/arm-m-profile-mve.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index e0dcb33e32..134da0d0ae 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -199,6 +199,27 @@ static int vfp_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) return 0; } +static int mve_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) +{ + switch (reg) { + case 0: + return gdb_get_reg32(buf, env->v7m.vpr); + default: + return 0; + } +} + +static int mve_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) +{ + switch (reg) { + case 0: + env->v7m.vpr = ldl_p(buf); + return 4; + default: + return 0; + } +} + /** * arm_get/set_gdb_*: get/set a gdb register * @env: the CPU state @@ -468,6 +489,10 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) 2, "arm-vfp-sysregs.xml", 0); } } + if (cpu_isar_feature(aa32_mve, cpu)) { + gdb_register_coprocessor(cs, mve_gdb_get_reg, mve_gdb_set_reg, + 1, "arm-m-profile-mve.xml", 0); + } gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, arm_gen_dynamic_sysreg_xml(cs, cs->gdb_num_regs), "system-registers.xml", 0); From 060c1f4252bd8bfa06e311bdcb665b46964ce0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 30 Oct 2021 01:18:30 +0200 Subject: [PATCH 1141/1334] target/arm: Use tcg_constant_i32() in op_smlad() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using a TCG temporary for a read-only constant. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029231834.2476117-2-f4bug@amsat.org> Signed-off-by: Richard Henderson --- target/arm/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index d6af5b1b03..083a6d6ed7 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -7849,10 +7849,9 @@ static bool op_smlad(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) t3 = tcg_temp_new_i32(); tcg_gen_sari_i32(t3, t1, 31); qf = load_cpu_field(QF); - one = tcg_const_i32(1); + one = tcg_constant_i32(1); tcg_gen_movcond_i32(TCG_COND_NE, qf, t2, t3, one, qf); store_cpu_field(qf, QF); - tcg_temp_free_i32(one); tcg_temp_free_i32(t3); tcg_temp_free_i32(t2); } From daf7a1814feec8a52481a695c2b9f0b9c71f0d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 30 Oct 2021 01:18:31 +0200 Subject: [PATCH 1142/1334] target/arm: Introduce store_cpu_field_constant() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to the store_cpu_field() helper which takes a TCG temporary, store its value to the CPUState, introduce the store_cpu_field_constant() helper which store a constant to CPUState (without using any TCG temporary). Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029231834.2476117-3-f4bug@amsat.org> Signed-off-by: Richard Henderson --- target/arm/translate-a32.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/translate-a32.h b/target/arm/translate-a32.h index 88f15df60e..17af8dc95a 100644 --- a/target/arm/translate-a32.h +++ b/target/arm/translate-a32.h @@ -70,6 +70,9 @@ static inline void store_cpu_offset(TCGv_i32 var, int offset) #define store_cpu_field(var, name) \ store_cpu_offset(var, offsetof(CPUARMState, name)) +#define store_cpu_field_constant(val, name) \ + tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, offsetof(CPUARMState, name)) + /* Create a new temporary and set it to the value of a CPU register. */ static inline TCGv_i32 load_reg(DisasContext *s, int reg) { From cacb1aa486e713b9492e18668276a12b938cee33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 30 Oct 2021 01:18:32 +0200 Subject: [PATCH 1143/1334] target/arm: Use the constant variant of store_cpu_field() when possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using a constant variable, we can replace the store_cpu_field() call by store_cpu_field_constant() which avoid using TCG temporaries. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029231834.2476117-4-f4bug@amsat.org> Signed-off-by: Richard Henderson --- target/arm/translate.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index 083a6d6ed7..52ba562c96 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -364,8 +364,7 @@ void clear_eci_state(DisasContext *s) * multiple insn executes. */ if (s->eci) { - TCGv_i32 tmp = tcg_const_i32(0); - store_cpu_field(tmp, condexec_bits); + store_cpu_field_constant(0, condexec_bits); s->eci = 0; } } @@ -740,9 +739,8 @@ void gen_set_condexec(DisasContext *s) { if (s->condexec_mask) { uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, val); - store_cpu_field(tmp, condexec_bits); + + store_cpu_field_constant(val, condexec_bits); } } @@ -8362,8 +8360,6 @@ static bool trans_BL(DisasContext *s, arg_i *a) static bool trans_BLX_i(DisasContext *s, arg_BLX_i *a) { - TCGv_i32 tmp; - /* * BLX would be useless on M-profile; the encoding space * is used for other insns from v8.1M onward, and UNDEFs before that. @@ -8377,8 +8373,7 @@ static bool trans_BLX_i(DisasContext *s, arg_BLX_i *a) return false; } tcg_gen_movi_i32(cpu_R[14], s->base.pc_next | s->thumb); - tmp = tcg_const_i32(!s->thumb); - store_cpu_field(tmp, thumb); + store_cpu_field_constant(!s->thumb, thumb); gen_jmp(s, (read_pc(s) & ~3) + a->imm); return true; } @@ -8677,7 +8672,6 @@ static bool trans_LCTP(DisasContext *s, arg_LCTP *a) * doesn't cache branch information, all we need to do is reset * FPSCR.LTPSIZE to 4. */ - TCGv_i32 ltpsize; if (!dc_isar_feature(aa32_lob, s) || !dc_isar_feature(aa32_mve, s)) { @@ -8688,8 +8682,7 @@ static bool trans_LCTP(DisasContext *s, arg_LCTP *a) return true; } - ltpsize = tcg_const_i32(4); - store_cpu_field(ltpsize, v7m.ltpsize); + store_cpu_field_constant(4, v7m.ltpsize); return true; } @@ -9487,9 +9480,7 @@ static void arm_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) /* Reset the conditional execution bits immediately. This avoids complications trying to do it at the end of the block. */ if (dc->condexec_mask || dc->condexec_cond) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, 0); - store_cpu_field(tmp, condexec_bits); + store_cpu_field_constant(0, condexec_bits); } } From 35a1ec8e47a45a674b1ab2097ca9a28805dfd7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 30 Oct 2021 01:18:33 +0200 Subject: [PATCH 1144/1334] target/arm: Use tcg_constant_i64() in do_sat_addsub_64() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The immediate value used for comparison is constant and read-only. Move it to the constant pool. This frees a TCG temporary for unsigned saturation opcodes. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029231834.2476117-5-f4bug@amsat.org> Signed-off-by: Richard Henderson --- target/arm/translate-sve.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index bc91a64171..76b5fe9f31 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -1943,20 +1943,20 @@ static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) static void do_sat_addsub_64(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) { TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2; if (u) { if (d) { tcg_gen_sub_i64(t0, reg, val); - tcg_gen_movi_i64(t1, 0); - tcg_gen_movcond_i64(TCG_COND_LTU, reg, reg, val, t1, t0); + t2 = tcg_constant_i64(0); + tcg_gen_movcond_i64(TCG_COND_LTU, reg, reg, val, t2, t0); } else { tcg_gen_add_i64(t0, reg, val); - tcg_gen_movi_i64(t1, -1); - tcg_gen_movcond_i64(TCG_COND_LTU, reg, t0, reg, t1, t0); + t2 = tcg_constant_i64(-1); + tcg_gen_movcond_i64(TCG_COND_LTU, reg, t0, reg, t2, t0); } } else { + TCGv_i64 t1 = tcg_temp_new_i64(); if (d) { /* Detect signed overflow for subtraction. */ tcg_gen_xor_i64(t0, reg, val); @@ -1966,7 +1966,7 @@ static void do_sat_addsub_64(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) /* Bound the result. */ tcg_gen_movi_i64(reg, INT64_MIN); - t2 = tcg_const_i64(0); + t2 = tcg_constant_i64(0); tcg_gen_movcond_i64(TCG_COND_LT, reg, t0, t2, reg, t1); } else { /* Detect signed overflow for addition. */ @@ -1977,13 +1977,12 @@ static void do_sat_addsub_64(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) /* Bound the result. */ tcg_gen_movi_i64(t1, INT64_MAX); - t2 = tcg_const_i64(0); + t2 = tcg_constant_i64(0); tcg_gen_movcond_i64(TCG_COND_LT, reg, t0, t2, t1, reg); } - tcg_temp_free_i64(t2); + tcg_temp_free_i64(t1); } tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } /* Similarly with a vector and a scalar operand. */ From a7ac8e83aea003e69c22c205df43d75bb7c2f13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 30 Oct 2021 01:18:34 +0200 Subject: [PATCH 1145/1334] target/arm: Use tcg_constant_i32() in gen_rev16() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the mask is a constant value, use tcg_constant_i32() instead of a TCG temporary. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029231834.2476117-6-f4bug@amsat.org> Signed-off-by: Richard Henderson --- target/arm/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index 52ba562c96..98f5925928 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -388,13 +388,12 @@ static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b) void gen_rev16(TCGv_i32 dest, TCGv_i32 var) { TCGv_i32 tmp = tcg_temp_new_i32(); - TCGv_i32 mask = tcg_const_i32(0x00ff00ff); + TCGv_i32 mask = tcg_constant_i32(0x00ff00ff); tcg_gen_shri_i32(tmp, var, 8); tcg_gen_and_i32(tmp, tmp, mask); tcg_gen_and_i32(var, var, mask); tcg_gen_shli_i32(var, var, 8); tcg_gen_or_i32(dest, var, tmp); - tcg_temp_free_i32(mask); tcg_temp_free_i32(tmp); } From 9dad363a223df8269175d218413aa8cd265e078e Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Tue, 26 Oct 2021 10:37:34 +0100 Subject: [PATCH 1146/1334] hw/arm/virt: Rename default_bus_bypass_iommu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit d8fb7d0969d5 ("vl: switch -M parsing to keyval"), machine parameter definitions cannot use underscores, because keyval_dashify() transforms them to dashes and the parser doesn't find the parameter. This affects option default_bus_bypass_iommu which was introduced in the same release: $ qemu-system-aarch64 -M virt,default_bus_bypass_iommu=on qemu-system-aarch64: Property 'virt-6.1-machine.default-bus-bypass-iommu' not found Rename the parameter to "default-bus-bypass-iommu". Passing "default_bus_bypass_iommu" is still valid since the underscore are transformed automatically. Fixes: 6d7a85483a06 ("hw/arm/virt: Add default_bus_bypass_iommu machine option") Signed-off-by: Jean-Philippe Brucker Tested-by: Eric Auger Reviewed-by: Eric Auger Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211026093733.2144161-1-jean-philippe@linaro.org> Signed-off-by: Richard Henderson --- hw/arm/virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ca433adb5b..369552ad45 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2737,10 +2737,10 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) "Set the IOMMU type. " "Valid values are none and smmuv3"); - object_class_property_add_bool(oc, "default_bus_bypass_iommu", + object_class_property_add_bool(oc, "default-bus-bypass-iommu", virt_get_default_bus_bypass_iommu, virt_set_default_bus_bypass_iommu); - object_class_property_set_description(oc, "default_bus_bypass_iommu", + object_class_property_set_description(oc, "default-bus-bypass-iommu", "Set on/off to enable/disable " "bypass_iommu for default root bus"); From 5fd6a3e23669444026f84f466a7ed402c203a84e Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 26 Oct 2021 09:12:41 +0200 Subject: [PATCH 1147/1334] hvf: arm: Ignore cache operations on MMIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apple's Hypervisor.Framework forwards cache operations as MMIO traps into user space. For MMIO however, these have no meaning: There is no cache attached to them. So let's just treat cache data exits as nops. This fixes OpenBSD booting as guest. Reported-by: AJ Barris Signed-off-by: Alexander Graf Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Mark Kettenis Reference: https://github.com/utmapp/UTM/issues/3197 Message-Id: <20211026071241.74889-1-agraf@csgraf.de> Signed-off-by: Richard Henderson --- target/arm/hvf/hvf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index bff3e0cde7..0dc96560d3 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1150,12 +1150,19 @@ int hvf_vcpu_exec(CPUState *cpu) uint32_t sas = (syndrome >> 22) & 3; uint32_t len = 1 << sas; uint32_t srt = (syndrome >> 16) & 0x1f; + uint32_t cm = (syndrome >> 8) & 0x1; uint64_t val = 0; trace_hvf_data_abort(env->pc, hvf_exit->exception.virtual_address, hvf_exit->exception.physical_address, isv, iswrite, s1ptw, len, srt); + if (cm) { + /* We don't cache MMIO regions */ + advance_pc = true; + break; + } + assert(isv); if (iswrite) { From 835b04ed79b72913841bb04ba3302da388767b44 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Tue, 2 Nov 2021 16:52:19 -0600 Subject: [PATCH 1148/1334] bsd-user: Add stubs for new signal routines Until the signal support is merged from the bsd-user fork, we need stubs for cpu_loop_exit_sigsegv and cpu_loop_exit_sigbus to link. These call abort after logging a message. Since singals aren't supported here yet, this is sufficient. Signed-off-by: Warner Losh Message-Id: <20211102225248.52999-2-imp@bsdimp.com> Tested-by: Richard Henderson Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- bsd-user/signal.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 0c1093deb1..05b277c642 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -20,6 +20,11 @@ #include "qemu/osdep.h" #include "qemu.h" +/* + * Stubbed out routines until we merge signal support from bsd-user + * fork. + */ + /* * Queue a signal so that it will be send to the virtual CPU as soon as * possible. @@ -36,3 +41,19 @@ void signal_init(void) void process_pending_signals(CPUArchState *cpu_env) { } + +void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, bool maperr, uintptr_t ra) +{ + qemu_log_mask(LOG_UNIMP, "No signal support for SIGSEGV\n"); + /* unreachable */ + abort(); +} + +void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, uintptr_t ra) +{ + qemu_log_mask(LOG_UNIMP, "No signal support for SIGBUS\n"); + /* unreachable */ + abort(); +} From 458fecca80963b4c2c2164889d817542d2cece4f Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 29 Sep 2021 16:43:10 +0200 Subject: [PATCH 1149/1334] migration: provide an error message to migration_cancel() This avoids to call migrate_get_current() in the caller function whereas migration_cancel() already needs the pointer to the current migration state. Signed-off-by: Laurent Vivier Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 9 ++++++--- migration/migration.h | 2 +- migration/ram.c | 3 +-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 53b9a8af96..ec3d87f0a9 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -215,8 +215,11 @@ void migration_object_init(void) dirty_bitmap_mig_init(); } -void migration_cancel(void) +void migration_cancel(const Error *error) { + if (error) { + migrate_set_error(current_migration, error); + } migrate_fd_cancel(current_migration); } @@ -226,7 +229,7 @@ void migration_shutdown(void) * Cancel the current migration - that will (eventually) * stop the migration using this structure */ - migration_cancel(); + migration_cancel(NULL); object_unref(OBJECT(current_migration)); /* @@ -2334,7 +2337,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, void qmp_migrate_cancel(Error **errp) { - migration_cancel(); + migration_cancel(NULL); } void qmp_migrate_continue(MigrationStatus state, Error **errp) diff --git a/migration/migration.h b/migration/migration.h index 7a5aa8c2fd..8130b703eb 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -388,7 +388,7 @@ int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); void migration_make_urgent_request(void); void migration_consume_urgent_request(void); bool migration_rate_limit(void); -void migration_cancel(void); +void migration_cancel(const Error *error); void populate_vfio_info(MigrationInfo *info); diff --git a/migration/ram.c b/migration/ram.c index 680a5158aa..f5d39db4e4 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4323,9 +4323,8 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, * Abort and indicate a proper reason. */ error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr); - migrate_set_error(migrate_get_current(), err); + migration_cancel(err); error_free(err); - migration_cancel(); } switch (ps) { From fa0b31d585b66e0a9e6e9e72edf729ad2f10e6fd Mon Sep 17 00:00:00 2001 From: yuxiating Date: Thu, 2 Sep 2021 19:51:17 +0800 Subject: [PATCH 1150/1334] migration: initialise compression_counters for a new migration If the compression migration fails or is canceled, the query for the value of compression_counters during the next compression migration is wrong. Signed-off-by: yuxiating Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index ec3d87f0a9..edc0dac80a 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2271,10 +2271,11 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, migrate_init(s); /* - * set ram_counters memory to zero for a + * set ram_counters compression_counters memory to zero for a * new migration */ memset(&ram_counters, 0, sizeof(ram_counters)); + memset(&compression_counters, 0, sizeof(compression_counters)); return true; } From 02abee3d5129e1430af35ba4a174980a5a22d64e Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 2 Nov 2021 16:43:52 +0100 Subject: [PATCH 1151/1334] migration: Zero migration compression counters Based on previous patch from yuxiating Signed-off-by: Juan Quintela --- migration/savevm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/savevm.c b/migration/savevm.c index 7b7b64bd13..d59e976d50 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1567,6 +1567,7 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) migrate_init(ms); memset(&ram_counters, 0, sizeof(ram_counters)); + memset(&compression_counters, 0, sizeof(compression_counters)); ms->to_dst_file = f; qemu_mutex_unlock_iothread(); From ae4c2099351a6ea5774428a0884ace32595b341c Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Mon, 1 Nov 2021 15:56:59 +0800 Subject: [PATCH 1152/1334] Some minor optimizations for COLO Signed-off-by: Lei Rao Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/colo.c | 2 +- net/colo-compare.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index 79fa1f6619..616dc00af7 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -152,7 +152,7 @@ static void primary_vm_do_failover(void) * kick COLO thread which might wait at * qemu_sem_wait(&s->colo_checkpoint_sem). */ - colo_checkpoint_notify(migrate_get_current()); + colo_checkpoint_notify(s); /* * Wake up COLO thread which may blocked in recv() or send(), diff --git a/net/colo-compare.c b/net/colo-compare.c index b100e7b51f..4a64a5d386 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -170,7 +170,7 @@ static bool packet_matches_str(const char *str, return false; } - return !memcmp(str, buf, strlen(str)); + return !memcmp(str, buf, packet_len); } static void notify_remote_frame(CompareState *s) From aa505f8e0e04ddb404c00b907b176a6204cbb3c7 Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Mon, 1 Nov 2021 15:57:00 +0800 Subject: [PATCH 1153/1334] Fixed qemu crash when guest power off in COLO mode This patch fixes the following: qemu-system-x86_64: invalid runstate transition: 'shutdown' -> 'running' Aborted (core dumped) The gdb bt as following: 0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 1 0x00007faa3d613859 in __GI_abort () at abort.c:79 2 0x000055c5a21268fd in runstate_set (new_state=RUN_STATE_RUNNING) at vl.c:723 3 0x000055c5a1f8cae4 in vm_prepare_start () at /home/workspace/colo-qemu/cpus.c:2206 4 0x000055c5a1f8cb1b in vm_start () at /home/workspace/colo-qemu/cpus.c:2213 5 0x000055c5a2332bba in migration_iteration_finish (s=0x55c5a4658810) at migration/migration.c:3376 6 0x000055c5a2332f3b in migration_thread (opaque=0x55c5a4658810) at migration/migration.c:3527 7 0x000055c5a251d68a in qemu_thread_start (args=0x55c5a5491a70) at util/qemu-thread-posix.c:519 8 0x00007faa3d7e9609 in start_thread (arg=) at pthread_create.c:477 9 0x00007faa3d710293 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 Signed-off-by: Lei Rao Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index edc0dac80a..3fb856f6e1 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3626,7 +3626,9 @@ static void migration_iteration_finish(MigrationState *s) case MIGRATION_STATUS_CANCELLED: case MIGRATION_STATUS_CANCELLING: if (s->vm_was_running) { - vm_start(); + if (!runstate_check(RUN_STATE_SHUTDOWN)) { + vm_start(); + } } else { if (runstate_check(RUN_STATE_FINISH_MIGRATE)) { runstate_set(RUN_STATE_POSTMIGRATE); From 684bfd1820d45435ee4956e5a3ed231121b36634 Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Mon, 1 Nov 2021 15:57:01 +0800 Subject: [PATCH 1154/1334] Fixed SVM hang when do failover before PVM crash This patch fixed as follows: Thread 1 (Thread 0x7f34ee738d80 (LWP 11212)): #0 __pthread_clockjoin_ex (threadid=139847152957184, thread_return=0x7f30b1febf30, clockid=, abstime=, block=) at pthread_join_common.c:145 #1 0x0000563401998e36 in qemu_thread_join (thread=0x563402d66610) at util/qemu-thread-posix.c:587 #2 0x00005634017a79fa in process_incoming_migration_co (opaque=0x0) at migration/migration.c:502 #3 0x00005634019b59c9 in coroutine_trampoline (i0=63395504, i1=22068) at util/coroutine-ucontext.c:115 #4 0x00007f34ef860660 in ?? () at ../sysdeps/unix/sysv/linux/x86_64/__start_context.S:91 from /lib/x86_64-linux-gnu/libc.so.6 #5 0x00007f30b21ee730 in ?? () #6 0x0000000000000000 in ?? () Thread 13 (Thread 0x7f30b3dff700 (LWP 11747)): #0 __lll_lock_wait (futex=futex@entry=0x56340218ffa0 , private=0) at lowlevellock.c:52 #1 0x00007f34efa000a3 in _GI__pthread_mutex_lock (mutex=0x56340218ffa0 ) at ../nptl/pthread_mutex_lock.c:80 #2 0x0000563401997f99 in qemu_mutex_lock_impl (mutex=0x56340218ffa0 , file=0x563401b7a80e "migration/colo.c", line=806) at util/qemu-thread-posix.c:78 #3 0x0000563401407144 in qemu_mutex_lock_iothread_impl (file=0x563401b7a80e "migration/colo.c", line=806) at /home/workspace/colo-qemu/cpus.c:1899 #4 0x00005634017ba8e8 in colo_process_incoming_thread (opaque=0x563402d664c0) at migration/colo.c:806 #5 0x0000563401998b72 in qemu_thread_start (args=0x5634039f8370) at util/qemu-thread-posix.c:519 #6 0x00007f34ef9fd609 in start_thread (arg=) at pthread_create.c:477 #7 0x00007f34ef924293 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 The QEMU main thread is holding the lock: (gdb) p qemu_global_mutex $1 = {lock = {_data = {lock = 2, __count = 0, __owner = 11212, __nusers = 9, __kind = 0, __spins = 0, __elision = 0, __list = {_prev = 0x0, __next = 0x0}}, __size = "\002\000\000\000\000\000\000\000\314+\000\000\t", '\000' , __align = 2}, file = 0x563401c07e4b "util/main-loop.c", line = 240, initialized = true} >From the call trace, we can see it is a deadlock bug. and the QEMU main thread holds the global mutex to wait until the COLO thread ends. and the colo thread wants to acquire the global mutex, which will cause a deadlock. So, we should release the qemu_global_mutex before waiting colo thread ends. Signed-off-by: Lei Rao Reviewed-by: Li Zhijian Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index 3fb856f6e1..abaf6f9e3d 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -590,8 +590,10 @@ static void process_incoming_migration_co(void *opaque) mis->have_colo_incoming_thread = true; qemu_coroutine_yield(); + qemu_mutex_unlock_iothread(); /* Wait checkpoint incoming thread exit before free resource */ qemu_thread_join(&mis->colo_incoming_thread); + qemu_mutex_lock_iothread(); /* We hold the global iothread lock, so it is safe here */ colo_release_ram_cache(); } From ac183dac96603005ef2985d0e2ea2eb84fe2e03b Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Mon, 1 Nov 2021 15:57:02 +0800 Subject: [PATCH 1155/1334] colo: fixed 'Segmentation fault' when the simplex mode PVM poweroff The GDB statck is as follows: Program terminated with signal SIGSEGV, Segmentation fault. 0 object_class_dynamic_cast (class=0x55c8f5d2bf50, typename=0x55c8f2f7379e "qio-channel") at qom/object.c:832 if (type->class->interfaces && [Current thread is 1 (Thread 0x7f756e97eb00 (LWP 1811577))] (gdb) bt 0 object_class_dynamic_cast (class=0x55c8f5d2bf50, typename=0x55c8f2f7379e "qio-channel") at qom/object.c:832 1 0x000055c8f2c3dd14 in object_dynamic_cast (obj=0x55c8f543ac00, typename=0x55c8f2f7379e "qio-channel") at qom/object.c:763 2 0x000055c8f2c3ddce in object_dynamic_cast_assert (obj=0x55c8f543ac00, typename=0x55c8f2f7379e "qio-channel", file=0x55c8f2f73780 "migration/qemu-file-channel.c", line=117, func=0x55c8f2f73800 <__func__.18724> "channel_shutdown") at qom/object.c:786 3 0x000055c8f2bbc6ac in channel_shutdown (opaque=0x55c8f543ac00, rd=true, wr=true, errp=0x0) at migration/qemu-file-channel.c:117 4 0x000055c8f2bba56e in qemu_file_shutdown (f=0x7f7558070f50) at migration/qemu-file.c:67 5 0x000055c8f2ba5373 in migrate_fd_cancel (s=0x55c8f4ccf3f0) at migration/migration.c:1699 6 0x000055c8f2ba1992 in migration_shutdown () at migration/migration.c:187 7 0x000055c8f29a5b77 in main (argc=69, argv=0x7fff3e9e8c08, envp=0x7fff3e9e8e38) at vl.c:4512 The root cause is that we still want to shutdown the from_dst_file in migrate_fd_cancel() after qemu_close in colo_process_checkpoint(). So, we should set the s->rp_state.from_dst_file = NULL after qemu_close(). Signed-off-by: Lei Rao Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/colo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/colo.c b/migration/colo.c index 616dc00af7..907241ab5c 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -640,6 +640,7 @@ out: */ if (s->rp_state.from_dst_file) { qemu_fclose(s->rp_state.from_dst_file); + s->rp_state.from_dst_file = NULL; } } From 04dd89169b94789aacde0a9b29943e8614879343 Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Mon, 1 Nov 2021 15:57:03 +0800 Subject: [PATCH 1156/1334] Removed the qemu_fclose() in colo_process_incoming_thread After the live migration, the related fd will be cleanup in migration_incoming_state_destroy(). So, the qemu_close() in colo_process_incoming_thread is not necessary. Signed-off-by: Lei Rao Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/colo.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index 907241ab5c..71fc82a040 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -919,11 +919,6 @@ out: /* Hope this not to be too long to loop here */ qemu_sem_wait(&mis->colo_incoming_sem); qemu_sem_destroy(&mis->colo_incoming_sem); - /* Must be called after failover BH is completed */ - if (mis->to_src_file) { - qemu_fclose(mis->to_src_file); - mis->to_src_file = NULL; - } rcu_unregister_thread(); return NULL; From 2b9f6bf36c7483a07e45cc20cb0bb794769fb6d1 Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Mon, 1 Nov 2021 15:57:04 +0800 Subject: [PATCH 1157/1334] Changed the last-mode to none of first start COLO When we first stated the COLO, the last-mode is as follows: { "execute": "query-colo-status" } {"return": {"last-mode": "primary", "mode": "primary", "reason": "none"}} The last-mode is unreasonable. After the patch, will be changed to the following: { "execute": "query-colo-status" } {"return": {"last-mode": "none", "mode": "primary", "reason": "none"}} Signed-off-by: Lei Rao Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/colo.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index 71fc82a040..e3b1f136f4 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -205,7 +205,7 @@ void colo_do_failover(void) vm_stop_force_state(RUN_STATE_COLO); } - switch (get_colo_mode()) { + switch (last_colo_mode = get_colo_mode()) { case COLO_MODE_PRIMARY: primary_vm_do_failover(); break; @@ -530,8 +530,7 @@ static void colo_process_checkpoint(MigrationState *s) Error *local_err = NULL; int ret; - last_colo_mode = get_colo_mode(); - if (last_colo_mode != COLO_MODE_PRIMARY) { + if (get_colo_mode() != COLO_MODE_PRIMARY) { error_report("COLO mode must be COLO_MODE_PRIMARY"); return; } @@ -830,8 +829,7 @@ void *colo_process_incoming_thread(void *opaque) migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_COLO); - last_colo_mode = get_colo_mode(); - if (last_colo_mode != COLO_MODE_SECONDARY) { + if (get_colo_mode() != COLO_MODE_SECONDARY) { error_report("COLO mode must be COLO_MODE_SECONDARY"); return NULL; } From e5fdf920964b65678798960d8b3a55453c2e9094 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Sun, 4 Jul 2021 18:14:44 +0200 Subject: [PATCH 1158/1334] colo: Don't dump colo cache if dump-guest-core=off One might set dump-guest-core=off to make coredumps smaller and still allow to debug many qemu bugs. Extend this option to the colo cache. Signed-off-by: Lukas Straub Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/migration/ram.c b/migration/ram.c index f5d39db4e4..847af461f2 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -56,6 +56,8 @@ #include "multifd.h" #include "sysemu/runstate.h" +#include "hw/boards.h" /* for machine_dump_guest_core() */ + #if defined(__linux__) #include "qemu/userfaultfd.h" #endif /* defined(__linux__) */ @@ -3542,6 +3544,10 @@ int colo_init_ram_cache(void) } return -errno; } + if (!machine_dump_guest_core(current_machine)) { + qemu_madvise(block->colo_cache, block->used_length, + QEMU_MADV_DONTDUMP); + } } } From 64153ca613d0a50d1301eae4bd895aade001fcca Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Wed, 3 Nov 2021 10:21:12 +0800 Subject: [PATCH 1159/1334] Optimized the function of fill_connection_key. Remove some unnecessary code to improve the performance of the filter-rewriter module. Signed-off-by: Lei Rao Reviewed-by: Zhang Chen Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- net/colo-compare.c | 2 +- net/colo.c | 31 ++++++++++++------------------- net/colo.h | 6 +++--- net/filter-rewriter.c | 10 +--------- 4 files changed, 17 insertions(+), 32 deletions(-) diff --git a/net/colo-compare.c b/net/colo-compare.c index 4a64a5d386..b8876d7fd9 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -264,7 +264,7 @@ static int packet_enqueue(CompareState *s, int mode, Connection **con) pkt = NULL; return -1; } - fill_connection_key(pkt, &key); + fill_connection_key(pkt, &key, false); conn = connection_get(s->connection_track_table, &key, diff --git a/net/colo.c b/net/colo.c index 3a3e6e89a0..1f8162f59f 100644 --- a/net/colo.c +++ b/net/colo.c @@ -83,19 +83,26 @@ int parse_packet_early(Packet *pkt) return 0; } -void extract_ip_and_port(uint32_t tmp_ports, ConnectionKey *key, Packet *pkt) +void extract_ip_and_port(uint32_t tmp_ports, ConnectionKey *key, + Packet *pkt, bool reverse) { + if (reverse) { + key->src = pkt->ip->ip_dst; + key->dst = pkt->ip->ip_src; + key->src_port = ntohs(tmp_ports & 0xffff); + key->dst_port = ntohs(tmp_ports >> 16); + } else { key->src = pkt->ip->ip_src; key->dst = pkt->ip->ip_dst; key->src_port = ntohs(tmp_ports >> 16); key->dst_port = ntohs(tmp_ports & 0xffff); + } } -void fill_connection_key(Packet *pkt, ConnectionKey *key) +void fill_connection_key(Packet *pkt, ConnectionKey *key, bool reverse) { - uint32_t tmp_ports; + uint32_t tmp_ports = 0; - memset(key, 0, sizeof(*key)); key->ip_proto = pkt->ip->ip_p; switch (key->ip_proto) { @@ -106,29 +113,15 @@ void fill_connection_key(Packet *pkt, ConnectionKey *key) case IPPROTO_SCTP: case IPPROTO_UDPLITE: tmp_ports = *(uint32_t *)(pkt->transport_header); - extract_ip_and_port(tmp_ports, key, pkt); break; case IPPROTO_AH: tmp_ports = *(uint32_t *)(pkt->transport_header + 4); - extract_ip_and_port(tmp_ports, key, pkt); break; default: break; } -} -void reverse_connection_key(ConnectionKey *key) -{ - struct in_addr tmp_ip; - uint16_t tmp_port; - - tmp_ip = key->src; - key->src = key->dst; - key->dst = tmp_ip; - - tmp_port = key->src_port; - key->src_port = key->dst_port; - key->dst_port = tmp_port; + extract_ip_and_port(tmp_ports, key, pkt, reverse); } Connection *connection_new(ConnectionKey *key) diff --git a/net/colo.h b/net/colo.h index d91cd245c4..8b3e8d5a83 100644 --- a/net/colo.h +++ b/net/colo.h @@ -89,9 +89,9 @@ typedef struct Connection { uint32_t connection_key_hash(const void *opaque); int connection_key_equal(const void *opaque1, const void *opaque2); int parse_packet_early(Packet *pkt); -void extract_ip_and_port(uint32_t tmp_ports, ConnectionKey *key, Packet *pkt); -void fill_connection_key(Packet *pkt, ConnectionKey *key); -void reverse_connection_key(ConnectionKey *key); +void extract_ip_and_port(uint32_t tmp_ports, ConnectionKey *key, + Packet *pkt, bool reverse); +void fill_connection_key(Packet *pkt, ConnectionKey *key, bool reverse); Connection *connection_new(ConnectionKey *key); void connection_destroy(void *opaque); Connection *connection_get(GHashTable *connection_track_table, diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index cb3a96cde1..bf05023dc3 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -279,15 +279,7 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, */ if (pkt && is_tcp_packet(pkt)) { - fill_connection_key(pkt, &key); - - if (sender == nf->netdev) { - /* - * We need make tcp TX and RX packet - * into one connection. - */ - reverse_connection_key(&key); - } + fill_connection_key(pkt, &key, sender == nf->netdev); /* After failover we needn't change new TCP packet */ if (s->failover_mode && From 375bcf389f1e5f8c3bde70b3f8c847fdd73074c0 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 10 Mar 2021 20:08:48 -0600 Subject: [PATCH 1160/1334] Hexagon HVX (target/hexagon) README Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/README | 81 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/target/hexagon/README b/target/hexagon/README index b0b2435070..372e24747c 100644 --- a/target/hexagon/README +++ b/target/hexagon/README @@ -1,9 +1,13 @@ Hexagon is Qualcomm's very long instruction word (VLIW) digital signal -processor(DSP). +processor(DSP). We also support Hexagon Vector eXtensions (HVX). HVX +is a wide vector coprocessor designed for high performance computer vision, +image processing, machine learning, and other workloads. The following versions of the Hexagon core are supported Scalar core: v67 https://developer.qualcomm.com/downloads/qualcomm-hexagon-v67-programmer-s-reference-manual + HVX extension: v66 + https://developer.qualcomm.com/downloads/qualcomm-hexagon-v66-hvx-programmer-s-reference-manual We presented an overview of the project at the 2019 KVM Forum. https://kvmforum2019.sched.com/event/Tmwc/qemu-hexagon-automatic-translation-of-the-isa-manual-pseudcode-to-tiny-code-instructions-of-a-vliw-architecture-niccolo-izzo-revng-taylor-simpson-qualcomm-innovation-center @@ -124,6 +128,71 @@ There are also cases where we brute force the TCG code generation. Instructions with multiple definitions are examples. These require special handling because qemu helpers can only return a single value. +For HVX vectors, the generator behaves slightly differently. The wide vectors +won't fit in a TCGv or TCGv_i64, so we pass TCGv_ptr variables to pass the +address to helper functions. Here's an example for an HVX vector-add-word +istruction. + static void generate_V6_vaddw( + CPUHexagonState *env, + DisasContext *ctx, + Insn *insn, + Packet *pkt) + { + const int VdN = insn->regno[0]; + const intptr_t VdV_off = + ctx_future_vreg_off(ctx, VdN, 1, true); + TCGv_ptr VdV = tcg_temp_local_new_ptr(); + tcg_gen_addi_ptr(VdV, cpu_env, VdV_off); + const int VuN = insn->regno[1]; + const intptr_t VuV_off = + vreg_src_off(ctx, VuN); + TCGv_ptr VuV = tcg_temp_local_new_ptr(); + const int VvN = insn->regno[2]; + const intptr_t VvV_off = + vreg_src_off(ctx, VvN); + TCGv_ptr VvV = tcg_temp_local_new_ptr(); + tcg_gen_addi_ptr(VuV, cpu_env, VuV_off); + tcg_gen_addi_ptr(VvV, cpu_env, VvV_off); + TCGv slot = tcg_constant_tl(insn->slot); + gen_helper_V6_vaddw(cpu_env, VdV, VuV, VvV, slot); + tcg_temp_free(slot); + gen_log_vreg_write(ctx, VdV_off, VdN, EXT_DFL, insn->slot, false); + ctx_log_vreg_write(ctx, VdN, EXT_DFL, false); + tcg_temp_free_ptr(VdV); + tcg_temp_free_ptr(VuV); + tcg_temp_free_ptr(VvV); + } + +Notice that we also generate a variable named _off for each operand of +the instruction. This makes it easy to override the instruction semantics with +functions from tcg-op-gvec.h. Here's the override for this instruction. + #define fGEN_TCG_V6_vaddw(SHORTCODE) \ + tcg_gen_gvec_add(MO_32, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +Finally, we notice that the override doesn't use the TCGv_ptr variables, so +we don't generate them when an override is present. Here is what we generate +when the override is present. + static void generate_V6_vaddw( + CPUHexagonState *env, + DisasContext *ctx, + Insn *insn, + Packet *pkt) + { + const int VdN = insn->regno[0]; + const intptr_t VdV_off = + ctx_future_vreg_off(ctx, VdN, 1, true); + const int VuN = insn->regno[1]; + const intptr_t VuV_off = + vreg_src_off(ctx, VuN); + const int VvN = insn->regno[2]; + const intptr_t VvV_off = + vreg_src_off(ctx, VvN); + fGEN_TCG_V6_vaddw({ fHIDE(int i;) fVFOREACH(32, i) { VdV.w[i] = VuV.w[i] + VvV.w[i] ; } }); + gen_log_vreg_write(ctx, VdV_off, VdN, EXT_DFL, insn->slot, false); + ctx_log_vreg_write(ctx, VdN, EXT_DFL, false); + } + In addition to instruction semantics, we use a generator to create the decode tree. This generation is also a two step process. The first step is to run target/hexagon/gen_dectree_import.c to produce @@ -140,6 +209,7 @@ runtime information for each thread and contains stuff like the GPR and predicate registers. macros.h +mmvec/macros.h The Hexagon arch lib relies heavily on macros for the instruction semantics. This is a great advantage for qemu because we can override them for different @@ -203,6 +273,15 @@ During runtime, the following fields in CPUHexagonState (see cpu.h) are used pred_written boolean indicating if predicate was written mem_log_stores record of the stores (indexed by slot) +For Hexagon Vector eXtensions (HVX), the following fields are used + VRegs Vector registers + future_VRegs Registers to be stored during packet commit + tmp_VRegs Temporary registers *not* stored during commit + VRegs_updated Mask of predicated vector writes + QRegs Q (vector predicate) registers + future_QRegs Registers to be stored during packet commit + QRegs_updated Mask of predicated vector writes + *** Debugging *** You can turn on a lot of debugging by changing the HEX_DEBUG macro to 1 in From a1559537d183bf1f4e2cfef972610c8c9e1a6aa5 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 17 Mar 2021 11:48:57 -0500 Subject: [PATCH 1161/1334] Hexagon HVX (target/hexagon) add Hexagon Vector eXtensions (HVX) to core HVX is a set of wide vector instructions. Machine state includes vector registers (VRegs) vector predicate registers (QRegs) temporary registers for intermediate values store buffer (masked stores and scatter/gather) Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/cpu.c | 78 +++++++++++++++++++++++++++++-- target/hexagon/cpu.h | 35 +++++++++++++- target/hexagon/hex_arch_types.h | 5 ++ target/hexagon/insn.h | 3 ++ target/hexagon/internal.h | 3 ++ target/hexagon/mmvec/mmvec.h | 82 +++++++++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 target/hexagon/mmvec/mmvec.h diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 3338365c16..989bd767b5 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -113,7 +113,66 @@ static void print_reg(FILE *f, CPUHexagonState *env, int regnum) hexagon_regnames[regnum], value); } -static void hexagon_dump(CPUHexagonState *env, FILE *f) +static void print_vreg(FILE *f, CPUHexagonState *env, int regnum, + bool skip_if_zero) +{ + if (skip_if_zero) { + bool nonzero_found = false; + for (int i = 0; i < MAX_VEC_SIZE_BYTES; i++) { + if (env->VRegs[regnum].ub[i] != 0) { + nonzero_found = true; + break; + } + } + if (!nonzero_found) { + return; + } + } + + qemu_fprintf(f, " v%d = ( ", regnum); + qemu_fprintf(f, "0x%02x", env->VRegs[regnum].ub[MAX_VEC_SIZE_BYTES - 1]); + for (int i = MAX_VEC_SIZE_BYTES - 2; i >= 0; i--) { + qemu_fprintf(f, ", 0x%02x", env->VRegs[regnum].ub[i]); + } + qemu_fprintf(f, " )\n"); +} + +void hexagon_debug_vreg(CPUHexagonState *env, int regnum) +{ + print_vreg(stdout, env, regnum, false); +} + +static void print_qreg(FILE *f, CPUHexagonState *env, int regnum, + bool skip_if_zero) +{ + if (skip_if_zero) { + bool nonzero_found = false; + for (int i = 0; i < MAX_VEC_SIZE_BYTES / 8; i++) { + if (env->QRegs[regnum].ub[i] != 0) { + nonzero_found = true; + break; + } + } + if (!nonzero_found) { + return; + } + } + + qemu_fprintf(f, " q%d = ( ", regnum); + qemu_fprintf(f, "0x%02x", + env->QRegs[regnum].ub[MAX_VEC_SIZE_BYTES / 8 - 1]); + for (int i = MAX_VEC_SIZE_BYTES / 8 - 2; i >= 0; i--) { + qemu_fprintf(f, ", 0x%02x", env->QRegs[regnum].ub[i]); + } + qemu_fprintf(f, " )\n"); +} + +void hexagon_debug_qreg(CPUHexagonState *env, int regnum) +{ + print_qreg(stdout, env, regnum, false); +} + +static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags) { HexagonCPU *cpu = env_archcpu(env); @@ -159,6 +218,17 @@ static void hexagon_dump(CPUHexagonState *env, FILE *f) print_reg(f, env, HEX_REG_CS1); #endif qemu_fprintf(f, "}\n"); + + if (flags & CPU_DUMP_FPU) { + qemu_fprintf(f, "Vector Registers = {\n"); + for (int i = 0; i < NUM_VREGS; i++) { + print_vreg(f, env, i, true); + } + for (int i = 0; i < NUM_QREGS; i++) { + print_qreg(f, env, i, true); + } + qemu_fprintf(f, "}\n"); + } } static void hexagon_dump_state(CPUState *cs, FILE *f, int flags) @@ -166,12 +236,12 @@ static void hexagon_dump_state(CPUState *cs, FILE *f, int flags) HexagonCPU *cpu = HEXAGON_CPU(cs); CPUHexagonState *env = &cpu->env; - hexagon_dump(env, f); + hexagon_dump(env, f, flags); } void hexagon_debug(CPUHexagonState *env) { - hexagon_dump(env, stdout); + hexagon_dump(env, stdout, CPU_DUMP_FPU); } static void hexagon_cpu_set_pc(CPUState *cs, vaddr value) @@ -292,7 +362,7 @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data) cc->set_pc = hexagon_cpu_set_pc; cc->gdb_read_register = hexagon_gdb_read_register; cc->gdb_write_register = hexagon_gdb_write_register; - cc->gdb_num_core_regs = TOTAL_PER_THREAD_REGS; + cc->gdb_num_core_regs = TOTAL_PER_THREAD_REGS + NUM_VREGS + NUM_QREGS; cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = hexagon_cpu_disas_set_info; cc->tcg_ops = &hexagon_tcg_ops; diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index f90c187888..de121d950f 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -26,6 +26,7 @@ typedef struct CPUHexagonState CPUHexagonState; #include "qemu-common.h" #include "exec/cpu-defs.h" #include "hex_regs.h" +#include "mmvec/mmvec.h" #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -34,6 +35,7 @@ typedef struct CPUHexagonState CPUHexagonState; #define STORES_MAX 2 #define REG_WRITES_MAX 32 #define PRED_WRITES_MAX 5 /* 4 insns + endloop */ +#define VSTORES_MAX 2 #define TYPE_HEXAGON_CPU "hexagon-cpu" @@ -52,6 +54,13 @@ typedef struct { uint64_t data64; } MemLog; +typedef struct { + target_ulong va; + int size; + DECLARE_BITMAP(mask, MAX_VEC_SIZE_BYTES) QEMU_ALIGNED(16); + MMVector data QEMU_ALIGNED(16); +} VStoreLog; + #define EXEC_STATUS_OK 0x0000 #define EXEC_STATUS_STOP 0x0002 #define EXEC_STATUS_REPLAY 0x0010 @@ -64,6 +73,9 @@ typedef struct { #define CLEAR_EXCEPTION (env->status &= (~EXEC_STATUS_EXCEPTION)) #define SET_EXCEPTION (env->status |= EXEC_STATUS_EXCEPTION) +/* Maximum number of vector temps in a packet */ +#define VECTOR_TEMPS_MAX 4 + struct CPUHexagonState { target_ulong gpr[TOTAL_PER_THREAD_REGS]; target_ulong pred[NUM_PREGS]; @@ -97,8 +109,27 @@ struct CPUHexagonState { target_ulong llsc_val; uint64_t llsc_val_i64; - target_ulong is_gather_store_insn; - target_ulong gather_issued; + MMVector VRegs[NUM_VREGS] QEMU_ALIGNED(16); + MMVector future_VRegs[VECTOR_TEMPS_MAX] QEMU_ALIGNED(16); + MMVector tmp_VRegs[VECTOR_TEMPS_MAX] QEMU_ALIGNED(16); + + VRegMask VRegs_updated; + + MMQReg QRegs[NUM_QREGS] QEMU_ALIGNED(16); + MMQReg future_QRegs[NUM_QREGS] QEMU_ALIGNED(16); + QRegMask QRegs_updated; + + /* Temporaries used within instructions */ + MMVectorPair VuuV QEMU_ALIGNED(16); + MMVectorPair VvvV QEMU_ALIGNED(16); + MMVectorPair VxxV QEMU_ALIGNED(16); + MMVector vtmp QEMU_ALIGNED(16); + MMQReg qtmp QEMU_ALIGNED(16); + + VStoreLog vstore[VSTORES_MAX]; + target_ulong vstore_pending[VSTORES_MAX]; + bool vtcm_pending; + VTCMStoreLog vtcm_log; }; #define HEXAGON_CPU_CLASS(klass) \ diff --git a/target/hexagon/hex_arch_types.h b/target/hexagon/hex_arch_types.h index d721e1f934..78ad607f53 100644 --- a/target/hexagon/hex_arch_types.h +++ b/target/hexagon/hex_arch_types.h @@ -19,6 +19,7 @@ #define HEXAGON_ARCH_TYPES_H #include "qemu/osdep.h" +#include "mmvec/mmvec.h" #include "qemu/int128.h" /* @@ -35,4 +36,8 @@ typedef uint64_t size8u_t; typedef int64_t size8s_t; typedef Int128 size16s_t; +typedef MMVector mmvector_t; +typedef MMVectorPair mmvector_pair_t; +typedef MMQReg mmqret_t; + #endif diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h index 2e345912a8..aa26389147 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.h @@ -67,6 +67,9 @@ struct Packet { bool pkt_has_store_s0; bool pkt_has_store_s1; + bool pkt_has_hvx; + Insn *vhist_insn; + Insn insn[INSTRUCTIONS_MAX]; }; diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h index 6b20affdfa..82ac3042ab 100644 --- a/target/hexagon/internal.h +++ b/target/hexagon/internal.h @@ -31,6 +31,9 @@ int hexagon_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hexagon_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +void hexagon_debug_vreg(CPUHexagonState *env, int regnum); +void hexagon_debug_qreg(CPUHexagonState *env, int regnum); void hexagon_debug(CPUHexagonState *env); extern const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS]; diff --git a/target/hexagon/mmvec/mmvec.h b/target/hexagon/mmvec/mmvec.h new file mode 100644 index 0000000000..52d470709c --- /dev/null +++ b/target/hexagon/mmvec/mmvec.h @@ -0,0 +1,82 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HEXAGON_MMVEC_H +#define HEXAGON_MMVEC_H + +#define MAX_VEC_SIZE_LOGBYTES 7 +#define MAX_VEC_SIZE_BYTES (1 << MAX_VEC_SIZE_LOGBYTES) + +#define NUM_VREGS 32 +#define NUM_QREGS 4 + +typedef uint32_t VRegMask; /* at least NUM_VREGS bits */ +typedef uint32_t QRegMask; /* at least NUM_QREGS bits */ + +#define VECTOR_SIZE_BYTE (fVECSIZE()) + +typedef union { + uint64_t ud[MAX_VEC_SIZE_BYTES / 8]; + int64_t d[MAX_VEC_SIZE_BYTES / 8]; + uint32_t uw[MAX_VEC_SIZE_BYTES / 4]; + int32_t w[MAX_VEC_SIZE_BYTES / 4]; + uint16_t uh[MAX_VEC_SIZE_BYTES / 2]; + int16_t h[MAX_VEC_SIZE_BYTES / 2]; + uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; + int8_t b[MAX_VEC_SIZE_BYTES / 1]; +} MMVector; + +typedef union { + uint64_t ud[2 * MAX_VEC_SIZE_BYTES / 8]; + int64_t d[2 * MAX_VEC_SIZE_BYTES / 8]; + uint32_t uw[2 * MAX_VEC_SIZE_BYTES / 4]; + int32_t w[2 * MAX_VEC_SIZE_BYTES / 4]; + uint16_t uh[2 * MAX_VEC_SIZE_BYTES / 2]; + int16_t h[2 * MAX_VEC_SIZE_BYTES / 2]; + uint8_t ub[2 * MAX_VEC_SIZE_BYTES / 1]; + int8_t b[2 * MAX_VEC_SIZE_BYTES / 1]; + MMVector v[2]; +} MMVectorPair; + +typedef union { + uint64_t ud[MAX_VEC_SIZE_BYTES / 8 / 8]; + int64_t d[MAX_VEC_SIZE_BYTES / 8 / 8]; + uint32_t uw[MAX_VEC_SIZE_BYTES / 4 / 8]; + int32_t w[MAX_VEC_SIZE_BYTES / 4 / 8]; + uint16_t uh[MAX_VEC_SIZE_BYTES / 2 / 8]; + int16_t h[MAX_VEC_SIZE_BYTES / 2 / 8]; + uint8_t ub[MAX_VEC_SIZE_BYTES / 1 / 8]; + int8_t b[MAX_VEC_SIZE_BYTES / 1 / 8]; +} MMQReg; + +typedef struct { + MMVector data; + DECLARE_BITMAP(mask, MAX_VEC_SIZE_BYTES); + target_ulong va[MAX_VEC_SIZE_BYTES]; + bool op; + int op_size; +} VTCMStoreLog; + + +/* Types of vector register assignment */ +typedef enum { + EXT_DFL, /* Default */ + EXT_NEW, /* New - value used in the same packet */ + EXT_TMP /* Temp - value used but not stored to register */ +} VRegWriteType; + +#endif From 40438b67071cd1547c7497d714bbb8b24e7bee5a Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Fri, 26 Feb 2021 03:30:12 -0800 Subject: [PATCH 1162/1334] Hexagon HVX (target/hexagon) register names Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/cpu.c | 2 +- target/hexagon/hex_regs.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 989bd767b5..3bd3f100dd 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -59,7 +59,7 @@ const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "sa0", "lc0", "sa1", "lc1", "p3_0", "c5", "m0", "m1", "usr", "pc", "ugp", "gp", "cs0", "cs1", "c14", "c15", - "c16", "c17", "c18", "c19", "pkt_cnt", "insn_cnt", "c22", "c23", + "c16", "c17", "c18", "c19", "pkt_cnt", "insn_cnt", "hvx_cnt", "c23", "c24", "c25", "c26", "c27", "c28", "c29", "c30", "c31", }; diff --git a/target/hexagon/hex_regs.h b/target/hexagon/hex_regs.h index f291911eef..e1b3149b07 100644 --- a/target/hexagon/hex_regs.h +++ b/target/hexagon/hex_regs.h @@ -76,6 +76,7 @@ enum { /* Use reserved control registers for qemu execution counts */ HEX_REG_QEMU_PKT_CNT = 52, HEX_REG_QEMU_INSN_CNT = 53, + HEX_REG_QEMU_HVX_CNT = 54, HEX_REG_UTIMERLO = 62, HEX_REG_UTIMERHI = 63, }; From 828a210785c5bae0180134dfb7265f044efc9e0f Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Fri, 26 Feb 2021 02:36:48 -0800 Subject: [PATCH 1163/1334] Hexagon HVX (target/hexagon) instruction attributes Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/attribs_def.h.inc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index e44a7ead16..dc890a557f 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -41,6 +41,27 @@ DEF_ATTRIB(STORE, "Stores to memory", "", "") DEF_ATTRIB(MEMLIKE, "Memory-like instruction", "", "") DEF_ATTRIB(MEMLIKE_PACKET_RULES, "follows Memory-like packet rules", "", "") +/* V6 Vector attributes */ +DEF_ATTRIB(CVI, "Executes on the HVX extension", "", "") + +DEF_ATTRIB(CVI_NEW, "New value memory instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_VM, "Memory instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_VP, "Permute instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_VP_VS, "Double vector permute/shft insn executes on HVX", "", "") +DEF_ATTRIB(CVI_VX, "Multiply instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_VX_DV, "Double vector multiply insn executes on HVX", "", "") +DEF_ATTRIB(CVI_VS, "Shift instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_VS_VX, "Permute/shift and multiply insn executes on HVX", "", "") +DEF_ATTRIB(CVI_VA, "ALU instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_VA_DV, "Double vector alu instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_4SLOT, "Consumes all the vector execution resources", "", "") +DEF_ATTRIB(CVI_TMP, "Transient Memory Load not written to register", "", "") +DEF_ATTRIB(CVI_GATHER, "CVI Gather operation", "", "") +DEF_ATTRIB(CVI_SCATTER, "CVI Scatter operation", "", "") +DEF_ATTRIB(CVI_SCATTER_RELEASE, "CVI Store Release for scatter", "", "") +DEF_ATTRIB(CVI_TMP_DST, "CVI instruction that doesn't write a register", "", "") +DEF_ATTRIB(CVI_SLOT23, "Can execute in slot 2 or slot 3 (HVX)", "", "") + /* Change-of-flow attributes */ DEF_ATTRIB(JUMP, "Jump-type instruction", "", "") @@ -87,6 +108,7 @@ DEF_ATTRIB(HWLOOP1_END, "Ends HW loop1", "", "") DEF_ATTRIB(DCZEROA, "dczeroa type", "", "") DEF_ATTRIB(ICFLUSHOP, "icflush op type", "", "") DEF_ATTRIB(DCFLUSHOP, "dcflush op type", "", "") +DEF_ATTRIB(L2FLUSHOP, "l2flush op type", "", "") DEF_ATTRIB(DCFETCH, "dcfetch type", "", "") DEF_ATTRIB(L2FETCH, "Instruction is l2fetch type", "", "") From 64458f4855302373229eb7ca1902b65a7578ab7d Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Sun, 7 Mar 2021 15:30:59 -0800 Subject: [PATCH 1164/1334] Hexagon HVX (target/hexagon) macros macros to interface with the generator macros referenced in instruction semantics Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/macros.h | 22 +++ target/hexagon/mmvec/macros.h | 354 ++++++++++++++++++++++++++++++++++ 2 files changed, 376 insertions(+) create mode 100644 target/hexagon/mmvec/macros.h diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 13e957b41d..19d103cad5 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -266,6 +266,10 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num) #define fNEWREG_ST(VAL) (VAL) +#define fVSATUVALN(N, VAL) \ + ({ \ + (((int)(VAL)) < 0) ? 0 : ((1LL << (N)) - 1); \ + }) #define fSATUVALN(N, VAL) \ ({ \ fSET_OVERFLOW(); \ @@ -276,10 +280,16 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num) fSET_OVERFLOW(); \ ((VAL) < 0) ? (-(1LL << ((N) - 1))) : ((1LL << ((N) - 1)) - 1); \ }) +#define fVSATVALN(N, VAL) \ + ({ \ + ((VAL) < 0) ? (-(1LL << ((N) - 1))) : ((1LL << ((N) - 1)) - 1); \ + }) #define fZXTN(N, M, VAL) (((N) != 0) ? extract64((VAL), 0, (N)) : 0LL) #define fSXTN(N, M, VAL) (((N) != 0) ? sextract64((VAL), 0, (N)) : 0LL) #define fSATN(N, VAL) \ ((fSXTN(N, 64, VAL) == (VAL)) ? (VAL) : fSATVALN(N, VAL)) +#define fVSATN(N, VAL) \ + ((fSXTN(N, 64, VAL) == (VAL)) ? (VAL) : fVSATVALN(N, VAL)) #define fADDSAT64(DST, A, B) \ do { \ uint64_t __a = fCAST8u(A); \ @@ -302,12 +312,18 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num) DST = __sum; \ } \ } while (0) +#define fVSATUN(N, VAL) \ + ((fZXTN(N, 64, VAL) == (VAL)) ? (VAL) : fVSATUVALN(N, VAL)) #define fSATUN(N, VAL) \ ((fZXTN(N, 64, VAL) == (VAL)) ? (VAL) : fSATUVALN(N, VAL)) #define fSATH(VAL) (fSATN(16, VAL)) #define fSATUH(VAL) (fSATUN(16, VAL)) +#define fVSATH(VAL) (fVSATN(16, VAL)) +#define fVSATUH(VAL) (fVSATUN(16, VAL)) #define fSATUB(VAL) (fSATUN(8, VAL)) #define fSATB(VAL) (fSATN(8, VAL)) +#define fVSATUB(VAL) (fVSATUN(8, VAL)) +#define fVSATB(VAL) (fVSATN(8, VAL)) #define fIMMEXT(IMM) (IMM = IMM) #define fMUST_IMMEXT(IMM) fIMMEXT(IMM) @@ -414,6 +430,8 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fCAST4s(A) ((int32_t)(A)) #define fCAST8u(A) ((uint64_t)(A)) #define fCAST8s(A) ((int64_t)(A)) +#define fCAST2_2s(A) ((int16_t)(A)) +#define fCAST2_2u(A) ((uint16_t)(A)) #define fCAST4_4s(A) ((int32_t)(A)) #define fCAST4_4u(A) ((uint32_t)(A)) #define fCAST4_8s(A) ((int64_t)((int32_t)(A))) @@ -510,7 +528,9 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fPM_M(REG, MVAL) do { REG = REG + (MVAL); } while (0) #endif #define fSCALE(N, A) (((int64_t)(A)) << N) +#define fVSATW(A) fVSATN(32, ((long long)A)) #define fSATW(A) fSATN(32, ((long long)A)) +#define fVSAT(A) fVSATN(32, (A)) #define fSAT(A) fSATN(32, (A)) #define fSAT_ORIG_SHL(A, ORIG_REG) \ ((((int32_t)((fSAT(A)) ^ ((int32_t)(ORIG_REG)))) < 0) \ @@ -647,12 +667,14 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) fSETBIT(j, DST, VAL); \ } \ } while (0) +#define fCOUNTONES_2(VAL) ctpop16(VAL) #define fCOUNTONES_4(VAL) ctpop32(VAL) #define fCOUNTONES_8(VAL) ctpop64(VAL) #define fBREV_8(VAL) revbit64(VAL) #define fBREV_4(VAL) revbit32(VAL) #define fCL1_8(VAL) clo64(VAL) #define fCL1_4(VAL) clo32(VAL) +#define fCL1_2(VAL) (clz32(~(uint16_t)(VAL) & 0xffff) - 16) #define fINTERLEAVE(ODD, EVEN) interleave(ODD, EVEN) #define fDEINTERLEAVE(MIXED) deinterleave(MIXED) #define fHIDE(A) A diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h new file mode 100644 index 0000000000..10f4630364 --- /dev/null +++ b/target/hexagon/mmvec/macros.h @@ -0,0 +1,354 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HEXAGON_MMVEC_MACROS_H +#define HEXAGON_MMVEC_MACROS_H + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "arch.h" +#include "mmvec/system_ext_mmvec.h" + +#ifndef QEMU_GENERATE +#define VdV (*(MMVector *)(VdV_void)) +#define VsV (*(MMVector *)(VsV_void)) +#define VuV (*(MMVector *)(VuV_void)) +#define VvV (*(MMVector *)(VvV_void)) +#define VwV (*(MMVector *)(VwV_void)) +#define VxV (*(MMVector *)(VxV_void)) +#define VyV (*(MMVector *)(VyV_void)) + +#define VddV (*(MMVectorPair *)(VddV_void)) +#define VuuV (*(MMVectorPair *)(VuuV_void)) +#define VvvV (*(MMVectorPair *)(VvvV_void)) +#define VxxV (*(MMVectorPair *)(VxxV_void)) + +#define QeV (*(MMQReg *)(QeV_void)) +#define QdV (*(MMQReg *)(QdV_void)) +#define QsV (*(MMQReg *)(QsV_void)) +#define QtV (*(MMQReg *)(QtV_void)) +#define QuV (*(MMQReg *)(QuV_void)) +#define QvV (*(MMQReg *)(QvV_void)) +#define QxV (*(MMQReg *)(QxV_void)) +#endif + +#define LOG_VTCM_BYTE(VA, MASK, VAL, IDX) \ + do { \ + env->vtcm_log.data.ub[IDX] = (VAL); \ + if (MASK) { \ + set_bit((IDX), env->vtcm_log.mask); \ + } else { \ + clear_bit((IDX), env->vtcm_log.mask); \ + } \ + env->vtcm_log.va[IDX] = (VA); \ + } while (0) + +#define fNOTQ(VAL) \ + ({ \ + MMQReg _ret; \ + int _i_; \ + for (_i_ = 0; _i_ < fVECSIZE() / 64; _i_++) { \ + _ret.ud[_i_] = ~VAL.ud[_i_]; \ + } \ + _ret;\ + }) +#define fGETQBITS(REG, WIDTH, MASK, BITNO) \ + ((MASK) & (REG.w[(BITNO) >> 5] >> ((BITNO) & 0x1f))) +#define fGETQBIT(REG, BITNO) fGETQBITS(REG, 1, 1, BITNO) +#define fGENMASKW(QREG, IDX) \ + (((fGETQBIT(QREG, (IDX * 4 + 0)) ? 0xFF : 0x0) << 0) | \ + ((fGETQBIT(QREG, (IDX * 4 + 1)) ? 0xFF : 0x0) << 8) | \ + ((fGETQBIT(QREG, (IDX * 4 + 2)) ? 0xFF : 0x0) << 16) | \ + ((fGETQBIT(QREG, (IDX * 4 + 3)) ? 0xFF : 0x0) << 24)) +#define fGETNIBBLE(IDX, SRC) (fSXTN(4, 8, (SRC >> (4 * IDX)) & 0xF)) +#define fGETCRUMB(IDX, SRC) (fSXTN(2, 8, (SRC >> (2 * IDX)) & 0x3)) +#define fGETCRUMB_SYMMETRIC(IDX, SRC) \ + ((fGETCRUMB(IDX, SRC) >= 0 ? (2 - fGETCRUMB(IDX, SRC)) \ + : fGETCRUMB(IDX, SRC))) +#define fGENMASKH(QREG, IDX) \ + (((fGETQBIT(QREG, (IDX * 2 + 0)) ? 0xFF : 0x0) << 0) | \ + ((fGETQBIT(QREG, (IDX * 2 + 1)) ? 0xFF : 0x0) << 8)) +#define fGETMASKW(VREG, QREG, IDX) (VREG.w[IDX] & fGENMASKW((QREG), IDX)) +#define fGETMASKH(VREG, QREG, IDX) (VREG.h[IDX] & fGENMASKH((QREG), IDX)) +#define fCONDMASK8(QREG, IDX, YESVAL, NOVAL) \ + (fGETQBIT(QREG, IDX) ? (YESVAL) : (NOVAL)) +#define fCONDMASK16(QREG, IDX, YESVAL, NOVAL) \ + ((fGENMASKH(QREG, IDX) & (YESVAL)) | \ + (fGENMASKH(fNOTQ(QREG), IDX) & (NOVAL))) +#define fCONDMASK32(QREG, IDX, YESVAL, NOVAL) \ + ((fGENMASKW(QREG, IDX) & (YESVAL)) | \ + (fGENMASKW(fNOTQ(QREG), IDX) & (NOVAL))) +#define fSETQBITS(REG, WIDTH, MASK, BITNO, VAL) \ + do { \ + uint32_t __TMP = (VAL); \ + REG.w[(BITNO) >> 5] &= ~((MASK) << ((BITNO) & 0x1f)); \ + REG.w[(BITNO) >> 5] |= (((__TMP) & (MASK)) << ((BITNO) & 0x1f)); \ + } while (0) +#define fSETQBIT(REG, BITNO, VAL) fSETQBITS(REG, 1, 1, BITNO, VAL) +#define fVBYTES() (fVECSIZE()) +#define fVALIGN(ADDR, LOG2_ALIGNMENT) (ADDR = ADDR & ~(LOG2_ALIGNMENT - 1)) +#define fVLASTBYTE(ADDR, LOG2_ALIGNMENT) (ADDR = ADDR | (LOG2_ALIGNMENT - 1)) +#define fVELEM(WIDTH) ((fVECSIZE() * 8) / WIDTH) +#define fVECLOGSIZE() (7) +#define fVECSIZE() (1 << fVECLOGSIZE()) +#define fSWAPB(A, B) do { uint8_t tmp = A; A = B; B = tmp; } while (0) +#define fV_AL_CHECK(EA, MASK) \ + if ((EA) & (MASK)) { \ + warn("aligning misaligned vector. EA=%08x", (EA)); \ + } +#define fSCATTER_INIT(REGION_START, LENGTH, ELEMENT_SIZE) \ + mem_vector_scatter_init(env) +#define fGATHER_INIT(REGION_START, LENGTH, ELEMENT_SIZE) \ + mem_vector_gather_init(env) +#define fSCATTER_FINISH(OP) +#define fGATHER_FINISH() +#define fLOG_SCATTER_OP(SIZE) \ + do { \ + env->vtcm_log.op = true; \ + env->vtcm_log.op_size = SIZE; \ + } while (0) +#define fVLOG_VTCM_WORD_INCREMENT(EA, OFFSET, INC, IDX, ALIGNMENT, LEN) \ + do { \ + int log_byte = 0; \ + target_ulong va = EA; \ + target_ulong va_high = EA + LEN; \ + for (int i0 = 0; i0 < 4; i0++) { \ + log_byte = (va + i0) <= va_high; \ + LOG_VTCM_BYTE(va + i0, log_byte, INC. ub[4 * IDX + i0], \ + 4 * IDX + i0); \ + } \ + } while (0) +#define fVLOG_VTCM_HALFWORD_INCREMENT(EA, OFFSET, INC, IDX, ALIGNMENT, LEN) \ + do { \ + int log_byte = 0; \ + target_ulong va = EA; \ + target_ulong va_high = EA + LEN; \ + for (int i0 = 0; i0 < 2; i0++) { \ + log_byte = (va + i0) <= va_high; \ + LOG_VTCM_BYTE(va + i0, log_byte, INC.ub[2 * IDX + i0], \ + 2 * IDX + i0); \ + } \ + } while (0) + +#define fVLOG_VTCM_HALFWORD_INCREMENT_DV(EA, OFFSET, INC, IDX, IDX2, IDX_H, \ + ALIGNMENT, LEN) \ + do { \ + int log_byte = 0; \ + target_ulong va = EA; \ + target_ulong va_high = EA + LEN; \ + for (int i0 = 0; i0 < 2; i0++) { \ + log_byte = (va + i0) <= va_high; \ + LOG_VTCM_BYTE(va + i0, log_byte, INC.ub[2 * IDX + i0], \ + 2 * IDX + i0); \ + } \ + } while (0) + +/* NOTE - Will this always be tmp_VRegs[0]; */ +#define GATHER_FUNCTION(EA, OFFSET, IDX, LEN, ELEMENT_SIZE, BANK_IDX, QVAL) \ + do { \ + int i0; \ + target_ulong va = EA; \ + target_ulong va_high = EA + LEN; \ + uintptr_t ra = GETPC(); \ + int log_bank = 0; \ + int log_byte = 0; \ + for (i0 = 0; i0 < ELEMENT_SIZE; i0++) { \ + log_byte = ((va + i0) <= va_high) && QVAL; \ + log_bank |= (log_byte << i0); \ + uint8_t B; \ + B = cpu_ldub_data_ra(env, EA + i0, ra); \ + env->tmp_VRegs[0].ub[ELEMENT_SIZE * IDX + i0] = B; \ + LOG_VTCM_BYTE(va + i0, log_byte, B, ELEMENT_SIZE * IDX + i0); \ + } \ + } while (0) +#define fVLOG_VTCM_GATHER_WORD(EA, OFFSET, IDX, LEN) \ + do { \ + GATHER_FUNCTION(EA, OFFSET, IDX, LEN, 4, IDX, 1); \ + } while (0) +#define fVLOG_VTCM_GATHER_HALFWORD(EA, OFFSET, IDX, LEN) \ + do { \ + GATHER_FUNCTION(EA, OFFSET, IDX, LEN, 2, IDX, 1); \ + } while (0) +#define fVLOG_VTCM_GATHER_HALFWORD_DV(EA, OFFSET, IDX, IDX2, IDX_H, LEN) \ + do { \ + GATHER_FUNCTION(EA, OFFSET, IDX, LEN, 2, (2 * IDX2 + IDX_H), 1); \ + } while (0) +#define fVLOG_VTCM_GATHER_WORDQ(EA, OFFSET, IDX, Q, LEN) \ + do { \ + GATHER_FUNCTION(EA, OFFSET, IDX, LEN, 4, IDX, \ + fGETQBIT(QsV, 4 * IDX + i0)); \ + } while (0) +#define fVLOG_VTCM_GATHER_HALFWORDQ(EA, OFFSET, IDX, Q, LEN) \ + do { \ + GATHER_FUNCTION(EA, OFFSET, IDX, LEN, 2, IDX, \ + fGETQBIT(QsV, 2 * IDX + i0)); \ + } while (0) +#define fVLOG_VTCM_GATHER_HALFWORDQ_DV(EA, OFFSET, IDX, IDX2, IDX_H, Q, LEN) \ + do { \ + GATHER_FUNCTION(EA, OFFSET, IDX, LEN, 2, (2 * IDX2 + IDX_H), \ + fGETQBIT(QsV, 2 * IDX + i0)); \ + } while (0) +#define SCATTER_OP_WRITE_TO_MEM(TYPE) \ + do { \ + uintptr_t ra = GETPC(); \ + for (int i = 0; i < sizeof(MMVector); i += sizeof(TYPE)) { \ + if (test_bit(i, env->vtcm_log.mask)) { \ + TYPE dst = 0; \ + TYPE inc = 0; \ + for (int j = 0; j < sizeof(TYPE); j++) { \ + uint8_t val; \ + val = cpu_ldub_data_ra(env, env->vtcm_log.va[i + j], ra); \ + dst |= val << (8 * j); \ + inc |= env->vtcm_log.data.ub[j + i] << (8 * j); \ + clear_bit(j + i, env->vtcm_log.mask); \ + env->vtcm_log.data.ub[j + i] = 0; \ + } \ + dst += inc; \ + for (int j = 0; j < sizeof(TYPE); j++) { \ + cpu_stb_data_ra(env, env->vtcm_log.va[i + j], \ + (dst >> (8 * j)) & 0xFF, ra); \ + } \ + } \ + } \ + } while (0) +#define SCATTER_OP_PROBE_MEM(TYPE, MMU_IDX, RETADDR) \ + do { \ + for (int i = 0; i < sizeof(MMVector); i += sizeof(TYPE)) { \ + if (test_bit(i, env->vtcm_log.mask)) { \ + for (int j = 0; j < sizeof(TYPE); j++) { \ + probe_read(env, env->vtcm_log.va[i + j], 1, \ + MMU_IDX, RETADDR); \ + probe_write(env, env->vtcm_log.va[i + j], 1, \ + MMU_IDX, RETADDR); \ + } \ + } \ + } \ + } while (0) +#define SCATTER_FUNCTION(EA, OFFSET, IDX, LEN, ELEM_SIZE, BANK_IDX, QVAL, IN) \ + do { \ + int i0; \ + target_ulong va = EA; \ + target_ulong va_high = EA + LEN; \ + int log_bank = 0; \ + int log_byte = 0; \ + for (i0 = 0; i0 < ELEM_SIZE; i0++) { \ + log_byte = ((va + i0) <= va_high) && QVAL; \ + log_bank |= (log_byte << i0); \ + LOG_VTCM_BYTE(va + i0, log_byte, IN.ub[ELEM_SIZE * IDX + i0], \ + ELEM_SIZE * IDX + i0); \ + } \ + } while (0) +#define fVLOG_VTCM_HALFWORD(EA, OFFSET, IN, IDX, LEN) \ + do { \ + SCATTER_FUNCTION(EA, OFFSET, IDX, LEN, 2, IDX, 1, IN); \ + } while (0) +#define fVLOG_VTCM_WORD(EA, OFFSET, IN, IDX, LEN) \ + do { \ + SCATTER_FUNCTION(EA, OFFSET, IDX, LEN, 4, IDX, 1, IN); \ + } while (0) +#define fVLOG_VTCM_HALFWORDQ(EA, OFFSET, IN, IDX, Q, LEN) \ + do { \ + SCATTER_FUNCTION(EA, OFFSET, IDX, LEN, 2, IDX, \ + fGETQBIT(QsV, 2 * IDX + i0), IN); \ + } while (0) +#define fVLOG_VTCM_WORDQ(EA, OFFSET, IN, IDX, Q, LEN) \ + do { \ + SCATTER_FUNCTION(EA, OFFSET, IDX, LEN, 4, IDX, \ + fGETQBIT(QsV, 4 * IDX + i0), IN); \ + } while (0) +#define fVLOG_VTCM_HALFWORD_DV(EA, OFFSET, IN, IDX, IDX2, IDX_H, LEN) \ + do { \ + SCATTER_FUNCTION(EA, OFFSET, IDX, LEN, 2, \ + (2 * IDX2 + IDX_H), 1, IN); \ + } while (0) +#define fVLOG_VTCM_HALFWORDQ_DV(EA, OFFSET, IN, IDX, Q, IDX2, IDX_H, LEN) \ + do { \ + SCATTER_FUNCTION(EA, OFFSET, IDX, LEN, 2, (2 * IDX2 + IDX_H), \ + fGETQBIT(QsV, 2 * IDX + i0), IN); \ + } while (0) +#define fSTORERELEASE(EA, TYPE) \ + do { \ + fV_AL_CHECK(EA, fVECSIZE() - 1); \ + } while (0) +#ifdef QEMU_GENERATE +#define fLOADMMV(EA, DST) gen_vreg_load(ctx, DST##_off, EA, true) +#endif +#ifdef QEMU_GENERATE +#define fLOADMMVU(EA, DST) gen_vreg_load(ctx, DST##_off, EA, false) +#endif +#ifdef QEMU_GENERATE +#define fSTOREMMV(EA, SRC) \ + gen_vreg_store(ctx, insn, pkt, EA, SRC##_off, insn->slot, true) +#endif +#ifdef QEMU_GENERATE +#define fSTOREMMVQ(EA, SRC, MASK) \ + gen_vreg_masked_store(ctx, EA, SRC##_off, MASK##_off, insn->slot, false) +#endif +#ifdef QEMU_GENERATE +#define fSTOREMMVNQ(EA, SRC, MASK) \ + gen_vreg_masked_store(ctx, EA, SRC##_off, MASK##_off, insn->slot, true) +#endif +#ifdef QEMU_GENERATE +#define fSTOREMMVU(EA, SRC) \ + gen_vreg_store(ctx, insn, pkt, EA, SRC##_off, insn->slot, false) +#endif +#define fVFOREACH(WIDTH, VAR) for (VAR = 0; VAR < fVELEM(WIDTH); VAR++) +#define fVARRAY_ELEMENT_ACCESS(ARRAY, TYPE, INDEX) \ + ARRAY.v[(INDEX) / (fVECSIZE() / (sizeof(ARRAY.TYPE[0])))].TYPE[(INDEX) % \ + (fVECSIZE() / (sizeof(ARRAY.TYPE[0])))] + +#define fVSATDW(U, V) fVSATW(((((long long)U) << 32) | fZXTN(32, 64, V))) +#define fVASL_SATHI(U, V) fVSATW(((U) << 1) | ((V) >> 31)) +#define fVUADDSAT(WIDTH, U, V) \ + fVSATUN(WIDTH, fZXTN(WIDTH, 2 * WIDTH, U) + fZXTN(WIDTH, 2 * WIDTH, V)) +#define fVSADDSAT(WIDTH, U, V) \ + fVSATN(WIDTH, fSXTN(WIDTH, 2 * WIDTH, U) + fSXTN(WIDTH, 2 * WIDTH, V)) +#define fVUSUBSAT(WIDTH, U, V) \ + fVSATUN(WIDTH, fZXTN(WIDTH, 2 * WIDTH, U) - fZXTN(WIDTH, 2 * WIDTH, V)) +#define fVSSUBSAT(WIDTH, U, V) \ + fVSATN(WIDTH, fSXTN(WIDTH, 2 * WIDTH, U) - fSXTN(WIDTH, 2 * WIDTH, V)) +#define fVAVGU(WIDTH, U, V) \ + ((fZXTN(WIDTH, 2 * WIDTH, U) + fZXTN(WIDTH, 2 * WIDTH, V)) >> 1) +#define fVAVGURND(WIDTH, U, V) \ + ((fZXTN(WIDTH, 2 * WIDTH, U) + fZXTN(WIDTH, 2 * WIDTH, V) + 1) >> 1) +#define fVNAVGU(WIDTH, U, V) \ + ((fZXTN(WIDTH, 2 * WIDTH, U) - fZXTN(WIDTH, 2 * WIDTH, V)) >> 1) +#define fVNAVGURNDSAT(WIDTH, U, V) \ + fVSATUN(WIDTH, ((fZXTN(WIDTH, 2 * WIDTH, U) - \ + fZXTN(WIDTH, 2 * WIDTH, V) + 1) >> 1)) +#define fVAVGS(WIDTH, U, V) \ + ((fSXTN(WIDTH, 2 * WIDTH, U) + fSXTN(WIDTH, 2 * WIDTH, V)) >> 1) +#define fVAVGSRND(WIDTH, U, V) \ + ((fSXTN(WIDTH, 2 * WIDTH, U) + fSXTN(WIDTH, 2 * WIDTH, V) + 1) >> 1) +#define fVNAVGS(WIDTH, U, V) \ + ((fSXTN(WIDTH, 2 * WIDTH, U) - fSXTN(WIDTH, 2 * WIDTH, V)) >> 1) +#define fVNAVGSRND(WIDTH, U, V) \ + ((fSXTN(WIDTH, 2 * WIDTH, U) - fSXTN(WIDTH, 2 * WIDTH, V) + 1) >> 1) +#define fVNAVGSRNDSAT(WIDTH, U, V) \ + fVSATN(WIDTH, ((fSXTN(WIDTH, 2 * WIDTH, U) - \ + fSXTN(WIDTH, 2 * WIDTH, V) + 1) >> 1)) +#define fVNOROUND(VAL, SHAMT) VAL +#define fVNOSAT(VAL) VAL +#define fVROUND(VAL, SHAMT) \ + ((VAL) + (((SHAMT) > 0) ? (1LL << ((SHAMT) - 1)) : 0)) +#define fCARRY_FROM_ADD32(A, B, C) \ + (((fZXTN(32, 64, A) + fZXTN(32, 64, B) + C) >> 32) & 1) +#define fUARCH_NOTE_PUMP_4X() +#define fUARCH_NOTE_PUMP_2X() + +#define IV1DEAD() +#endif From e3d143e98e53a7bc8d76d340a5c471567c342a3f Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Thu, 23 Jan 2020 13:51:06 -0600 Subject: [PATCH 1165/1334] Hexagon HVX (target/hexagon) import macro definitions Imported from the Hexagon architecture library imported/allext_macros.def Top level macro include for all extensions imported/macros.def Scalar core macros (some HVX here) imported/mmvec/macros.def HVX macro definitions The macro definition files specify instruction attributes that are applied to each instruction that reverences the macro. Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/imported/allext_macros.def | 25 + target/hexagon/imported/macros.def | 88 +++ target/hexagon/imported/mmvec/macros.def | 842 ++++++++++++++++++++++ 3 files changed, 955 insertions(+) create mode 100644 target/hexagon/imported/allext_macros.def create mode 100755 target/hexagon/imported/mmvec/macros.def diff --git a/target/hexagon/imported/allext_macros.def b/target/hexagon/imported/allext_macros.def new file mode 100644 index 0000000000..9c91199151 --- /dev/null +++ b/target/hexagon/imported/allext_macros.def @@ -0,0 +1,25 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Top level file for all instruction set extensions + */ +#define EXTNAME mmvec +#define EXTSTR "mmvec" +#include "mmvec/macros.def" +#undef EXTNAME +#undef EXTSTR diff --git a/target/hexagon/imported/macros.def b/target/hexagon/imported/macros.def index 32ed3bf8fc..e23f91562e 100755 --- a/target/hexagon/imported/macros.def +++ b/target/hexagon/imported/macros.def @@ -176,6 +176,12 @@ DEF_MACRO( (A_DOTNEWVALUE,A_RESTRICT_SLOT0ONLY) ) +DEF_MACRO( + fVSATUVALN, + ({ ((VAL) < 0) ? 0 : ((1LL<<(N))-1);}), + () +) + DEF_MACRO( fSATUVALN, ({fSET_OVERFLOW(); ((VAL) < 0) ? 0 : ((1LL<<(N))-1);}), @@ -188,6 +194,12 @@ DEF_MACRO( () ) +DEF_MACRO( + fVSATVALN, + ({((VAL) < 0) ? (-(1LL<<((N)-1))) : ((1LL<<((N)-1))-1);}), + () +) + DEF_MACRO( fZXTN, /* macro name */ ((VAL) & ((1LL<<(N))-1)), @@ -205,6 +217,11 @@ DEF_MACRO( ((fSXTN(N,64,VAL) == (VAL)) ? (VAL) : fSATVALN(N,VAL)), () ) +DEF_MACRO( + fVSATN, + ((fSXTN(N,64,VAL) == (VAL)) ? (VAL) : fVSATVALN(N,VAL)), + () +) DEF_MACRO( fADDSAT64, @@ -234,6 +251,12 @@ DEF_MACRO( () ) +DEF_MACRO( + fVSATUN, + ((fZXTN(N,64,VAL) == (VAL)) ? (VAL) : fVSATUVALN(N,VAL)), + () +) + DEF_MACRO( fSATUN, ((fZXTN(N,64,VAL) == (VAL)) ? (VAL) : fSATUVALN(N,VAL)), @@ -253,6 +276,19 @@ DEF_MACRO( () ) +DEF_MACRO( + fVSATH, + (fVSATN(16,VAL)), + () +) + +DEF_MACRO( + fVSATUH, + (fVSATUN(16,VAL)), + () +) + + DEF_MACRO( fSATUB, (fSATUN(8,VAL)), @@ -265,6 +301,20 @@ DEF_MACRO( ) +DEF_MACRO( + fVSATUB, + (fVSATUN(8,VAL)), + () +) +DEF_MACRO( + fVSATB, + (fVSATN(8,VAL)), + () +) + + + + /*************************************/ /* immediate extension */ /*************************************/ @@ -556,6 +606,18 @@ DEF_MACRO( /* optional attributes */ ) +DEF_MACRO( + fCAST2_2s, /* macro name */ + ((size2s_t)(A)), + /* optional attributes */ +) + +DEF_MACRO( + fCAST2_2u, /* macro name */ + ((size2u_t)(A)), + /* optional attributes */ +) + DEF_MACRO( fCAST4_4s, /* macro name */ ((size4s_t)(A)), @@ -876,6 +938,11 @@ DEF_MACRO( (((size8s_t)(A))<. + */ + +DEF_MACRO(fDUMPQ, + do { + printf(STR ":" #REG ": 0x%016llx\n",REG.ud[0]); + } while (0), + () +) + +DEF_MACRO(fUSE_LOOKUP_ADDRESS_BY_REV, + PROC->arch_proc_options->mmvec_use_full_va_for_lookup, + () +) + +DEF_MACRO(fUSE_LOOKUP_ADDRESS, + 1, + () +) + +DEF_MACRO(fNOTQ, + ({mmqreg_t _ret = {0}; int _i_; for (_i_ = 0; _i_ < fVECSIZE()/64; _i_++) _ret.ud[_i_] = ~VAL.ud[_i_]; _ret;}), + () +) + +DEF_MACRO(fGETQBITS, + ((MASK) & (REG.w[(BITNO)>>5] >> ((BITNO) & 0x1f))), + () +) + +DEF_MACRO(fGETQBIT, + fGETQBITS(REG,1,1,BITNO), + () +) + +DEF_MACRO(fGENMASKW, + (((fGETQBIT(QREG,(IDX*4+0)) ? 0xFF : 0x0) << 0) + |((fGETQBIT(QREG,(IDX*4+1)) ? 0xFF : 0x0) << 8) + |((fGETQBIT(QREG,(IDX*4+2)) ? 0xFF : 0x0) << 16) + |((fGETQBIT(QREG,(IDX*4+3)) ? 0xFF : 0x0) << 24)), + () +) +DEF_MACRO(fGET10BIT, + { + COE = (((((fGETUBYTE(3,VAL) >> (2 * POS)) & 3) << 8) | fGETUBYTE(POS,VAL)) << 6); + COE >>= 6; + }, + () +) + +DEF_MACRO(fVMAX, + (X>Y) ? X : Y, + () +) + + +DEF_MACRO(fGETNIBBLE, + ( fSXTN(4,8,(SRC >> (4*IDX)) & 0xF) ), + () +) + +DEF_MACRO(fGETCRUMB, + ( fSXTN(2,8,(SRC >> (2*IDX)) & 0x3) ), + () +) + +DEF_MACRO(fGETCRUMB_SYMMETRIC, + ( (fGETCRUMB(IDX,SRC)>=0 ? (2-fGETCRUMB(IDX,SRC)) : fGETCRUMB(IDX,SRC) ) ), + () +) + +#define ZERO_OFFSET_2B + + +DEF_MACRO(fGENMASKH, + (((fGETQBIT(QREG,(IDX*2+0)) ? 0xFF : 0x0) << 0) + |((fGETQBIT(QREG,(IDX*2+1)) ? 0xFF : 0x0) << 8)), + () +) + +DEF_MACRO(fGETMASKW, + (VREG.w[IDX] & fGENMASKW((QREG),IDX)), + () +) + +DEF_MACRO(fGETMASKH, + (VREG.h[IDX] & fGENMASKH((QREG),IDX)), + () +) + +DEF_MACRO(fCONDMASK8, + (fGETQBIT(QREG,IDX) ? (YESVAL) : (NOVAL)), + () +) + +DEF_MACRO(fCONDMASK16, + ((fGENMASKH(QREG,IDX) & (YESVAL)) | (fGENMASKH(fNOTQ(QREG),IDX) & (NOVAL))), + () +) + +DEF_MACRO(fCONDMASK32, + ((fGENMASKW(QREG,IDX) & (YESVAL)) | (fGENMASKW(fNOTQ(QREG),IDX) & (NOVAL))), + () +) + + +DEF_MACRO(fSETQBITS, + do { + size4u_t __TMP = (VAL); + REG.w[(BITNO)>>5] &= ~((MASK) << ((BITNO) & 0x1f)); + REG.w[(BITNO)>>5] |= (((__TMP) & (MASK)) << ((BITNO) & 0x1f)); + } while (0), + () +) + +DEF_MACRO(fSETQBIT, + fSETQBITS(REG,1,1,BITNO,VAL), + () +) + +DEF_MACRO(fVBYTES, + (fVECSIZE()), + () +) + +DEF_MACRO(fVHALVES, + (fVECSIZE()/2), + () +) + +DEF_MACRO(fVWORDS, + (fVECSIZE()/4), + () +) + +DEF_MACRO(fVDWORDS, + (fVECSIZE()/8), + () +) + +DEF_MACRO(fVALIGN, + ( ADDR = ADDR & ~(LOG2_ALIGNMENT-1)), + () +) + +DEF_MACRO(fVLASTBYTE, + ( ADDR = ADDR | (LOG2_ALIGNMENT-1)), + () +) + + +DEF_MACRO(fVELEM, + ((fVECSIZE()*8)/WIDTH), + () +) + +DEF_MACRO(fVECLOGSIZE, + (mmvec_current_veclogsize(thread)), + () +) + +DEF_MACRO(fVECSIZE, + (1<VRegs_updated & (((VRegMask)1)<future_VRegs[VNUM] : mmvec_zero_vector()), + (A_DOTNEWVALUE,A_RESTRICT_SLOT0ONLY) +) + +DEF_MACRO( + fV_AL_CHECK, + if ((EA) & (MASK)) { + warn("aligning misaligned vector. PC=%08x EA=%08x",thread->Regs[REG_PC],(EA)); + }, + () +) +DEF_MACRO(fSCATTER_INIT, + { + mem_vector_scatter_init(thread, insn, REGION_START, LENGTH, ELEMENT_SIZE); + if (EXCEPTION_DETECTED) return; + }, + (A_STORE,A_MEMLIKE,A_RESTRICT_SLOT0ONLY) +) + +DEF_MACRO(fGATHER_INIT, + { + mem_vector_gather_init(thread, insn, REGION_START, LENGTH, ELEMENT_SIZE); + if (EXCEPTION_DETECTED) return; + }, + (A_LOAD,A_MEMLIKE,A_RESTRICT_SLOT1ONLY) +) + +DEF_MACRO(fSCATTER_FINISH, + { + if (EXCEPTION_DETECTED) return; + mem_vector_scatter_finish(thread, insn, OP); + }, + () +) + +DEF_MACRO(fGATHER_FINISH, + { + if (EXCEPTION_DETECTED) return; + mem_vector_gather_finish(thread, insn); + }, + () +) + + +DEF_MACRO(CHECK_VTCM_PAGE, + { + int slot = insn->slot; + paddr_t pa = thread->mem_access[slot].paddr+OFFSET; + pa = pa & ~(ALIGNMENT-1); + FLAG = (pa < (thread->mem_access[slot].paddr+LENGTH)); + }, + () +) +DEF_MACRO(COUNT_OUT_OF_BOUNDS, + { + if (!FLAG) + { + THREAD2STRUCT->vtcm_log.oob_access += SIZE; + warn("Scatter/Gather out of bounds of region"); + } + }, + () +) + +DEF_MACRO(fLOG_SCATTER_OP, + { + // Log the size and indicate that the extension ext.c file needs to increment right before memory write + THREAD2STRUCT->vtcm_log.op = 1; + THREAD2STRUCT->vtcm_log.op_size = SIZE; + }, + () +) + + + +DEF_MACRO(fVLOG_VTCM_WORD_INCREMENT, + { + int slot = insn->slot; + int log_bank = 0; + int log_byte =0; + paddr_t pa = thread->mem_access[slot].paddr+(OFFSET & ~(ALIGNMENT-1)); + paddr_t pa_high = thread->mem_access[slot].paddr+LEN; + for(int i0 = 0; i0 < 4; i0++) + { + log_byte = ((OFFSET>=0)&&((pa+i0)<=pa_high)); + log_bank |= (log_byte<slot; + int log_bank = 0; + int log_byte = 0; + paddr_t pa = thread->mem_access[slot].paddr+(OFFSET & ~(ALIGNMENT-1)); + paddr_t pa_high = thread->mem_access[slot].paddr+LEN; + for(int i0 = 0; i0 < 2; i0++) { + log_byte = ((OFFSET>=0)&&((pa+i0)<=pa_high)); + log_bank |= (log_byte<slot; + int log_bank = 0; + int log_byte = 0; + paddr_t pa = thread->mem_access[slot].paddr+(OFFSET & ~(ALIGNMENT-1)); + paddr_t pa_high = thread->mem_access[slot].paddr+LEN; + for(int i0 = 0; i0 < 2; i0++) { + log_byte = ((OFFSET>=0)&&((pa+i0)<=pa_high)); + log_bank |= (log_byte<slot; + int i0; + paddr_t pa = thread->mem_access[slot].paddr+OFFSET; + paddr_t pa_high = thread->mem_access[slot].paddr+LEN; + int log_bank = 0; + int log_byte = 0; + for(i0 = 0; i0 < ELEMENT_SIZE; i0++) + { + log_byte = ((OFFSET>=0)&&((pa+i0)<=pa_high)) && QVAL; + log_bank |= (log_byte<system_ptr, thread->threadId, thread->mem_access[slot].paddr+OFFSET+i0); + THREAD2STRUCT->tmp_VRegs[0].ub[ELEMENT_SIZE*IDX+i0] = B; + LOG_VTCM_BYTE(pa+i0,log_byte,B,ELEMENT_SIZE*IDX+i0); + } + LOG_VTCM_BANK(pa, log_bank,BANK_IDX); +}, +() +) + + + +DEF_MACRO(fVLOG_VTCM_GATHER_WORD, + { + GATHER_FUNCTION(EA,OFFSET,IDX, LEN, 4, IDX, 1); + }, + () +) +DEF_MACRO(fVLOG_VTCM_GATHER_HALFWORD, + { + GATHER_FUNCTION(EA,OFFSET,IDX, LEN, 2, IDX, 1); + }, + () +) +DEF_MACRO(fVLOG_VTCM_GATHER_HALFWORD_DV, + { + GATHER_FUNCTION(EA,OFFSET,IDX, LEN, 2, (2*IDX2+IDX_H), 1); + }, + () +) +DEF_MACRO(fVLOG_VTCM_GATHER_WORDQ, + { + GATHER_FUNCTION(EA,OFFSET,IDX, LEN, 4, IDX, fGETQBIT(QsV,4*IDX+i0)); + }, + () +) +DEF_MACRO(fVLOG_VTCM_GATHER_HALFWORDQ, + { + GATHER_FUNCTION(EA,OFFSET,IDX, LEN, 2, IDX, fGETQBIT(QsV,2*IDX+i0)); + }, + () +) + +DEF_MACRO(fVLOG_VTCM_GATHER_HALFWORDQ_DV, + { + GATHER_FUNCTION(EA,OFFSET,IDX, LEN, 2, (2*IDX2+IDX_H), fGETQBIT(QsV,2*IDX+i0)); + }, + () +) + + +DEF_MACRO(DEBUG_LOG_ADDR, + { + + if (thread->processor_ptr->arch_proc_options->mmvec_network_addr_log2) + { + + int slot = insn->slot; + paddr_t pa = thread->mem_access[slot].paddr+OFFSET; + } + }, + () +) + + + + + + + +DEF_MACRO(SCATTER_OP_WRITE_TO_MEM, + { + for (int i = 0; i < mmvecx->vtcm_log.size; i+=sizeof(TYPE)) + { + if ( mmvecx->vtcm_log.mask.ub[i] != 0) { + TYPE dst = 0; + TYPE inc = 0; + for(int j = 0; j < sizeof(TYPE); j++) { + dst |= (sim_mem_read1(thread->system_ptr, thread->threadId, mmvecx->vtcm_log.pa[i+j]) << (8*j)); + inc |= mmvecx->vtcm_log.data.ub[j+i] << (8*j); + + mmvecx->vtcm_log.mask.ub[j+i] = 0; + mmvecx->vtcm_log.data.ub[j+i] = 0; + mmvecx->vtcm_log.offsets.ub[j+i] = 0; + } + dst += inc; + for(int j = 0; j < sizeof(TYPE); j++) { + sim_mem_write1(thread->system_ptr,thread->threadId, mmvecx->vtcm_log.pa[i+j], (dst >> (8*j))& 0xFF ); + } + } + + } + }, + () +) + +DEF_MACRO(SCATTER_FUNCTION, +{ + int slot = insn->slot; + int i0; + paddr_t pa = thread->mem_access[slot].paddr+OFFSET; + paddr_t pa_high = thread->mem_access[slot].paddr+LEN; + int log_bank = 0; + int log_byte = 0; + for(i0 = 0; i0 < ELEMENT_SIZE; i0++) { + log_byte = ((OFFSET>=0)&&((pa+i0)<=pa_high)) && QVAL; + log_bank |= (log_byte<processor_ptr)); + }, + (A_STORE,A_MEMLIKE) +) + +DEF_MACRO(fVFETCH_AL, + { + fV_AL_CHECK(EA,fVECSIZE()-1); + mem_fetch_vector(thread, insn, EA&~(fVECSIZE()-1), insn->slot, fVECSIZE()); + }, + (A_LOAD,A_MEMLIKE) +) + + +DEF_MACRO(fLOADMMV_AL, + { + fV_AL_CHECK(EA,ALIGNMENT-1); + thread->last_pkt->double_access_vec = 0; + mem_load_vector_oddva(thread, insn, EA&~(ALIGNMENT-1), EA, insn->slot, LEN, &DST.ub[0], LEN, fUSE_LOOKUP_ADDRESS_BY_REV(thread->processor_ptr)); + }, + (A_LOAD,A_MEMLIKE) +) + +DEF_MACRO(fLOADMMV, + fLOADMMV_AL(EA,fVECSIZE(),fVECSIZE(),DST), + () +) + +DEF_MACRO(fLOADMMVQ, + do { + int __i; + fLOADMMV_AL(EA,fVECSIZE(),fVECSIZE(),DST); + fVFOREACH(8,__i) if (!fGETQBIT(QVAL,__i)) DST.b[__i] = 0; + } while (0), + () +) + +DEF_MACRO(fLOADMMVNQ, + do { + int __i; + fLOADMMV_AL(EA,fVECSIZE(),fVECSIZE(),DST); + fVFOREACH(8,__i) if (fGETQBIT(QVAL,__i)) DST.b[__i] = 0; + } while (0), + () +) + +DEF_MACRO(fLOADMMVU_AL, + { + size4u_t size2 = (EA)&(ALIGNMENT-1); + size4u_t size1 = LEN-size2; + thread->last_pkt->double_access_vec = 1; + mem_load_vector_oddva(thread, insn, EA+size1, EA+fVECSIZE(), /* slot */ 1, size2, &DST.ub[size1], size2, fUSE_LOOKUP_ADDRESS()); + mem_load_vector_oddva(thread, insn, EA, EA,/* slot */ 0, size1, &DST.ub[0], size1, fUSE_LOOKUP_ADDRESS_BY_REV(thread->processor_ptr)); + }, + (A_LOAD,A_MEMLIKE) +) + +DEF_MACRO(fLOADMMVU, + { + /* if address happens to be aligned, only do aligned load */ + thread->last_pkt->pkt_has_vtcm_access = 0; + thread->last_pkt->pkt_access_count = 0; + if ( (EA & (fVECSIZE()-1)) == 0) { + thread->last_pkt->pkt_has_vmemu_access = 0; + thread->last_pkt->double_access = 0; + + fLOADMMV_AL(EA,fVECSIZE(),fVECSIZE(),DST); + } else { + thread->last_pkt->pkt_has_vmemu_access = 1; + thread->last_pkt->double_access = 1; + + fLOADMMVU_AL(EA,fVECSIZE(),fVECSIZE(),DST); + } + }, + () +) + +DEF_MACRO(fSTOREMMV_AL, + { + fV_AL_CHECK(EA,ALIGNMENT-1); + mem_store_vector_oddva(thread, insn, EA&~(ALIGNMENT-1), EA, insn->slot, LEN, &SRC.ub[0], 0, 0, fUSE_LOOKUP_ADDRESS_BY_REV(thread->processor_ptr)); + }, + (A_STORE,A_MEMLIKE) +) + +DEF_MACRO(fSTOREMMV, + fSTOREMMV_AL(EA,fVECSIZE(),fVECSIZE(),SRC), + () +) + +DEF_MACRO(fSTOREMMVQ_AL, + do { + mmvector_t maskvec; + int i; + for (i = 0; i < fVECSIZE(); i++) maskvec.ub[i] = fGETQBIT(MASK,i); + mem_store_vector_oddva(thread, insn, EA&~(ALIGNMENT-1), EA, insn->slot, LEN, &SRC.ub[0], &maskvec.ub[0], 0, fUSE_LOOKUP_ADDRESS_BY_REV(thread->processor_ptr)); + } while (0), + (A_STORE,A_MEMLIKE) +) + +DEF_MACRO(fSTOREMMVQ, + fSTOREMMVQ_AL(EA,fVECSIZE(),fVECSIZE(),SRC,MASK), + () +) + +DEF_MACRO(fSTOREMMVNQ_AL, + { + mmvector_t maskvec; + int i; + for (i = 0; i < fVECSIZE(); i++) maskvec.ub[i] = fGETQBIT(MASK,i); + fV_AL_CHECK(EA,ALIGNMENT-1); + mem_store_vector_oddva(thread, insn, EA&~(ALIGNMENT-1), EA, insn->slot, LEN, &SRC.ub[0], &maskvec.ub[0], 1, fUSE_LOOKUP_ADDRESS_BY_REV(thread->processor_ptr)); + }, + (A_STORE,A_MEMLIKE) +) + +DEF_MACRO(fSTOREMMVNQ, + fSTOREMMVNQ_AL(EA,fVECSIZE(),fVECSIZE(),SRC,MASK), + () +) + +DEF_MACRO(fSTOREMMVU_AL, + { + size4u_t size1 = ALIGNMENT-((EA)&(ALIGNMENT-1)); + size4u_t size2; + if (size1>LEN) size1 = LEN; + size2 = LEN-size1; + mem_store_vector_oddva(thread, insn, EA+size1, EA+fVECSIZE(), /* slot */ 1, size2, &SRC.ub[size1], 0, 0, fUSE_LOOKUP_ADDRESS()); + mem_store_vector_oddva(thread, insn, EA, EA, /* slot */ 0, size1, &SRC.ub[0], 0, 0, fUSE_LOOKUP_ADDRESS_BY_REV(thread->processor_ptr)); + }, + (A_STORE,A_MEMLIKE) +) + +DEF_MACRO(fSTOREMMVU, + { + thread->last_pkt->pkt_has_vtcm_access = 0; + thread->last_pkt->pkt_access_count = 0; + if ( (EA & (fVECSIZE()-1)) == 0) { + thread->last_pkt->double_access = 0; + fSTOREMMV_AL(EA,fVECSIZE(),fVECSIZE(),SRC); + } else { + thread->last_pkt->double_access = 1; + thread->last_pkt->pkt_has_vmemu_access = 1; + fSTOREMMVU_AL(EA,fVECSIZE(),fVECSIZE(),SRC); + } + }, + () +) + +DEF_MACRO(fSTOREMMVQU_AL, + { + size4u_t size1 = ALIGNMENT-((EA)&(ALIGNMENT-1)); + size4u_t size2; + mmvector_t maskvec; + int i; + for (i = 0; i < fVECSIZE(); i++) maskvec.ub[i] = fGETQBIT(MASK,i); + if (size1>LEN) size1 = LEN; + size2 = LEN-size1; + mem_store_vector_oddva(thread, insn, EA+size1, EA+fVECSIZE(),/* slot */ 1, size2, &SRC.ub[size1], &maskvec.ub[size1], 0, fUSE_LOOKUP_ADDRESS()); + mem_store_vector_oddva(thread, insn, EA, /* slot */ 0, size1, &SRC.ub[0], &maskvec.ub[0], 0, fUSE_LOOKUP_ADDRESS_BY_REV(thread->processor_ptr)); + }, + (A_STORE,A_MEMLIKE) +) + +DEF_MACRO(fSTOREMMVQU, + { + thread->last_pkt->pkt_has_vtcm_access = 0; + thread->last_pkt->pkt_access_count = 0; + if ( (EA & (fVECSIZE()-1)) == 0) { + thread->last_pkt->double_access = 0; + fSTOREMMVQ_AL(EA,fVECSIZE(),fVECSIZE(),SRC,MASK); + } else { + thread->last_pkt->double_access = 1; + thread->last_pkt->pkt_has_vmemu_access = 1; + fSTOREMMVQU_AL(EA,fVECSIZE(),fVECSIZE(),SRC,MASK); + } + }, + () +) + +DEF_MACRO(fSTOREMMVNQU_AL, + { + size4u_t size1 = ALIGNMENT-((EA)&(ALIGNMENT-1)); + size4u_t size2; + mmvector_t maskvec; + int i; + for (i = 0; i < fVECSIZE(); i++) maskvec.ub[i] = fGETQBIT(MASK,i); + if (size1>LEN) size1 = LEN; + size2 = LEN-size1; + mem_store_vector_oddva(thread, insn, EA+size1, EA+fVECSIZE(), /* slot */ 1, size2, &SRC.ub[size1], &maskvec.ub[size1], 1, fUSE_LOOKUP_ADDRESS()); + mem_store_vector_oddva(thread, insn, EA, EA, /* slot */ 0, size1, &SRC.ub[0], &maskvec.ub[0], 1, fUSE_LOOKUP_ADDRESS_BY_REV(thread->processor_ptr)); + }, + (A_STORE,A_MEMLIKE) +) + +DEF_MACRO(fSTOREMMVNQU, + { + thread->last_pkt->pkt_has_vtcm_access = 0; + thread->last_pkt->pkt_access_count = 0; + if ( (EA & (fVECSIZE()-1)) == 0) { + thread->last_pkt->double_access = 0; + fSTOREMMVNQ_AL(EA,fVECSIZE(),fVECSIZE(),SRC,MASK); + } else { + thread->last_pkt->double_access = 1; + thread->last_pkt->pkt_has_vmemu_access = 1; + fSTOREMMVNQU_AL(EA,fVECSIZE(),fVECSIZE(),SRC,MASK); + } + }, + () +) + + + + +DEF_MACRO(fVFOREACH, + for (VAR = 0; VAR < fVELEM(WIDTH); VAR++), + /* NOTHING */ +) + +DEF_MACRO(fVARRAY_ELEMENT_ACCESS, + ARRAY.v[(INDEX) / (fVECSIZE()/(sizeof(ARRAY.TYPE[0])))].TYPE[(INDEX) % (fVECSIZE()/(sizeof(ARRAY.TYPE[0])))], + () +) + +DEF_MACRO(fVNEWCANCEL, + do { THREAD2STRUCT->VRegs_select &= ~(1<<(REGNUM)); } while (0), + () +) + +DEF_MACRO(fTMPVDATA, + mmvec_vtmp_data(thread), + (A_CVI) +) + +DEF_MACRO(fVSATDW, + fVSATW( ( ( ((long long)U)<<32 ) | fZXTN(32,64,V) ) ), + /* attribs */ +) + +DEF_MACRO(fVASL_SATHI, + fVSATW(((U)<<1) | ((V)>>31)), + /* attribs */ +) + +DEF_MACRO(fVUADDSAT, + fVSATUN( WIDTH, fZXTN(WIDTH, 2*WIDTH, U) + fZXTN(WIDTH, 2*WIDTH, V)), + /* attribs */ +) + +DEF_MACRO(fVSADDSAT, + fVSATN( WIDTH, fSXTN(WIDTH, 2*WIDTH, U) + fSXTN(WIDTH, 2*WIDTH, V)), + /* attribs */ +) + +DEF_MACRO(fVUSUBSAT, + fVSATUN( WIDTH, fZXTN(WIDTH, 2*WIDTH, U) - fZXTN(WIDTH, 2*WIDTH, V)), + /* attribs */ +) + +DEF_MACRO(fVSSUBSAT, + fVSATN( WIDTH, fSXTN(WIDTH, 2*WIDTH, U) - fSXTN(WIDTH, 2*WIDTH, V)), + /* attribs */ +) + +DEF_MACRO(fVAVGU, + ((fZXTN(WIDTH, 2*WIDTH, U) + fZXTN(WIDTH, 2*WIDTH, V))>>1), + /* attribs */ +) + +DEF_MACRO(fVAVGURND, + ((fZXTN(WIDTH, 2*WIDTH, U) + fZXTN(WIDTH, 2*WIDTH, V)+1)>>1), + /* attribs */ +) + +DEF_MACRO(fVNAVGU, + ((fZXTN(WIDTH, 2*WIDTH, U) - fZXTN(WIDTH, 2*WIDTH, V))>>1), + /* attribs */ +) + +DEF_MACRO(fVNAVGURNDSAT, + fVSATUN(WIDTH,((fZXTN(WIDTH, 2*WIDTH, U) - fZXTN(WIDTH, 2*WIDTH, V)+1)>>1)), + /* attribs */ +) + +DEF_MACRO(fVAVGS, + ((fSXTN(WIDTH, 2*WIDTH, U) + fSXTN(WIDTH, 2*WIDTH, V))>>1), + /* attribs */ +) + +DEF_MACRO(fVAVGSRND, + ((fSXTN(WIDTH, 2*WIDTH, U) + fSXTN(WIDTH, 2*WIDTH, V)+1)>>1), + /* attribs */ +) + +DEF_MACRO(fVNAVGS, + ((fSXTN(WIDTH, 2*WIDTH, U) - fSXTN(WIDTH, 2*WIDTH, V))>>1), + /* attribs */ +) + +DEF_MACRO(fVNAVGSRND, + ((fSXTN(WIDTH, 2*WIDTH, U) - fSXTN(WIDTH, 2*WIDTH, V)+1)>>1), + /* attribs */ +) + +DEF_MACRO(fVNAVGSRNDSAT, + fVSATN(WIDTH,((fSXTN(WIDTH, 2*WIDTH, U) - fSXTN(WIDTH, 2*WIDTH, V)+1)>>1)), + /* attribs */ +) + + +DEF_MACRO(fVNOROUND, + VAL, + /* NOTHING */ +) +DEF_MACRO(fVNOSAT, + VAL, + /* NOTHING */ +) + +DEF_MACRO(fVROUND, + ((VAL) + (((SHAMT)>0)?(1LL<<((SHAMT)-1)):0)), + /* NOTHING */ +) + +DEF_MACRO(fCARRY_FROM_ADD32, + (((fZXTN(32,64,A)+fZXTN(32,64,B)+C) >> 32) & 1), + /* NOTHING */ +) + +DEF_MACRO(fUARCH_NOTE_PUMP_4X, + , + () +) + +DEF_MACRO(fUARCH_NOTE_PUMP_2X, + , + () +) From 144da35776e33a25c7783b6b04ed77e375b1c87c Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 18 May 2021 16:45:18 -0500 Subject: [PATCH 1166/1334] Hexagon HVX (target/hexagon) semantics generator Add HVX support to the semantics generator Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_semantics.c | 33 +++++++++++++++++++++++++++++++++ target/hexagon/hex_common.py | 13 +++++++++++++ 2 files changed, 46 insertions(+) diff --git a/target/hexagon/gen_semantics.c b/target/hexagon/gen_semantics.c index c5fccecc9e..4a2bdd70e9 100644 --- a/target/hexagon/gen_semantics.c +++ b/target/hexagon/gen_semantics.c @@ -44,6 +44,11 @@ int main(int argc, char *argv[]) * Q6INSN(A2_add,"Rd32=add(Rs32,Rt32)",ATTRIBS(), * "Add 32-bit registers", * { RdV=RsV+RtV;}) + * HVX instructions have the following form + * EXTINSN(V6_vinsertwr, "Vx32.w=vinsert(Rt32)", + * ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX), + * "Insert Word Scalar into Vector", + * VxV.uw[0] = RtV;) */ #define Q6INSN(TAG, BEH, ATTRIBS, DESCR, SEM) \ do { \ @@ -59,8 +64,23 @@ int main(int argc, char *argv[]) ")\n", \ #TAG, STRINGIZE(ATTRIBS)); \ } while (0); +#define EXTINSN(TAG, BEH, ATTRIBS, DESCR, SEM) \ + do { \ + fprintf(outfile, "SEMANTICS( \\\n" \ + " \"%s\", \\\n" \ + " %s, \\\n" \ + " \"\"\"%s\"\"\" \\\n" \ + ")\n", \ + #TAG, STRINGIZE(BEH), STRINGIZE(SEM)); \ + fprintf(outfile, "ATTRIBUTES( \\\n" \ + " \"%s\", \\\n" \ + " \"%s\" \\\n" \ + ")\n", \ + #TAG, STRINGIZE(ATTRIBS)); \ + } while (0); #include "imported/allidefs.def" #undef Q6INSN +#undef EXTINSN /* * Process the macro definitions @@ -81,6 +101,19 @@ int main(int argc, char *argv[]) ")\n", \ #MNAME, STRINGIZE(BEH), STRINGIZE(ATTRS)); #include "imported/macros.def" +#undef DEF_MACRO + +/* + * Process the macros for HVX + */ +#define DEF_MACRO(MNAME, BEH, ATTRS) \ + fprintf(outfile, "MACROATTRIB( \\\n" \ + " \"%s\", \\\n" \ + " \"\"\"%s\"\"\", \\\n" \ + " \"%s\" \\\n" \ + ")\n", \ + #MNAME, STRINGIZE(BEH), STRINGIZE(ATTRS)); +#include "imported/allext_macros.def" #undef DEF_MACRO fclose(outfile); diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index a84b003f7e..c81aca8d2a 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -145,6 +145,9 @@ def compute_tag_immediates(tag): ## P predicate register ## R GPR register ## M modifier register +## Q HVX predicate vector +## V HVX vector register +## O HVX new vector register ## regid can be one of the following ## d, e destination register ## dd destination register pair @@ -180,6 +183,9 @@ def is_readwrite(regid): def is_scalar_reg(regtype): return regtype in "RPC" +def is_hvx_reg(regtype): + return regtype in "VQ" + def is_old_val(regtype, regid, tag): return regtype+regid+'V' in semdict[tag] @@ -203,6 +209,13 @@ def need_ea(tag): def skip_qemu_helper(tag): return tag in overrides.keys() +def is_tmp_result(tag): + return ('A_CVI_TMP' in attribdict[tag] or + 'A_CVI_TMP_DST' in attribdict[tag]) + +def is_new_result(tag): + return ('A_CVI_NEW' in attribdict[tag]) + def imm_name(immlett): return "%siV" % immlett From ccd9eec874cf510aad54351fa7cf7c4624686287 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 9 Dec 2020 18:35:22 -0600 Subject: [PATCH 1167/1334] Hexagon HVX (target/hexagon) semantics generator - part 2 Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_helper_funcs.py | 112 +++++++++++- target/hexagon/gen_helper_protos.py | 16 +- target/hexagon/gen_tcg_funcs.py | 254 ++++++++++++++++++++++++++-- 3 files changed, 360 insertions(+), 22 deletions(-) diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index 2b1c5d8e3e..ac5ce1023c 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py @@ -48,12 +48,26 @@ def gen_helper_arg_pair(f,regtype,regid,regno): if regno >= 0 : f.write(", ") f.write("int64_t %s%sV" % (regtype,regid)) +def gen_helper_arg_ext(f,regtype,regid,regno): + if regno > 0 : f.write(", ") + f.write("void *%s%sV_void" % (regtype,regid)) + +def gen_helper_arg_ext_pair(f,regtype,regid,regno): + if regno > 0 : f.write(", ") + f.write("void *%s%sV_void" % (regtype,regid)) + def gen_helper_arg_opn(f,regtype,regid,i,tag): if (hex_common.is_pair(regid)): - gen_helper_arg_pair(f,regtype,regid,i) + if (hex_common.is_hvx_reg(regtype)): + gen_helper_arg_ext_pair(f,regtype,regid,i) + else: + gen_helper_arg_pair(f,regtype,regid,i) elif (hex_common.is_single(regid)): if hex_common.is_old_val(regtype, regid, tag): - gen_helper_arg(f,regtype,regid,i) + if (hex_common.is_hvx_reg(regtype)): + gen_helper_arg_ext(f,regtype,regid,i) + else: + gen_helper_arg(f,regtype,regid,i) elif hex_common.is_new_val(regtype, regid, tag): gen_helper_arg_new(f,regtype,regid,i) else: @@ -72,25 +86,67 @@ def gen_helper_dest_decl_pair(f,regtype,regid,regno,subfield=""): f.write(" int64_t %s%sV%s = 0;\n" % \ (regtype,regid,subfield)) +def gen_helper_dest_decl_ext(f,regtype,regid): + if (regtype == "Q"): + f.write(" /* %s%sV is *(MMQReg *)(%s%sV_void) */\n" % \ + (regtype,regid,regtype,regid)) + else: + f.write(" /* %s%sV is *(MMVector *)(%s%sV_void) */\n" % \ + (regtype,regid,regtype,regid)) + +def gen_helper_dest_decl_ext_pair(f,regtype,regid,regno): + f.write(" /* %s%sV is *(MMVectorPair *))%s%sV_void) */\n" % \ + (regtype,regid,regtype, regid)) + def gen_helper_dest_decl_opn(f,regtype,regid,i): if (hex_common.is_pair(regid)): - gen_helper_dest_decl_pair(f,regtype,regid,i) + if (hex_common.is_hvx_reg(regtype)): + gen_helper_dest_decl_ext_pair(f,regtype,regid, i) + else: + gen_helper_dest_decl_pair(f,regtype,regid,i) elif (hex_common.is_single(regid)): - gen_helper_dest_decl(f,regtype,regid,i) + if (hex_common.is_hvx_reg(regtype)): + gen_helper_dest_decl_ext(f,regtype,regid) + else: + gen_helper_dest_decl(f,regtype,regid,i) else: print("Bad register parse: ",regtype,regid,toss,numregs) +def gen_helper_src_var_ext(f,regtype,regid): + if (regtype == "Q"): + f.write(" /* %s%sV is *(MMQReg *)(%s%sV_void) */\n" % \ + (regtype,regid,regtype,regid)) + else: + f.write(" /* %s%sV is *(MMVector *)(%s%sV_void) */\n" % \ + (regtype,regid,regtype,regid)) + +def gen_helper_src_var_ext_pair(f,regtype,regid,regno): + f.write(" /* %s%sV%s is *(MMVectorPair *)(%s%sV%s_void) */\n" % \ + (regtype,regid,regno,regtype,regid,regno)) + def gen_helper_return(f,regtype,regid,regno): f.write(" return %s%sV;\n" % (regtype,regid)) def gen_helper_return_pair(f,regtype,regid,regno): f.write(" return %s%sV;\n" % (regtype,regid)) +def gen_helper_dst_write_ext(f,regtype,regid): + return + +def gen_helper_dst_write_ext_pair(f,regtype,regid): + return + def gen_helper_return_opn(f, regtype, regid, i): if (hex_common.is_pair(regid)): - gen_helper_return_pair(f,regtype,regid,i) + if (hex_common.is_hvx_reg(regtype)): + gen_helper_dst_write_ext_pair(f,regtype,regid) + else: + gen_helper_return_pair(f,regtype,regid,i) elif (hex_common.is_single(regid)): - gen_helper_return(f,regtype,regid,i) + if (hex_common.is_hvx_reg(regtype)): + gen_helper_dst_write_ext(f,regtype,regid) + else: + gen_helper_return(f,regtype,regid,i) else: print("Bad register parse: ",regtype,regid,toss,numregs) @@ -129,14 +185,20 @@ def gen_helper_function(f, tag, tagregs, tagimms): % (tag, tag)) else: ## The return type of the function is the type of the destination - ## register + ## register (if scalar) i=0 for regtype,regid,toss,numregs in regs: if (hex_common.is_written(regid)): if (hex_common.is_pair(regid)): - gen_helper_return_type_pair(f,regtype,regid,i) + if (hex_common.is_hvx_reg(regtype)): + continue + else: + gen_helper_return_type_pair(f,regtype,regid,i) elif (hex_common.is_single(regid)): - gen_helper_return_type(f,regtype,regid,i) + if (hex_common.is_hvx_reg(regtype)): + continue + else: + gen_helper_return_type(f,regtype,regid,i) else: print("Bad register parse: ",regtype,regid,toss,numregs) i += 1 @@ -145,16 +207,37 @@ def gen_helper_function(f, tag, tagregs, tagimms): f.write("void") f.write(" HELPER(%s)(CPUHexagonState *env" % tag) + ## Arguments include the vector destination operands i = 1 + for regtype,regid,toss,numregs in regs: + if (hex_common.is_written(regid)): + if (hex_common.is_pair(regid)): + if (hex_common.is_hvx_reg(regtype)): + gen_helper_arg_ext_pair(f,regtype,regid,i) + else: + continue + elif (hex_common.is_single(regid)): + if (hex_common.is_hvx_reg(regtype)): + gen_helper_arg_ext(f,regtype,regid,i) + else: + # This is the return value of the function + continue + else: + print("Bad register parse: ",regtype,regid,toss,numregs) + i += 1 ## Arguments to the helper function are the source regs and immediates for regtype,regid,toss,numregs in regs: if (hex_common.is_read(regid)): + if (hex_common.is_hvx_reg(regtype) and + hex_common.is_readwrite(regid)): + continue gen_helper_arg_opn(f,regtype,regid,i,tag) i += 1 for immlett,bits,immshift in imms: gen_helper_arg_imm(f,immlett) i += 1 + if hex_common.need_slot(tag): if i > 0: f.write(", ") f.write("uint32_t slot") @@ -173,6 +256,17 @@ def gen_helper_function(f, tag, tagregs, tagimms): gen_helper_dest_decl_opn(f,regtype,regid,i) i += 1 + for regtype,regid,toss,numregs in regs: + if (hex_common.is_read(regid)): + if (hex_common.is_pair(regid)): + if (hex_common.is_hvx_reg(regtype)): + gen_helper_src_var_ext_pair(f,regtype,regid,i) + elif (hex_common.is_single(regid)): + if (hex_common.is_hvx_reg(regtype)): + gen_helper_src_var_ext(f,regtype,regid) + else: + print("Bad register parse: ",regtype,regid,toss,numregs) + if 'A_FPOP' in hex_common.attribdict[tag]: f.write(' arch_fpop_start(env);\n'); diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index ea41007ec9..229ef8dbd2 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py @@ -94,19 +94,33 @@ def gen_helper_prototype(f, tag, tagregs, tagimms): f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag)) ## Generate the qemu DEF_HELPER type for each result + ## Iterate over this list twice + ## - Emit the scalar result + ## - Emit the vector result i=0 for regtype,regid,toss,numregs in regs: if (hex_common.is_written(regid)): - gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i) + if (not hex_common.is_hvx_reg(regtype)): + gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i) i += 1 ## Put the env between the outputs and inputs f.write(', env' ) i += 1 + # Second pass + for regtype,regid,toss,numregs in regs: + if (hex_common.is_written(regid)): + if (hex_common.is_hvx_reg(regtype)): + gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i) + i += 1 + ## Generate the qemu type for each input operand (regs and immediates) for regtype,regid,toss,numregs in regs: if (hex_common.is_read(regid)): + if (hex_common.is_hvx_reg(regtype) and + hex_common.is_readwrite(regid)): + continue gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i) i += 1 for immlett,bits,immshift in imms: diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index e3d59dd552..691ff6a949 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -119,10 +119,95 @@ def genptr_decl(f, tag, regtype, regid, regno): (regtype, regid, regtype, regid)) else: print("Bad register parse: ", regtype, regid) + elif (regtype == "V"): + if (regid in {"dd"}): + f.write(" const int %s%sN = insn->regno[%d];\n" %\ + (regtype, regid, regno)) + f.write(" const intptr_t %s%sV_off =\n" %\ + (regtype, regid)) + if (hex_common.is_tmp_result(tag)): + f.write(" ctx_tmp_vreg_off(ctx, %s%sN, 2, true);\n" % \ + (regtype, regid)) + else: + f.write(" ctx_future_vreg_off(ctx, %s%sN," % \ + (regtype, regid)) + f.write(" 2, true);\n") + if (not hex_common.skip_qemu_helper(tag)): + f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ + (regtype, regid)) + f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ + (regtype, regid, regtype, regid)) + elif (regid in {"uu", "vv", "xx"}): + f.write(" const int %s%sN = insn->regno[%d];\n" % \ + (regtype, regid, regno)) + f.write(" const intptr_t %s%sV_off =\n" % \ + (regtype, regid)) + f.write(" offsetof(CPUHexagonState, %s%sV);\n" % \ + (regtype, regid)) + if (not hex_common.skip_qemu_helper(tag)): + f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ + (regtype, regid)) + f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ + (regtype, regid, regtype, regid)) + elif (regid in {"s", "u", "v", "w"}): + f.write(" const int %s%sN = insn->regno[%d];\n" % \ + (regtype, regid, regno)) + f.write(" const intptr_t %s%sV_off =\n" % \ + (regtype, regid)) + f.write(" vreg_src_off(ctx, %s%sN);\n" % \ + (regtype, regid)) + if (not hex_common.skip_qemu_helper(tag)): + f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ + (regtype, regid)) + elif (regid in {"d", "x", "y"}): + f.write(" const int %s%sN = insn->regno[%d];\n" % \ + (regtype, regid, regno)) + f.write(" const intptr_t %s%sV_off =\n" % \ + (regtype, regid)) + if (hex_common.is_tmp_result(tag)): + f.write(" ctx_tmp_vreg_off(ctx, %s%sN, 1, true);\n" % \ + (regtype, regid)) + else: + f.write(" ctx_future_vreg_off(ctx, %s%sN," % \ + (regtype, regid)) + f.write(" 1, true);\n"); + if (not hex_common.skip_qemu_helper(tag)): + f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ + (regtype, regid)) + f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ + (regtype, regid, regtype, regid)) + else: + print("Bad register parse: ", regtype, regid) + elif (regtype == "Q"): + if (regid in {"d", "e", "x"}): + f.write(" const int %s%sN = insn->regno[%d];\n" % \ + (regtype, regid, regno)) + f.write(" const intptr_t %s%sV_off =\n" % \ + (regtype, regid)) + f.write(" offsetof(CPUHexagonState,\n") + f.write(" future_QRegs[%s%sN]);\n" % \ + (regtype, regid)) + if (not hex_common.skip_qemu_helper(tag)): + f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ + (regtype, regid)) + f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ + (regtype, regid, regtype, regid)) + elif (regid in {"s", "t", "u", "v"}): + f.write(" const int %s%sN = insn->regno[%d];\n" % \ + (regtype, regid, regno)) + f.write(" const intptr_t %s%sV_off =\n" %\ + (regtype, regid)) + f.write(" offsetof(CPUHexagonState, QRegs[%s%sN]);\n" % \ + (regtype, regid)) + if (not hex_common.skip_qemu_helper(tag)): + f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ + (regtype, regid)) + else: + print("Bad register parse: ", regtype, regid) else: print("Bad register parse: ", regtype, regid) -def genptr_decl_new(f,regtype,regid,regno): +def genptr_decl_new(f, tag, regtype, regid, regno): if (regtype == "N"): if (regid in {"s", "t"}): f.write(" TCGv %s%sN = hex_new_value[insn->regno[%d]];\n" % \ @@ -135,6 +220,21 @@ def genptr_decl_new(f,regtype,regid,regno): (regtype, regid, regno)) else: print("Bad register parse: ", regtype, regid) + elif (regtype == "O"): + if (regid == "s"): + f.write(" const intptr_t %s%sN_num = insn->regno[%d];\n" % \ + (regtype, regid, regno)) + if (hex_common.skip_qemu_helper(tag)): + f.write(" const intptr_t %s%sN_off =\n" % \ + (regtype, regid)) + f.write(" ctx_future_vreg_off(ctx, %s%sN_num," % \ + (regtype, regid)) + f.write(" 1, true);\n") + else: + f.write(" TCGv %s%sN = tcg_constant_tl(%s%sN_num);\n" % \ + (regtype, regid, regtype, regid)) + else: + print("Bad register parse: ", regtype, regid) else: print("Bad register parse: ", regtype, regid) @@ -145,7 +245,7 @@ def genptr_decl_opn(f, tag, regtype, regid, toss, numregs, i): if hex_common.is_old_val(regtype, regid, tag): genptr_decl(f,tag, regtype, regid, i) elif hex_common.is_new_val(regtype, regid, tag): - genptr_decl_new(f,regtype,regid,i) + genptr_decl_new(f, tag, regtype, regid, i) else: print("Bad register parse: ",regtype,regid,toss,numregs) else: @@ -159,7 +259,7 @@ def genptr_decl_imm(f,immlett): f.write(" int %s = insn->immed[%d];\n" % \ (hex_common.imm_name(immlett), i)) -def genptr_free(f,regtype,regid,regno): +def genptr_free(f, tag, regtype, regid, regno): if (regtype == "R"): if (regid in {"dd", "ss", "tt", "xx", "yy"}): f.write(" tcg_temp_free_i64(%s%sV);\n" % (regtype, regid)) @@ -182,33 +282,51 @@ def genptr_free(f,regtype,regid,regno): elif (regtype == "M"): if (regid != "u"): print("Bad register parse: ", regtype, regid) + elif (regtype == "V"): + if (regid in {"dd", "uu", "vv", "xx", \ + "d", "s", "u", "v", "w", "x", "y"}): + if (not hex_common.skip_qemu_helper(tag)): + f.write(" tcg_temp_free_ptr(%s%sV);\n" % \ + (regtype, regid)) + else: + print("Bad register parse: ", regtype, regid) + elif (regtype == "Q"): + if (regid in {"d", "e", "s", "t", "u", "v", "x"}): + if (not hex_common.skip_qemu_helper(tag)): + f.write(" tcg_temp_free_ptr(%s%sV);\n" % \ + (regtype, regid)) + else: + print("Bad register parse: ", regtype, regid) else: print("Bad register parse: ", regtype, regid) -def genptr_free_new(f,regtype,regid,regno): +def genptr_free_new(f, tag, regtype, regid, regno): if (regtype == "N"): if (regid not in {"s", "t"}): print("Bad register parse: ", regtype, regid) elif (regtype == "P"): if (regid not in {"t", "u", "v"}): print("Bad register parse: ", regtype, regid) + elif (regtype == "O"): + if (regid != "s"): + print("Bad register parse: ", regtype, regid) else: print("Bad register parse: ", regtype, regid) def genptr_free_opn(f,regtype,regid,i,tag): if (hex_common.is_pair(regid)): - genptr_free(f,regtype,regid,i) + genptr_free(f, tag, regtype, regid, i) elif (hex_common.is_single(regid)): if hex_common.is_old_val(regtype, regid, tag): - genptr_free(f,regtype,regid,i) + genptr_free(f, tag, regtype, regid, i) elif hex_common.is_new_val(regtype, regid, tag): - genptr_free_new(f,regtype,regid,i) + genptr_free_new(f, tag, regtype, regid, i) else: print("Bad register parse: ",regtype,regid,toss,numregs) else: print("Bad register parse: ",regtype,regid,toss,numregs) -def genptr_src_read(f,regtype,regid): +def genptr_src_read(f, tag, regtype, regid): if (regtype == "R"): if (regid in {"ss", "tt", "xx", "yy"}): f.write(" tcg_gen_concat_i32_i64(%s%sV, hex_gpr[%s%sN],\n" % \ @@ -238,6 +356,47 @@ def genptr_src_read(f,regtype,regid): elif (regtype == "M"): if (regid != "u"): print("Bad register parse: ", regtype, regid) + elif (regtype == "V"): + if (regid in {"uu", "vv", "xx"}): + f.write(" tcg_gen_gvec_mov(MO_64, %s%sV_off,\n" % \ + (regtype, regid)) + f.write(" vreg_src_off(ctx, %s%sN),\n" % \ + (regtype, regid)) + f.write(" sizeof(MMVector), sizeof(MMVector));\n") + f.write(" tcg_gen_gvec_mov(MO_64,\n") + f.write(" %s%sV_off + sizeof(MMVector),\n" % \ + (regtype, regid)) + f.write(" vreg_src_off(ctx, %s%sN ^ 1),\n" % \ + (regtype, regid)) + f.write(" sizeof(MMVector), sizeof(MMVector));\n") + elif (regid in {"s", "u", "v", "w"}): + if (not hex_common.skip_qemu_helper(tag)): + f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ + (regtype, regid, regtype, regid)) + elif (regid in {"x", "y"}): + f.write(" tcg_gen_gvec_mov(MO_64, %s%sV_off,\n" % \ + (regtype, regid)) + f.write(" vreg_src_off(ctx, %s%sN),\n" % \ + (regtype, regid)) + f.write(" sizeof(MMVector), sizeof(MMVector));\n") + if (not hex_common.skip_qemu_helper(tag)): + f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ + (regtype, regid, regtype, regid)) + else: + print("Bad register parse: ", regtype, regid) + elif (regtype == "Q"): + if (regid in {"s", "t", "u", "v"}): + if (not hex_common.skip_qemu_helper(tag)): + f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ + (regtype, regid, regtype, regid)) + elif (regid in {"x"}): + f.write(" tcg_gen_gvec_mov(MO_64, %s%sV_off,\n" % \ + (regtype, regid)) + f.write(" offsetof(CPUHexagonState, QRegs[%s%sN]),\n" % \ + (regtype, regid)) + f.write(" sizeof(MMQReg), sizeof(MMQReg));\n") + else: + print("Bad register parse: ", regtype, regid) else: print("Bad register parse: ", regtype, regid) @@ -248,15 +407,18 @@ def genptr_src_read_new(f,regtype,regid): elif (regtype == "P"): if (regid not in {"t", "u", "v"}): print("Bad register parse: ", regtype, regid) + elif (regtype == "O"): + if (regid != "s"): + print("Bad register parse: ", regtype, regid) else: print("Bad register parse: ", regtype, regid) def genptr_src_read_opn(f,regtype,regid,tag): if (hex_common.is_pair(regid)): - genptr_src_read(f,regtype,regid) + genptr_src_read(f, tag, regtype, regid) elif (hex_common.is_single(regid)): if hex_common.is_old_val(regtype, regid, tag): - genptr_src_read(f,regtype,regid) + genptr_src_read(f, tag, regtype, regid) elif hex_common.is_new_val(regtype, regid, tag): genptr_src_read_new(f,regtype,regid) else: @@ -331,11 +493,68 @@ def genptr_dst_write(f, tag, regtype, regid): else: print("Bad register parse: ", regtype, regid) +def genptr_dst_write_ext(f, tag, regtype, regid, newv="EXT_DFL"): + if (regtype == "V"): + if (regid in {"dd", "xx", "yy"}): + if ('A_CONDEXEC' in hex_common.attribdict[tag]): + is_predicated = "true" + else: + is_predicated = "false" + f.write(" gen_log_vreg_write_pair(ctx, %s%sV_off, %s%sN, " % \ + (regtype, regid, regtype, regid)) + f.write("%s, insn->slot, %s);\n" % \ + (newv, is_predicated)) + f.write(" ctx_log_vreg_write_pair(ctx, %s%sN, %s,\n" % \ + (regtype, regid, newv)) + f.write(" %s);\n" % (is_predicated)) + elif (regid in {"d", "x", "y"}): + if ('A_CONDEXEC' in hex_common.attribdict[tag]): + is_predicated = "true" + else: + is_predicated = "false" + f.write(" gen_log_vreg_write(ctx, %s%sV_off, %s%sN, %s, " % \ + (regtype, regid, regtype, regid, newv)) + f.write("insn->slot, %s);\n" % \ + (is_predicated)) + f.write(" ctx_log_vreg_write(ctx, %s%sN, %s, %s);\n" % \ + (regtype, regid, newv, is_predicated)) + else: + print("Bad register parse: ", regtype, regid) + elif (regtype == "Q"): + if (regid in {"d", "e", "x"}): + if ('A_CONDEXEC' in hex_common.attribdict[tag]): + is_predicated = "true" + else: + is_predicated = "false" + f.write(" gen_log_qreg_write(%s%sV_off, %s%sN, %s, " % \ + (regtype, regid, regtype, regid, newv)) + f.write("insn->slot, %s);\n" % (is_predicated)) + f.write(" ctx_log_qreg_write(ctx, %s%sN, %s);\n" % \ + (regtype, regid, is_predicated)) + else: + print("Bad register parse: ", regtype, regid) + else: + print("Bad register parse: ", regtype, regid) + def genptr_dst_write_opn(f,regtype, regid, tag): if (hex_common.is_pair(regid)): - genptr_dst_write(f, tag, regtype, regid) + if (hex_common.is_hvx_reg(regtype)): + if (hex_common.is_tmp_result(tag)): + genptr_dst_write_ext(f, tag, regtype, regid, "EXT_TMP") + else: + genptr_dst_write_ext(f, tag, regtype, regid) + else: + genptr_dst_write(f, tag, regtype, regid) elif (hex_common.is_single(regid)): - genptr_dst_write(f, tag, regtype, regid) + if (hex_common.is_hvx_reg(regtype)): + if (hex_common.is_new_result(tag)): + genptr_dst_write_ext(f, tag, regtype, regid, "EXT_NEW") + if (hex_common.is_tmp_result(tag)): + genptr_dst_write_ext(f, tag, regtype, regid, "EXT_TMP") + else: + genptr_dst_write_ext(f, tag, regtype, regid, "EXT_DFL") + else: + genptr_dst_write(f, tag, regtype, regid) else: print("Bad register parse: ",regtype,regid,toss,numregs) @@ -406,13 +625,24 @@ def gen_tcg_func(f, tag, regs, imms): ## If there is a scalar result, it is the return type for regtype,regid,toss,numregs in regs: if (hex_common.is_written(regid)): + if (hex_common.is_hvx_reg(regtype)): + continue gen_helper_call_opn(f, tag, regtype, regid, toss, numregs, i) i += 1 if (i > 0): f.write(", ") f.write("cpu_env") i=1 + for regtype,regid,toss,numregs in regs: + if (hex_common.is_written(regid)): + if (not hex_common.is_hvx_reg(regtype)): + continue + gen_helper_call_opn(f, tag, regtype, regid, toss, numregs, i) + i += 1 for regtype,regid,toss,numregs in regs: if (hex_common.is_read(regid)): + if (hex_common.is_hvx_reg(regtype) and + hex_common.is_readwrite(regid)): + continue gen_helper_call_opn(f, tag, regtype, regid, toss, numregs, i) i += 1 for immlett,bits,immshift in imms: From 9f1f2fe51e8a410dc8cbfbadcfd422a2195e7f06 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Fri, 26 Feb 2021 02:57:23 -0800 Subject: [PATCH 1168/1334] Hexagon HVX (target/hexagon) C preprocessor for decode tree Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_dectree_import.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/target/hexagon/gen_dectree_import.c b/target/hexagon/gen_dectree_import.c index 5b7ecfc6b3..ee354677fd 100644 --- a/target/hexagon/gen_dectree_import.c +++ b/target/hexagon/gen_dectree_import.c @@ -40,6 +40,11 @@ const char * const opcode_names[] = { * Q6INSN(A2_add,"Rd32=add(Rs32,Rt32)",ATTRIBS(), * "Add 32-bit registers", * { RdV=RsV+RtV;}) + * HVX instructions have the following form + * EXTINSN(V6_vinsertwr, "Vx32.w=vinsert(Rt32)", + * ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX,A_CVI_LATE), + * "Insert Word Scalar into Vector", + * VxV.uw[0] = RtV;) */ const char * const opcode_syntax[XX_LAST_OPCODE] = { #define Q6INSN(TAG, BEH, ATTRIBS, DESCR, SEM) \ @@ -105,6 +110,14 @@ static const char *get_opcode_enc(int opcode) static const char *get_opcode_enc_class(int opcode) { + const char *tmp = opcode_encodings[opcode].encoding; + if (tmp == NULL) { + const char *test = "V6_"; /* HVX */ + const char *name = opcode_names[opcode]; + if (strncmp(name, test, strlen(test)) == 0) { + return "EXT_mmvec"; + } + } return opcode_enc_class_names[opcode_encodings[opcode].enc_class]; } From 82f8b3dce2918813ed5807ec8f1e94c5b2300f6c Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 10 Mar 2021 19:38:45 -0600 Subject: [PATCH 1169/1334] Hexagon HVX (target/hexagon) instruction utility functions Functions to support scatter/gather Add new file to target/hexagon/meson.build Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/meson.build | 1 + target/hexagon/mmvec/system_ext_mmvec.c | 47 +++++++++++++++++++++++++ target/hexagon/mmvec/system_ext_mmvec.h | 25 +++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 target/hexagon/mmvec/system_ext_mmvec.c create mode 100644 target/hexagon/mmvec/system_ext_mmvec.h diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index c6d858ffb2..0bfaa41ec3 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -174,6 +174,7 @@ hexagon_ss.add(files( 'printinsn.c', 'arch.c', 'fma_emu.c', + 'mmvec/system_ext_mmvec.c', )) target_arch += {'hexagon': hexagon_ss} diff --git a/target/hexagon/mmvec/system_ext_mmvec.c b/target/hexagon/mmvec/system_ext_mmvec.c new file mode 100644 index 0000000000..8351f2cc01 --- /dev/null +++ b/target/hexagon/mmvec/system_ext_mmvec.c @@ -0,0 +1,47 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "mmvec/system_ext_mmvec.h" + +void mem_gather_store(CPUHexagonState *env, target_ulong vaddr, int slot) +{ + size_t size = sizeof(MMVector); + + env->vstore_pending[slot] = 1; + env->vstore[slot].va = vaddr; + env->vstore[slot].size = size; + memcpy(&env->vstore[slot].data.ub[0], &env->tmp_VRegs[0], size); + + /* On a gather store, overwrite the store mask to emulate dropped gathers */ + bitmap_copy(env->vstore[slot].mask, env->vtcm_log.mask, size); +} + +void mem_vector_scatter_init(CPUHexagonState *env) +{ + bitmap_zero(env->vtcm_log.mask, MAX_VEC_SIZE_BYTES); + + env->vtcm_pending = true; + env->vtcm_log.op = false; + env->vtcm_log.op_size = 0; +} + +void mem_vector_gather_init(CPUHexagonState *env) +{ + bitmap_zero(env->vtcm_log.mask, MAX_VEC_SIZE_BYTES); +} diff --git a/target/hexagon/mmvec/system_ext_mmvec.h b/target/hexagon/mmvec/system_ext_mmvec.h new file mode 100644 index 0000000000..bcefbffdf2 --- /dev/null +++ b/target/hexagon/mmvec/system_ext_mmvec.h @@ -0,0 +1,25 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HEXAGON_SYSTEM_EXT_MMVEC_H +#define HEXAGON_SYSTEM_EXT_MMVEC_H + +void mem_gather_store(CPUHexagonState *env, target_ulong vaddr, int slot); +void mem_vector_scatter_init(CPUHexagonState *env); +void mem_vector_gather_init(CPUHexagonState *env); + +#endif From 33e9ed11d5a52451f0777c0d9a1debb1a5d0922c Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Thu, 30 Sep 2021 11:07:00 -0500 Subject: [PATCH 1170/1334] Hexagon HVX (target/hexagon) helper functions Probe and commit vector stores (masked and scatter/gather) Log vector register writes Add the execution counters to the debug log Histogram instructions Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/helper.h | 16 +++ target/hexagon/op_helper.c | 282 ++++++++++++++++++++++++++++++++++++- 2 files changed, 296 insertions(+), 2 deletions(-) diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index 89de2a3ee5..c89aa4ed4d 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -23,6 +23,8 @@ DEF_HELPER_1(debug_start_packet, void, env) DEF_HELPER_FLAGS_3(debug_check_store_width, TCG_CALL_NO_WG, void, env, int, int) DEF_HELPER_FLAGS_3(debug_commit_end, TCG_CALL_NO_WG, void, env, int, int) DEF_HELPER_2(commit_store, void, env, int) +DEF_HELPER_3(gather_store, void, env, i32, int) +DEF_HELPER_1(commit_hvx_stores, void, env) DEF_HELPER_FLAGS_4(fcircadd, TCG_CALL_NO_RWG_SE, s32, s32, s32, s32, s32) DEF_HELPER_FLAGS_1(fbrev, TCG_CALL_NO_RWG_SE, i32, i32) DEF_HELPER_3(sfrecipa, i64, env, f32, f32) @@ -90,4 +92,18 @@ DEF_HELPER_4(sffms_lib, f32, env, f32, f32, f32) DEF_HELPER_3(dfmpyfix, f64, env, f64, f64) DEF_HELPER_4(dfmpyhh, f64, env, f64, f64, f64) +/* Histogram instructions */ +DEF_HELPER_1(vhist, void, env) +DEF_HELPER_1(vhistq, void, env) +DEF_HELPER_1(vwhist256, void, env) +DEF_HELPER_1(vwhist256q, void, env) +DEF_HELPER_1(vwhist256_sat, void, env) +DEF_HELPER_1(vwhist256q_sat, void, env) +DEF_HELPER_1(vwhist128, void, env) +DEF_HELPER_1(vwhist128q, void, env) +DEF_HELPER_2(vwhist128m, void, env, s32) +DEF_HELPER_2(vwhist128qm, void, env, s32) + DEF_HELPER_2(probe_pkt_scalar_store_s0, void, env, int) +DEF_HELPER_2(probe_hvx_stores, void, env, int) +DEF_HELPER_3(probe_pkt_scalar_hvx_stores, void, env, int, int) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index af32de4578..057baf9a48 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -27,6 +27,8 @@ #include "arch.h" #include "hex_arch_types.h" #include "fma_emu.h" +#include "mmvec/mmvec.h" +#include "mmvec/macros.h" #define SF_BIAS 127 #define SF_MANTBITS 23 @@ -164,6 +166,57 @@ void HELPER(commit_store)(CPUHexagonState *env, int slot_num) } } +void HELPER(gather_store)(CPUHexagonState *env, uint32_t addr, int slot) +{ + mem_gather_store(env, addr, slot); +} + +void HELPER(commit_hvx_stores)(CPUHexagonState *env) +{ + uintptr_t ra = GETPC(); + int i; + + /* Normal (possibly masked) vector store */ + for (i = 0; i < VSTORES_MAX; i++) { + if (env->vstore_pending[i]) { + env->vstore_pending[i] = 0; + target_ulong va = env->vstore[i].va; + int size = env->vstore[i].size; + for (int j = 0; j < size; j++) { + if (test_bit(j, env->vstore[i].mask)) { + cpu_stb_data_ra(env, va + j, env->vstore[i].data.ub[j], ra); + } + } + } + } + + /* Scatter store */ + if (env->vtcm_pending) { + env->vtcm_pending = false; + if (env->vtcm_log.op) { + /* Need to perform the scatter read/modify/write at commit time */ + if (env->vtcm_log.op_size == 2) { + SCATTER_OP_WRITE_TO_MEM(uint16_t); + } else if (env->vtcm_log.op_size == 4) { + /* Word Scatter += */ + SCATTER_OP_WRITE_TO_MEM(uint32_t); + } else { + g_assert_not_reached(); + } + } else { + for (i = 0; i < sizeof(MMVector); i++) { + if (test_bit(i, env->vtcm_log.mask)) { + cpu_stb_data_ra(env, env->vtcm_log.va[i], + env->vtcm_log.data.ub[i], ra); + clear_bit(i, env->vtcm_log.mask); + env->vtcm_log.data.ub[i] = 0; + } + + } + } + } +} + static void print_store(CPUHexagonState *env, int slot) { if (!(env->slot_cancelled & (1 << slot))) { @@ -242,9 +295,10 @@ void HELPER(debug_commit_end)(CPUHexagonState *env, int has_st0, int has_st1) HEX_DEBUG_LOG("Next PC = " TARGET_FMT_lx "\n", env->next_PC); HEX_DEBUG_LOG("Exec counters: pkt = " TARGET_FMT_lx ", insn = " TARGET_FMT_lx - "\n", + ", hvx = " TARGET_FMT_lx "\n", env->gpr[HEX_REG_QEMU_PKT_CNT], - env->gpr[HEX_REG_QEMU_INSN_CNT]); + env->gpr[HEX_REG_QEMU_INSN_CNT], + env->gpr[HEX_REG_QEMU_HVX_CNT]); } @@ -393,6 +447,65 @@ void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int mmu_idx) probe_store(env, 0, mmu_idx); } +void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) +{ + uintptr_t retaddr = GETPC(); + int i; + + /* Normal (possibly masked) vector store */ + for (i = 0; i < VSTORES_MAX; i++) { + if (env->vstore_pending[i]) { + target_ulong va = env->vstore[i].va; + int size = env->vstore[i].size; + for (int j = 0; j < size; j++) { + if (test_bit(j, env->vstore[i].mask)) { + probe_write(env, va + j, 1, mmu_idx, retaddr); + } + } + } + } + + /* Scatter store */ + if (env->vtcm_pending) { + if (env->vtcm_log.op) { + /* Need to perform the scatter read/modify/write at commit time */ + if (env->vtcm_log.op_size == 2) { + SCATTER_OP_PROBE_MEM(size2u_t, mmu_idx, retaddr); + } else if (env->vtcm_log.op_size == 4) { + /* Word Scatter += */ + SCATTER_OP_PROBE_MEM(size4u_t, mmu_idx, retaddr); + } else { + g_assert_not_reached(); + } + } else { + for (int i = 0; i < sizeof(MMVector); i++) { + if (test_bit(i, env->vtcm_log.mask)) { + probe_write(env, env->vtcm_log.va[i], 1, mmu_idx, retaddr); + } + + } + } + } +} + +void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask, + int mmu_idx) +{ + bool has_st0 = (mask >> 0) & 1; + bool has_st1 = (mask >> 1) & 1; + bool has_hvx_stores = (mask >> 2) & 1; + + if (has_st0) { + probe_store(env, 0, mmu_idx); + } + if (has_st1) { + probe_store(env, 1, mmu_idx); + } + if (has_hvx_stores) { + HELPER(probe_hvx_stores)(env, mmu_idx); + } +} + /* * mem_noshuf * Section 5.5 of the Hexagon V67 Programmer's Reference Manual @@ -1181,6 +1294,171 @@ float64 HELPER(dfmpyhh)(CPUHexagonState *env, float64 RxxV, return RxxV; } +/* Histogram instructions */ + +void HELPER(vhist)(CPUHexagonState *env) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int lane = 0; lane < 8; lane++) { + for (int i = 0; i < sizeof(MMVector) / 8; ++i) { + unsigned char value = input->ub[(sizeof(MMVector) / 8) * lane + i]; + unsigned char regno = value >> 3; + unsigned char element = value & 7; + + env->VRegs[regno].uh[(sizeof(MMVector) / 16) * lane + element]++; + } + } +} + +void HELPER(vhistq)(CPUHexagonState *env) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int lane = 0; lane < 8; lane++) { + for (int i = 0; i < sizeof(MMVector) / 8; ++i) { + unsigned char value = input->ub[(sizeof(MMVector) / 8) * lane + i]; + unsigned char regno = value >> 3; + unsigned char element = value & 7; + + if (fGETQBIT(env->qtmp, sizeof(MMVector) / 8 * lane + i)) { + env->VRegs[regno].uh[ + (sizeof(MMVector) / 16) * lane + element]++; + } + } + } +} + +void HELPER(vwhist256)(CPUHexagonState *env) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int i = 0; i < (sizeof(MMVector) / 2); i++) { + unsigned int bucket = fGETUBYTE(0, input->h[i]); + unsigned int weight = fGETUBYTE(1, input->h[i]); + unsigned int vindex = (bucket >> 3) & 0x1F; + unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7); + + env->VRegs[vindex].uh[elindex] = + env->VRegs[vindex].uh[elindex] + weight; + } +} + +void HELPER(vwhist256q)(CPUHexagonState *env) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int i = 0; i < (sizeof(MMVector) / 2); i++) { + unsigned int bucket = fGETUBYTE(0, input->h[i]); + unsigned int weight = fGETUBYTE(1, input->h[i]); + unsigned int vindex = (bucket >> 3) & 0x1F; + unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7); + + if (fGETQBIT(env->qtmp, 2 * i)) { + env->VRegs[vindex].uh[elindex] = + env->VRegs[vindex].uh[elindex] + weight; + } + } +} + +void HELPER(vwhist256_sat)(CPUHexagonState *env) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int i = 0; i < (sizeof(MMVector) / 2); i++) { + unsigned int bucket = fGETUBYTE(0, input->h[i]); + unsigned int weight = fGETUBYTE(1, input->h[i]); + unsigned int vindex = (bucket >> 3) & 0x1F; + unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7); + + env->VRegs[vindex].uh[elindex] = + fVSATUH(env->VRegs[vindex].uh[elindex] + weight); + } +} + +void HELPER(vwhist256q_sat)(CPUHexagonState *env) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int i = 0; i < (sizeof(MMVector) / 2); i++) { + unsigned int bucket = fGETUBYTE(0, input->h[i]); + unsigned int weight = fGETUBYTE(1, input->h[i]); + unsigned int vindex = (bucket >> 3) & 0x1F; + unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7); + + if (fGETQBIT(env->qtmp, 2 * i)) { + env->VRegs[vindex].uh[elindex] = + fVSATUH(env->VRegs[vindex].uh[elindex] + weight); + } + } +} + +void HELPER(vwhist128)(CPUHexagonState *env) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int i = 0; i < (sizeof(MMVector) / 2); i++) { + unsigned int bucket = fGETUBYTE(0, input->h[i]); + unsigned int weight = fGETUBYTE(1, input->h[i]); + unsigned int vindex = (bucket >> 3) & 0x1F; + unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3); + + env->VRegs[vindex].uw[elindex] = + env->VRegs[vindex].uw[elindex] + weight; + } +} + +void HELPER(vwhist128q)(CPUHexagonState *env) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int i = 0; i < (sizeof(MMVector) / 2); i++) { + unsigned int bucket = fGETUBYTE(0, input->h[i]); + unsigned int weight = fGETUBYTE(1, input->h[i]); + unsigned int vindex = (bucket >> 3) & 0x1F; + unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3); + + if (fGETQBIT(env->qtmp, 2 * i)) { + env->VRegs[vindex].uw[elindex] = + env->VRegs[vindex].uw[elindex] + weight; + } + } +} + +void HELPER(vwhist128m)(CPUHexagonState *env, int32_t uiV) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int i = 0; i < (sizeof(MMVector) / 2); i++) { + unsigned int bucket = fGETUBYTE(0, input->h[i]); + unsigned int weight = fGETUBYTE(1, input->h[i]); + unsigned int vindex = (bucket >> 3) & 0x1F; + unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3); + + if ((bucket & 1) == uiV) { + env->VRegs[vindex].uw[elindex] = + env->VRegs[vindex].uw[elindex] + weight; + } + } +} + +void HELPER(vwhist128qm)(CPUHexagonState *env, int32_t uiV) +{ + MMVector *input = &env->tmp_VRegs[0]; + + for (int i = 0; i < (sizeof(MMVector) / 2); i++) { + unsigned int bucket = fGETUBYTE(0, input->h[i]); + unsigned int weight = fGETUBYTE(1, input->h[i]); + unsigned int vindex = (bucket >> 3) & 0x1F; + unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3); + + if (((bucket & 1) == uiV) && fGETQBIT(env->qtmp, 2 * i)) { + env->VRegs[vindex].uw[elindex] = + env->VRegs[vindex].uw[elindex] + weight; + } + } +} + static void cancel_slot(CPUHexagonState *env, uint32_t slot) { HEX_DEBUG_LOG("Slot %d cancelled\n", slot); From a82dd54862ec1c1c5c709d0d2b0161a00c6a19ce Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Thu, 30 Sep 2021 14:29:00 -0500 Subject: [PATCH 1171/1334] Hexagon HVX (target/hexagon) TCG generation Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/genptr.c | 15 +++ target/hexagon/translate.c | 239 ++++++++++++++++++++++++++++++++++++- target/hexagon/translate.h | 61 ++++++++++ 3 files changed, 311 insertions(+), 4 deletions(-) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 4a21fa590f..d16ff74f88 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -165,6 +165,9 @@ static inline void gen_read_ctrl_reg(DisasContext *ctx, const int reg_num, } else if (reg_num == HEX_REG_QEMU_INSN_CNT) { tcg_gen_addi_tl(dest, hex_gpr[HEX_REG_QEMU_INSN_CNT], ctx->num_insns); + } else if (reg_num == HEX_REG_QEMU_HVX_CNT) { + tcg_gen_addi_tl(dest, hex_gpr[HEX_REG_QEMU_HVX_CNT], + ctx->num_hvx_insns); } else { tcg_gen_mov_tl(dest, hex_gpr[reg_num]); } @@ -191,6 +194,12 @@ static inline void gen_read_ctrl_reg_pair(DisasContext *ctx, const int reg_num, tcg_gen_concat_i32_i64(dest, pkt_cnt, insn_cnt); tcg_temp_free(pkt_cnt); tcg_temp_free(insn_cnt); + } else if (reg_num == HEX_REG_QEMU_HVX_CNT) { + TCGv hvx_cnt = tcg_temp_new(); + tcg_gen_addi_tl(hvx_cnt, hex_gpr[HEX_REG_QEMU_HVX_CNT], + ctx->num_hvx_insns); + tcg_gen_concat_i32_i64(dest, hvx_cnt, hex_gpr[reg_num + 1]); + tcg_temp_free(hvx_cnt); } else { tcg_gen_concat_i32_i64(dest, hex_gpr[reg_num], @@ -226,6 +235,9 @@ static inline void gen_write_ctrl_reg(DisasContext *ctx, int reg_num, if (reg_num == HEX_REG_QEMU_INSN_CNT) { ctx->num_insns = 0; } + if (reg_num == HEX_REG_QEMU_HVX_CNT) { + ctx->num_hvx_insns = 0; + } } } @@ -247,6 +259,9 @@ static inline void gen_write_ctrl_reg_pair(DisasContext *ctx, int reg_num, ctx->num_packets = 0; ctx->num_insns = 0; } + if (reg_num == HEX_REG_QEMU_HVX_CNT) { + ctx->num_hvx_insns = 0; + } } } diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index e10ef36c5c..b6f541ecb2 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "internal.h" @@ -47,11 +48,60 @@ TCGv hex_dczero_addr; TCGv hex_llsc_addr; TCGv hex_llsc_val; TCGv_i64 hex_llsc_val_i64; +TCGv hex_VRegs_updated; +TCGv hex_QRegs_updated; +TCGv hex_vstore_addr[VSTORES_MAX]; +TCGv hex_vstore_size[VSTORES_MAX]; +TCGv hex_vstore_pending[VSTORES_MAX]; static const char * const hexagon_prednames[] = { "p0", "p1", "p2", "p3" }; +intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum, + int num, bool alloc_ok) +{ + intptr_t offset; + + /* See if it is already allocated */ + for (int i = 0; i < ctx->future_vregs_idx; i++) { + if (ctx->future_vregs_num[i] == regnum) { + return offsetof(CPUHexagonState, future_VRegs[i]); + } + } + + g_assert(alloc_ok); + offset = offsetof(CPUHexagonState, future_VRegs[ctx->future_vregs_idx]); + for (int i = 0; i < num; i++) { + ctx->future_vregs_num[ctx->future_vregs_idx + i] = regnum++; + } + ctx->future_vregs_idx += num; + g_assert(ctx->future_vregs_idx <= VECTOR_TEMPS_MAX); + return offset; +} + +intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum, + int num, bool alloc_ok) +{ + intptr_t offset; + + /* See if it is already allocated */ + for (int i = 0; i < ctx->tmp_vregs_idx; i++) { + if (ctx->tmp_vregs_num[i] == regnum) { + return offsetof(CPUHexagonState, tmp_VRegs[i]); + } + } + + g_assert(alloc_ok); + offset = offsetof(CPUHexagonState, tmp_VRegs[ctx->tmp_vregs_idx]); + for (int i = 0; i < num; i++) { + ctx->tmp_vregs_num[ctx->tmp_vregs_idx + i] = regnum++; + } + ctx->tmp_vregs_idx += num; + g_assert(ctx->tmp_vregs_idx <= VECTOR_TEMPS_MAX); + return offset; +} + static void gen_exception_raw(int excp) { gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); @@ -63,6 +113,8 @@ static void gen_exec_counters(DisasContext *ctx) hex_gpr[HEX_REG_QEMU_PKT_CNT], ctx->num_packets); tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_INSN_CNT], hex_gpr[HEX_REG_QEMU_INSN_CNT], ctx->num_insns); + tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_HVX_CNT], + hex_gpr[HEX_REG_QEMU_HVX_CNT], ctx->num_hvx_insns); } static void gen_end_tb(DisasContext *ctx) @@ -167,11 +219,19 @@ static void gen_start_packet(DisasContext *ctx, Packet *pkt) bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS); ctx->preg_log_idx = 0; bitmap_zero(ctx->pregs_written, NUM_PREGS); + ctx->future_vregs_idx = 0; + ctx->tmp_vregs_idx = 0; + ctx->vreg_log_idx = 0; + bitmap_zero(ctx->vregs_updated_tmp, NUM_VREGS); + bitmap_zero(ctx->vregs_updated, NUM_VREGS); + bitmap_zero(ctx->vregs_select, NUM_VREGS); + ctx->qreg_log_idx = 0; for (i = 0; i < STORES_MAX; i++) { ctx->store_width[i] = 0; } tcg_gen_movi_tl(hex_pkt_has_store_s1, pkt->pkt_has_store_s1); ctx->s1_store_processed = false; + ctx->pre_commit = true; if (HEX_DEBUG) { /* Handy place to set a breakpoint before the packet executes */ @@ -193,6 +253,26 @@ static void gen_start_packet(DisasContext *ctx, Packet *pkt) if (need_pred_written(pkt)) { tcg_gen_movi_tl(hex_pred_written, 0); } + + if (pkt->pkt_has_hvx) { + tcg_gen_movi_tl(hex_VRegs_updated, 0); + tcg_gen_movi_tl(hex_QRegs_updated, 0); + } +} + +bool is_gather_store_insn(Insn *insn, Packet *pkt) +{ + if (GET_ATTRIB(insn->opcode, A_CVI_NEW) && + insn->new_value_producer_slot == 1) { + /* Look for gather instruction */ + for (int i = 0; i < pkt->num_insns; i++) { + Insn *in = &pkt->insn[i]; + if (GET_ATTRIB(in->opcode, A_CVI_GATHER) && in->slot == 1) { + return true; + } + } + } + return false; } /* @@ -448,10 +528,98 @@ static void process_dczeroa(DisasContext *ctx, Packet *pkt) } } +static bool pkt_has_hvx_store(Packet *pkt) +{ + int i; + for (i = 0; i < pkt->num_insns; i++) { + int opcode = pkt->insn[i].opcode; + if (GET_ATTRIB(opcode, A_CVI) && GET_ATTRIB(opcode, A_STORE)) { + return true; + } + } + return false; +} + +static void gen_commit_hvx(DisasContext *ctx, Packet *pkt) +{ + int i; + + /* + * for (i = 0; i < ctx->vreg_log_idx; i++) { + * int rnum = ctx->vreg_log[i]; + * if (ctx->vreg_is_predicated[i]) { + * if (env->VRegs_updated & (1 << rnum)) { + * env->VRegs[rnum] = env->future_VRegs[rnum]; + * } + * } else { + * env->VRegs[rnum] = env->future_VRegs[rnum]; + * } + * } + */ + for (i = 0; i < ctx->vreg_log_idx; i++) { + int rnum = ctx->vreg_log[i]; + bool is_predicated = ctx->vreg_is_predicated[i]; + intptr_t dstoff = offsetof(CPUHexagonState, VRegs[rnum]); + intptr_t srcoff = ctx_future_vreg_off(ctx, rnum, 1, false); + size_t size = sizeof(MMVector); + + if (is_predicated) { + TCGv cmp = tcg_temp_new(); + TCGLabel *label_skip = gen_new_label(); + + tcg_gen_andi_tl(cmp, hex_VRegs_updated, 1 << rnum); + tcg_gen_brcondi_tl(TCG_COND_EQ, cmp, 0, label_skip); + tcg_temp_free(cmp); + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); + gen_set_label(label_skip); + } else { + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); + } + } + + /* + * for (i = 0; i < ctx->qreg_log_idx; i++) { + * int rnum = ctx->qreg_log[i]; + * if (ctx->qreg_is_predicated[i]) { + * if (env->QRegs_updated) & (1 << rnum)) { + * env->QRegs[rnum] = env->future_QRegs[rnum]; + * } + * } else { + * env->QRegs[rnum] = env->future_QRegs[rnum]; + * } + * } + */ + for (i = 0; i < ctx->qreg_log_idx; i++) { + int rnum = ctx->qreg_log[i]; + bool is_predicated = ctx->qreg_is_predicated[i]; + intptr_t dstoff = offsetof(CPUHexagonState, QRegs[rnum]); + intptr_t srcoff = offsetof(CPUHexagonState, future_QRegs[rnum]); + size_t size = sizeof(MMQReg); + + if (is_predicated) { + TCGv cmp = tcg_temp_new(); + TCGLabel *label_skip = gen_new_label(); + + tcg_gen_andi_tl(cmp, hex_QRegs_updated, 1 << rnum); + tcg_gen_brcondi_tl(TCG_COND_EQ, cmp, 0, label_skip); + tcg_temp_free(cmp); + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); + gen_set_label(label_skip); + } else { + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); + } + } + + if (pkt_has_hvx_store(pkt)) { + gen_helper_commit_hvx_stores(cpu_env); + } +} + static void update_exec_counters(DisasContext *ctx, Packet *pkt) { int num_insns = pkt->num_insns; int num_real_insns = 0; + int num_hvx_insns = 0; for (int i = 0; i < num_insns; i++) { if (!pkt->insn[i].is_endloop && @@ -459,13 +627,18 @@ static void update_exec_counters(DisasContext *ctx, Packet *pkt) !GET_ATTRIB(pkt->insn[i].opcode, A_IT_NOP)) { num_real_insns++; } + if (GET_ATTRIB(pkt->insn[i].opcode, A_CVI)) { + num_hvx_insns++; + } } ctx->num_packets++; ctx->num_insns += num_real_insns; + ctx->num_hvx_insns += num_hvx_insns; } -static void gen_commit_packet(DisasContext *ctx, Packet *pkt) +static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx, + Packet *pkt) { /* * If there is more than one store in a packet, make sure they are all OK @@ -474,6 +647,10 @@ static void gen_commit_packet(DisasContext *ctx, Packet *pkt) * dczeroa has to be the only store operation in the packet, so we go * ahead and process that first. * + * When there is an HVX store, there can also be a scalar store in either + * slot 0 or slot1, so we create a mask for the helper to indicate what + * work to do. + * * When there are two scalar stores, we probe the one in slot 0. * * Note that we don't call the probe helper for packets with only one @@ -482,13 +659,35 @@ static void gen_commit_packet(DisasContext *ctx, Packet *pkt) */ bool has_store_s0 = pkt->pkt_has_store_s0; bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed); + bool has_hvx_store = pkt_has_hvx_store(pkt); if (pkt->pkt_has_dczeroa) { /* * The dczeroa will be the store in slot 0, check that we don't have - * a store in slot 1. + * a store in slot 1 or an HVX store. */ - g_assert(has_store_s0 && !has_store_s1); + g_assert(has_store_s0 && !has_store_s1 && !has_hvx_store); process_dczeroa(ctx, pkt); + } else if (has_hvx_store) { + TCGv mem_idx = tcg_constant_tl(ctx->mem_idx); + + if (!has_store_s0 && !has_store_s1) { + gen_helper_probe_hvx_stores(cpu_env, mem_idx); + } else { + int mask = 0; + TCGv mask_tcgv; + + if (has_store_s0) { + mask |= (1 << 0); + } + if (has_store_s1) { + mask |= (1 << 1); + } + if (has_hvx_store) { + mask |= (1 << 2); + } + mask_tcgv = tcg_constant_tl(mask); + gen_helper_probe_pkt_scalar_hvx_stores(cpu_env, mask_tcgv, mem_idx); + } } else if (has_store_s0 && has_store_s1) { /* * process_store_log will execute the slot 1 store first, @@ -502,6 +701,9 @@ static void gen_commit_packet(DisasContext *ctx, Packet *pkt) gen_reg_writes(ctx); gen_pred_writes(ctx, pkt); + if (pkt->pkt_has_hvx) { + gen_commit_hvx(ctx, pkt); + } update_exec_counters(ctx, pkt); if (HEX_DEBUG) { TCGv has_st0 = @@ -513,6 +715,11 @@ static void gen_commit_packet(DisasContext *ctx, Packet *pkt) gen_helper_debug_commit_end(cpu_env, has_st0, has_st1); } + if (pkt->vhist_insn != NULL) { + ctx->pre_commit = false; + pkt->vhist_insn->generate(env, ctx, pkt->vhist_insn, pkt); + } + if (pkt->pkt_has_cof) { gen_end_tb(ctx); } @@ -537,7 +744,7 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) for (i = 0; i < pkt.num_insns; i++) { gen_insn(env, ctx, &pkt.insn[i], &pkt); } - gen_commit_packet(ctx, &pkt); + gen_commit_packet(env, ctx, &pkt); ctx->base.pc_next += pkt.encod_pkt_size_in_bytes; } else { gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET); @@ -552,6 +759,7 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase, ctx->mem_idx = MMU_USER_IDX; ctx->num_packets = 0; ctx->num_insns = 0; + ctx->num_hvx_insns = 0; } static void hexagon_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -656,6 +864,9 @@ static char store_addr_names[STORES_MAX][NAME_LEN]; static char store_width_names[STORES_MAX][NAME_LEN]; static char store_val32_names[STORES_MAX][NAME_LEN]; static char store_val64_names[STORES_MAX][NAME_LEN]; +static char vstore_addr_names[VSTORES_MAX][NAME_LEN]; +static char vstore_size_names[VSTORES_MAX][NAME_LEN]; +static char vstore_pending_names[VSTORES_MAX][NAME_LEN]; void hexagon_translate_init(void) { @@ -718,6 +929,10 @@ void hexagon_translate_init(void) offsetof(CPUHexagonState, llsc_val), "llsc_val"); hex_llsc_val_i64 = tcg_global_mem_new_i64(cpu_env, offsetof(CPUHexagonState, llsc_val_i64), "llsc_val_i64"); + hex_VRegs_updated = tcg_global_mem_new(cpu_env, + offsetof(CPUHexagonState, VRegs_updated), "VRegs_updated"); + hex_QRegs_updated = tcg_global_mem_new(cpu_env, + offsetof(CPUHexagonState, QRegs_updated), "QRegs_updated"); for (i = 0; i < STORES_MAX; i++) { snprintf(store_addr_names[i], NAME_LEN, "store_addr_%d", i); hex_store_addr[i] = tcg_global_mem_new(cpu_env, @@ -739,4 +954,20 @@ void hexagon_translate_init(void) offsetof(CPUHexagonState, mem_log_stores[i].data64), store_val64_names[i]); } + for (int i = 0; i < VSTORES_MAX; i++) { + snprintf(vstore_addr_names[i], NAME_LEN, "vstore_addr_%d", i); + hex_vstore_addr[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUHexagonState, vstore[i].va), + vstore_addr_names[i]); + + snprintf(vstore_size_names[i], NAME_LEN, "vstore_size_%d", i); + hex_vstore_size[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUHexagonState, vstore[i].size), + vstore_size_names[i]); + + snprintf(vstore_pending_names[i], NAME_LEN, "vstore_pending_%d", i); + hex_vstore_pending[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUHexagonState, vstore_pending[i]), + vstore_pending_names[i]); + } } diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 703fd1345f..fccfb94340 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -29,6 +29,7 @@ typedef struct DisasContext { uint32_t mem_idx; uint32_t num_packets; uint32_t num_insns; + uint32_t num_hvx_insns; int reg_log[REG_WRITES_MAX]; int reg_log_idx; DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); @@ -37,6 +38,20 @@ typedef struct DisasContext { DECLARE_BITMAP(pregs_written, NUM_PREGS); uint8_t store_width[STORES_MAX]; bool s1_store_processed; + int future_vregs_idx; + int future_vregs_num[VECTOR_TEMPS_MAX]; + int tmp_vregs_idx; + int tmp_vregs_num[VECTOR_TEMPS_MAX]; + int vreg_log[NUM_VREGS]; + bool vreg_is_predicated[NUM_VREGS]; + int vreg_log_idx; + DECLARE_BITMAP(vregs_updated_tmp, NUM_VREGS); + DECLARE_BITMAP(vregs_updated, NUM_VREGS); + DECLARE_BITMAP(vregs_select, NUM_VREGS); + int qreg_log[NUM_QREGS]; + bool qreg_is_predicated[NUM_QREGS]; + int qreg_log_idx; + bool pre_commit; } DisasContext; static inline void ctx_log_reg_write(DisasContext *ctx, int rnum) @@ -67,6 +82,46 @@ static inline bool is_preloaded(DisasContext *ctx, int num) return test_bit(num, ctx->regs_written); } +intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum, + int num, bool alloc_ok); +intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum, + int num, bool alloc_ok); + +static inline void ctx_log_vreg_write(DisasContext *ctx, + int rnum, VRegWriteType type, + bool is_predicated) +{ + if (type != EXT_TMP) { + ctx->vreg_log[ctx->vreg_log_idx] = rnum; + ctx->vreg_is_predicated[ctx->vreg_log_idx] = is_predicated; + ctx->vreg_log_idx++; + + set_bit(rnum, ctx->vregs_updated); + } + if (type == EXT_NEW) { + set_bit(rnum, ctx->vregs_select); + } + if (type == EXT_TMP) { + set_bit(rnum, ctx->vregs_updated_tmp); + } +} + +static inline void ctx_log_vreg_write_pair(DisasContext *ctx, + int rnum, VRegWriteType type, + bool is_predicated) +{ + ctx_log_vreg_write(ctx, rnum ^ 0, type, is_predicated); + ctx_log_vreg_write(ctx, rnum ^ 1, type, is_predicated); +} + +static inline void ctx_log_qreg_write(DisasContext *ctx, + int rnum, bool is_predicated) +{ + ctx->qreg_log[ctx->qreg_log_idx] = rnum; + ctx->qreg_is_predicated[ctx->qreg_log_idx] = is_predicated; + ctx->qreg_log_idx++; +} + extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; extern TCGv hex_pred[NUM_PREGS]; extern TCGv hex_next_PC; @@ -85,6 +140,12 @@ extern TCGv hex_dczero_addr; extern TCGv hex_llsc_addr; extern TCGv hex_llsc_val; extern TCGv_i64 hex_llsc_val_i64; +extern TCGv hex_VRegs_updated; +extern TCGv hex_QRegs_updated; +extern TCGv hex_vstore_addr[VSTORES_MAX]; +extern TCGv hex_vstore_size[VSTORES_MAX]; +extern TCGv hex_vstore_pending[VSTORES_MAX]; +bool is_gather_store_insn(Insn *insn, Packet *pkt); void process_store(DisasContext *ctx, Packet *pkt, int slot_num); #endif From d51bcabec1ef5e3ae45d21875c9b2f30ff26c432 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 18 May 2021 12:01:09 -0500 Subject: [PATCH 1172/1334] Hexagon HVX (target/hexagon) helper overrides infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build the infrastructure to create overrides for HVX instructions. We create a new empty file (gen_tcg_hvx.h) that will be populated in subsequent patches. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_helper_funcs.py | 3 ++- target/hexagon/gen_helper_protos.py | 3 ++- target/hexagon/gen_tcg_funcs.py | 3 ++- target/hexagon/gen_tcg_hvx.h | 21 +++++++++++++++++++++ target/hexagon/genptr.c | 1 + target/hexagon/meson.build | 13 +++++++------ 6 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 target/hexagon/gen_tcg_hvx.h diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index ac5ce1023c..a446c45384 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py @@ -286,11 +286,12 @@ def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) hex_common.read_overrides_file(sys.argv[3]) + hex_common.read_overrides_file(sys.argv[4]) hex_common.calculate_attribs() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[4], 'w') as f: + with open(sys.argv[5], 'w') as f: for tag in hex_common.tags: ## Skip the priv instructions if ( "A_PRIV" in hex_common.attribdict[tag] ) : diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index 229ef8dbd2..3b4e993fd1 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py @@ -135,11 +135,12 @@ def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) hex_common.read_overrides_file(sys.argv[3]) + hex_common.read_overrides_file(sys.argv[4]) hex_common.calculate_attribs() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[4], 'w') as f: + with open(sys.argv[5], 'w') as f: for tag in hex_common.tags: ## Skip the priv instructions if ( "A_PRIV" in hex_common.attribdict[tag] ) : diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index 691ff6a949..1fd9de95d5 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -675,11 +675,12 @@ def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) hex_common.read_overrides_file(sys.argv[3]) + hex_common.read_overrides_file(sys.argv[4]) hex_common.calculate_attribs() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[4], 'w') as f: + with open(sys.argv[5], 'w') as f: f.write("#ifndef HEXAGON_TCG_FUNCS_H\n") f.write("#define HEXAGON_TCG_FUNCS_H\n\n") diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h new file mode 100644 index 0000000000..b5c6cadd73 --- /dev/null +++ b/target/hexagon/gen_tcg_hvx.h @@ -0,0 +1,21 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HEXAGON_GEN_TCG_HVX_H +#define HEXAGON_GEN_TCG_HVX_H + +#endif diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index d16ff74f88..473438a6c7 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -26,6 +26,7 @@ #include "macros.h" #undef QEMU_GENERATE #include "gen_tcg.h" +#include "gen_tcg_hvx.h" static inline void gen_log_predicated_reg_write(int rnum, TCGv val, int slot) { diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index 0bfaa41ec3..a35eb2877e 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -20,6 +20,7 @@ hexagon_ss = ss.source_set() hex_common_py = 'hex_common.py' attribs_def = meson.current_source_dir() / 'attribs_def.h.inc' gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h' +gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h' # # Step 1 @@ -63,8 +64,8 @@ helper_protos_generated = custom_target( 'helper_protos_generated.h.inc', output: 'helper_protos_generated.h.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def, gen_tcg_h], - command: [python, files('gen_helper_protos.py'), semantics_generated, attribs_def, gen_tcg_h, '@OUTPUT@'], + depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + command: [python, files('gen_helper_protos.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'], ) hexagon_ss.add(helper_protos_generated) @@ -72,8 +73,8 @@ tcg_funcs_generated = custom_target( 'tcg_funcs_generated.c.inc', output: 'tcg_funcs_generated.c.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def, gen_tcg_h], - command: [python, files('gen_tcg_funcs.py'), semantics_generated, attribs_def, gen_tcg_h, '@OUTPUT@'], + depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + command: [python, files('gen_tcg_funcs.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'], ) hexagon_ss.add(tcg_funcs_generated) @@ -90,8 +91,8 @@ helper_funcs_generated = custom_target( 'helper_funcs_generated.c.inc', output: 'helper_funcs_generated.c.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def, gen_tcg_h], - command: [python, files('gen_helper_funcs.py'), semantics_generated, attribs_def, gen_tcg_h, '@OUTPUT@'], + depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + command: [python, files('gen_helper_funcs.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'], ) hexagon_ss.add(helper_funcs_generated) From 7ba7657bc9359b74576efe1b7b4de58271ea97b9 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Fri, 13 Aug 2021 00:22:23 -0500 Subject: [PATCH 1173/1334] Hexagon HVX (target/hexagon) helper overrides for histogram instructions Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 106 +++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index b5c6cadd73..a560504b78 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -18,4 +18,110 @@ #ifndef HEXAGON_GEN_TCG_HVX_H #define HEXAGON_GEN_TCG_HVX_H +/* + * Histogram instructions + * + * Note that these instructions operate directly on the vector registers + * and therefore happen after commit. + * + * The generate_ function is called twice + * The first time is during the normal TCG generation + * ctx->pre_commit is true + * In the masked cases, we save the mask to the qtmp temporary + * Otherwise, there is nothing to do + * The second call is at the end of gen_commit_packet + * ctx->pre_commit is false + * Generate the call to the helper + */ + +static inline void assert_vhist_tmp(DisasContext *ctx) +{ + /* vhist instructions require exactly one .tmp to be defined */ + g_assert(ctx->tmp_vregs_idx == 1); +} + +#define fGEN_TCG_V6_vhist(SHORTCODE) \ + if (!ctx->pre_commit) { \ + assert_vhist_tmp(ctx); \ + gen_helper_vhist(cpu_env); \ + } +#define fGEN_TCG_V6_vhistq(SHORTCODE) \ + do { \ + if (ctx->pre_commit) { \ + intptr_t dstoff = offsetof(CPUHexagonState, qtmp); \ + tcg_gen_gvec_mov(MO_64, dstoff, QvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + } else { \ + assert_vhist_tmp(ctx); \ + gen_helper_vhistq(cpu_env); \ + } \ + } while (0) +#define fGEN_TCG_V6_vwhist256(SHORTCODE) \ + if (!ctx->pre_commit) { \ + assert_vhist_tmp(ctx); \ + gen_helper_vwhist256(cpu_env); \ + } +#define fGEN_TCG_V6_vwhist256q(SHORTCODE) \ + do { \ + if (ctx->pre_commit) { \ + intptr_t dstoff = offsetof(CPUHexagonState, qtmp); \ + tcg_gen_gvec_mov(MO_64, dstoff, QvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + } else { \ + assert_vhist_tmp(ctx); \ + gen_helper_vwhist256q(cpu_env); \ + } \ + } while (0) +#define fGEN_TCG_V6_vwhist256_sat(SHORTCODE) \ + if (!ctx->pre_commit) { \ + assert_vhist_tmp(ctx); \ + gen_helper_vwhist256_sat(cpu_env); \ + } +#define fGEN_TCG_V6_vwhist256q_sat(SHORTCODE) \ + do { \ + if (ctx->pre_commit) { \ + intptr_t dstoff = offsetof(CPUHexagonState, qtmp); \ + tcg_gen_gvec_mov(MO_64, dstoff, QvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + } else { \ + assert_vhist_tmp(ctx); \ + gen_helper_vwhist256q_sat(cpu_env); \ + } \ + } while (0) +#define fGEN_TCG_V6_vwhist128(SHORTCODE) \ + if (!ctx->pre_commit) { \ + assert_vhist_tmp(ctx); \ + gen_helper_vwhist128(cpu_env); \ + } +#define fGEN_TCG_V6_vwhist128q(SHORTCODE) \ + do { \ + if (ctx->pre_commit) { \ + intptr_t dstoff = offsetof(CPUHexagonState, qtmp); \ + tcg_gen_gvec_mov(MO_64, dstoff, QvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + } else { \ + assert_vhist_tmp(ctx); \ + gen_helper_vwhist128q(cpu_env); \ + } \ + } while (0) +#define fGEN_TCG_V6_vwhist128m(SHORTCODE) \ + if (!ctx->pre_commit) { \ + TCGv tcgv_uiV = tcg_constant_tl(uiV); \ + assert_vhist_tmp(ctx); \ + gen_helper_vwhist128m(cpu_env, tcgv_uiV); \ + } +#define fGEN_TCG_V6_vwhist128qm(SHORTCODE) \ + do { \ + if (ctx->pre_commit) { \ + intptr_t dstoff = offsetof(CPUHexagonState, qtmp); \ + tcg_gen_gvec_mov(MO_64, dstoff, QvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + } else { \ + TCGv tcgv_uiV = tcg_constant_tl(uiV); \ + assert_vhist_tmp(ctx); \ + gen_helper_vwhist128qm(cpu_env, tcgv_uiV); \ + } \ + } while (0) + + #endif From 32488192c7fcfc9818ed1a59997a6cca7f0fcf50 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Fri, 13 Aug 2021 00:31:12 -0500 Subject: [PATCH 1174/1334] Hexagon HVX (target/hexagon) helper overrides - vector assign & cmov Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index a560504b78..916230e5d8 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -124,4 +124,35 @@ static inline void assert_vhist_tmp(DisasContext *ctx) } while (0) +#define fGEN_TCG_V6_vassign(SHORTCODE) \ + tcg_gen_gvec_mov(MO_64, VdV_off, VuV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +/* Vector conditional move */ +#define fGEN_TCG_VEC_CMOV(PRED) \ + do { \ + TCGv lsb = tcg_temp_new(); \ + TCGLabel *false_label = gen_new_label(); \ + TCGLabel *end_label = gen_new_label(); \ + tcg_gen_andi_tl(lsb, PsV, 1); \ + tcg_gen_brcondi_tl(TCG_COND_NE, lsb, PRED, false_label); \ + tcg_temp_free(lsb); \ + tcg_gen_gvec_mov(MO_64, VdV_off, VuV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_br(end_label); \ + gen_set_label(false_label); \ + tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \ + 1 << insn->slot); \ + gen_set_label(end_label); \ + } while (0) + + +/* Vector conditional move (true) */ +#define fGEN_TCG_V6_vcmov(SHORTCODE) \ + fGEN_TCG_VEC_CMOV(1) + +/* Vector conditional move (false) */ +#define fGEN_TCG_V6_vncmov(SHORTCODE) \ + fGEN_TCG_VEC_CMOV(0) + #endif From 928f0ce4e8336c1984fa1a3481bb8a312827020b Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 3 Aug 2021 13:33:55 -0500 Subject: [PATCH 1175/1334] Hexagon HVX (target/hexagon) helper overrides - vector add & sub Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 916230e5d8..ac2143e5e1 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -155,4 +155,54 @@ static inline void assert_vhist_tmp(DisasContext *ctx) #define fGEN_TCG_V6_vncmov(SHORTCODE) \ fGEN_TCG_VEC_CMOV(0) +/* Vector add - various forms */ +#define fGEN_TCG_V6_vaddb(SHORTCODE) \ + tcg_gen_gvec_add(MO_8, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vaddh(SHORTCYDE) \ + tcg_gen_gvec_add(MO_16, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vaddw(SHORTCODE) \ + tcg_gen_gvec_add(MO_32, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vaddb_dv(SHORTCODE) \ + tcg_gen_gvec_add(MO_8, VddV_off, VuuV_off, VvvV_off, \ + sizeof(MMVector) * 2, sizeof(MMVector) * 2) + +#define fGEN_TCG_V6_vaddh_dv(SHORTCYDE) \ + tcg_gen_gvec_add(MO_16, VddV_off, VuuV_off, VvvV_off, \ + sizeof(MMVector) * 2, sizeof(MMVector) * 2) + +#define fGEN_TCG_V6_vaddw_dv(SHORTCODE) \ + tcg_gen_gvec_add(MO_32, VddV_off, VuuV_off, VvvV_off, \ + sizeof(MMVector) * 2, sizeof(MMVector) * 2) + +/* Vector sub - various forms */ +#define fGEN_TCG_V6_vsubb(SHORTCODE) \ + tcg_gen_gvec_sub(MO_8, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vsubh(SHORTCODE) \ + tcg_gen_gvec_sub(MO_16, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vsubw(SHORTCODE) \ + tcg_gen_gvec_sub(MO_32, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vsubb_dv(SHORTCODE) \ + tcg_gen_gvec_sub(MO_8, VddV_off, VuuV_off, VvvV_off, \ + sizeof(MMVector) * 2, sizeof(MMVector) * 2) + +#define fGEN_TCG_V6_vsubh_dv(SHORTCODE) \ + tcg_gen_gvec_sub(MO_16, VddV_off, VuuV_off, VvvV_off, \ + sizeof(MMVector) * 2, sizeof(MMVector) * 2) + +#define fGEN_TCG_V6_vsubw_dv(SHORTCODE) \ + tcg_gen_gvec_sub(MO_32, VddV_off, VuuV_off, VvvV_off, \ + sizeof(MMVector) * 2, sizeof(MMVector) * 2) + #endif From 8866635cafbb7b29ca622d14116b6220bd99b7ec Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 3 Aug 2021 13:34:58 -0500 Subject: [PATCH 1176/1334] Hexagon HVX (target/hexagon) helper overrides - vector shifts Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 122 +++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index ac2143e5e1..e86541052b 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -205,4 +205,126 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_gvec_sub(MO_32, VddV_off, VuuV_off, VvvV_off, \ sizeof(MMVector) * 2, sizeof(MMVector) * 2) +/* Vector shift right - various forms */ +#define fGEN_TCG_V6_vasrh(SHORTCODE) \ + do { \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 15); \ + tcg_gen_gvec_sars(MO_16, VdV_off, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vasrh_acc(SHORTCODE) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 15); \ + tcg_gen_gvec_sars(MO_16, tmpoff, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_gvec_add(MO_16, VxV_off, VxV_off, tmpoff, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vasrw(SHORTCODE) \ + do { \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 31); \ + tcg_gen_gvec_sars(MO_32, VdV_off, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vasrw_acc(SHORTCODE) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 31); \ + tcg_gen_gvec_sars(MO_32, tmpoff, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_gvec_add(MO_32, VxV_off, VxV_off, tmpoff, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vlsrb(SHORTCODE) \ + do { \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 7); \ + tcg_gen_gvec_shrs(MO_8, VdV_off, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vlsrh(SHORTCODE) \ + do { \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 15); \ + tcg_gen_gvec_shrs(MO_16, VdV_off, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vlsrw(SHORTCODE) \ + do { \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 31); \ + tcg_gen_gvec_shrs(MO_32, VdV_off, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +/* Vector shift left - various forms */ +#define fGEN_TCG_V6_vaslb(SHORTCODE) \ + do { \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 7); \ + tcg_gen_gvec_shls(MO_8, VdV_off, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vaslh(SHORTCODE) \ + do { \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 15); \ + tcg_gen_gvec_shls(MO_16, VdV_off, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vaslh_acc(SHORTCODE) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 15); \ + tcg_gen_gvec_shls(MO_16, tmpoff, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_gvec_add(MO_16, VxV_off, VxV_off, tmpoff, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vaslw(SHORTCODE) \ + do { \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 31); \ + tcg_gen_gvec_shls(MO_32, VdV_off, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + +#define fGEN_TCG_V6_vaslw_acc(SHORTCODE) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + TCGv shift = tcg_temp_new(); \ + tcg_gen_andi_tl(shift, RtV, 31); \ + tcg_gen_gvec_shls(MO_32, tmpoff, VuV_off, shift, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_gvec_add(MO_32, VxV_off, VxV_off, tmpoff, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_temp_free(shift); \ + } while (0) + #endif From 2c8ffa8f823e7f1a103971f48ae6c7a1eaf0be72 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 3 Aug 2021 13:35:58 -0500 Subject: [PATCH 1177/1334] Hexagon HVX (target/hexagon) helper overrides - vector max/min Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index e86541052b..f5484041b7 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -327,4 +327,38 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_temp_free(shift); \ } while (0) +/* Vector max - various forms */ +#define fGEN_TCG_V6_vmaxw(SHORTCODE) \ + tcg_gen_gvec_smax(MO_32, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vmaxh(SHORTCODE) \ + tcg_gen_gvec_smax(MO_16, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vmaxuh(SHORTCODE) \ + tcg_gen_gvec_umax(MO_16, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vmaxb(SHORTCODE) \ + tcg_gen_gvec_smax(MO_8, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vmaxub(SHORTCODE) \ + tcg_gen_gvec_umax(MO_8, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +/* Vector min - various forms */ +#define fGEN_TCG_V6_vminw(SHORTCODE) \ + tcg_gen_gvec_smin(MO_32, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vminh(SHORTCODE) \ + tcg_gen_gvec_smin(MO_16, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vminuh(SHORTCODE) \ + tcg_gen_gvec_umin(MO_16, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vminb(SHORTCODE) \ + tcg_gen_gvec_smin(MO_8, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vminub(SHORTCODE) \ + tcg_gen_gvec_umin(MO_8, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + #endif From 7f4808ec99189689c07ead85521b381b1562daf3 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 3 Aug 2021 13:36:42 -0500 Subject: [PATCH 1178/1334] Hexagon HVX (target/hexagon) helper overrides - vector logical ops Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index f5484041b7..f53a7f28bf 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -361,4 +361,46 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_gvec_umin(MO_8, VdV_off, VuV_off, VvV_off, \ sizeof(MMVector), sizeof(MMVector)) +/* Vector logical ops */ +#define fGEN_TCG_V6_vxor(SHORTCODE) \ + tcg_gen_gvec_xor(MO_64, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vand(SHORTCODE) \ + tcg_gen_gvec_and(MO_64, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vor(SHORTCODE) \ + tcg_gen_gvec_or(MO_64, VdV_off, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vnot(SHORTCODE) \ + tcg_gen_gvec_not(MO_64, VdV_off, VuV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +/* Q register logical ops */ +#define fGEN_TCG_V6_pred_or(SHORTCODE) \ + tcg_gen_gvec_or(MO_64, QdV_off, QsV_off, QtV_off, \ + sizeof(MMQReg), sizeof(MMQReg)) + +#define fGEN_TCG_V6_pred_and(SHORTCODE) \ + tcg_gen_gvec_and(MO_64, QdV_off, QsV_off, QtV_off, \ + sizeof(MMQReg), sizeof(MMQReg)) + +#define fGEN_TCG_V6_pred_xor(SHORTCODE) \ + tcg_gen_gvec_xor(MO_64, QdV_off, QsV_off, QtV_off, \ + sizeof(MMQReg), sizeof(MMQReg)) + +#define fGEN_TCG_V6_pred_or_n(SHORTCODE) \ + tcg_gen_gvec_orc(MO_64, QdV_off, QsV_off, QtV_off, \ + sizeof(MMQReg), sizeof(MMQReg)) + +#define fGEN_TCG_V6_pred_and_n(SHORTCODE) \ + tcg_gen_gvec_andc(MO_64, QdV_off, QsV_off, QtV_off, \ + sizeof(MMQReg), sizeof(MMQReg)) + +#define fGEN_TCG_V6_pred_not(SHORTCODE) \ + tcg_gen_gvec_not(MO_64, QdV_off, QsV_off, \ + sizeof(MMQReg), sizeof(MMQReg)) + #endif From 242a2c2c0e383d4699c0aaa23750dd083605e639 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Fri, 13 Aug 2021 11:54:16 -0500 Subject: [PATCH 1179/1334] Hexagon HVX (target/hexagon) helper overrides - vector compares Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 103 +++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index f53a7f28bf..32f8e209f7 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -403,4 +403,107 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_gvec_not(MO_64, QdV_off, QsV_off, \ sizeof(MMQReg), sizeof(MMQReg)) +/* Vector compares */ +#define fGEN_TCG_VEC_CMP(COND, TYPE, SIZE) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + tcg_gen_gvec_cmp(COND, TYPE, tmpoff, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + vec_to_qvec(SIZE, QdV_off, tmpoff); \ + } while (0) + +#define fGEN_TCG_V6_vgtw(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_GT, MO_32, 4) +#define fGEN_TCG_V6_vgth(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_GT, MO_16, 2) +#define fGEN_TCG_V6_vgtb(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_GT, MO_8, 1) + +#define fGEN_TCG_V6_vgtuw(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_GTU, MO_32, 4) +#define fGEN_TCG_V6_vgtuh(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_GTU, MO_16, 2) +#define fGEN_TCG_V6_vgtub(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_GTU, MO_8, 1) + +#define fGEN_TCG_V6_veqw(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_EQ, MO_32, 4) +#define fGEN_TCG_V6_veqh(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_EQ, MO_16, 2) +#define fGEN_TCG_V6_veqb(SHORTCODE) \ + fGEN_TCG_VEC_CMP(TCG_COND_EQ, MO_8, 1) + +#define fGEN_TCG_VEC_CMP_OP(COND, TYPE, SIZE, OP) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + intptr_t qoff = offsetof(CPUHexagonState, qtmp); \ + tcg_gen_gvec_cmp(COND, TYPE, tmpoff, VuV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + vec_to_qvec(SIZE, qoff, tmpoff); \ + OP(MO_64, QxV_off, QxV_off, qoff, sizeof(MMQReg), sizeof(MMQReg)); \ + } while (0) + +#define fGEN_TCG_V6_vgtw_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_32, 4, tcg_gen_gvec_and) +#define fGEN_TCG_V6_vgtw_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_32, 4, tcg_gen_gvec_or) +#define fGEN_TCG_V6_vgtw_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_32, 4, tcg_gen_gvec_xor) + +#define fGEN_TCG_V6_vgtuw_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_32, 4, tcg_gen_gvec_and) +#define fGEN_TCG_V6_vgtuw_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_32, 4, tcg_gen_gvec_or) +#define fGEN_TCG_V6_vgtuw_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_32, 4, tcg_gen_gvec_xor) + +#define fGEN_TCG_V6_vgth_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_16, 2, tcg_gen_gvec_and) +#define fGEN_TCG_V6_vgth_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_16, 2, tcg_gen_gvec_or) +#define fGEN_TCG_V6_vgth_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_16, 2, tcg_gen_gvec_xor) + +#define fGEN_TCG_V6_vgtuh_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_16, 2, tcg_gen_gvec_and) +#define fGEN_TCG_V6_vgtuh_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_16, 2, tcg_gen_gvec_or) +#define fGEN_TCG_V6_vgtuh_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_16, 2, tcg_gen_gvec_xor) + +#define fGEN_TCG_V6_vgtb_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_8, 1, tcg_gen_gvec_and) +#define fGEN_TCG_V6_vgtb_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_8, 1, tcg_gen_gvec_or) +#define fGEN_TCG_V6_vgtb_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GT, MO_8, 1, tcg_gen_gvec_xor) + +#define fGEN_TCG_V6_vgtub_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_8, 1, tcg_gen_gvec_and) +#define fGEN_TCG_V6_vgtub_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_8, 1, tcg_gen_gvec_or) +#define fGEN_TCG_V6_vgtub_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_GTU, MO_8, 1, tcg_gen_gvec_xor) + +#define fGEN_TCG_V6_veqw_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_32, 4, tcg_gen_gvec_and) +#define fGEN_TCG_V6_veqw_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_32, 4, tcg_gen_gvec_or) +#define fGEN_TCG_V6_veqw_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_32, 4, tcg_gen_gvec_xor) + +#define fGEN_TCG_V6_veqh_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_16, 2, tcg_gen_gvec_and) +#define fGEN_TCG_V6_veqh_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_16, 2, tcg_gen_gvec_or) +#define fGEN_TCG_V6_veqh_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_16, 2, tcg_gen_gvec_xor) + +#define fGEN_TCG_V6_veqb_and(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_8, 1, tcg_gen_gvec_and) +#define fGEN_TCG_V6_veqb_or(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_8, 1, tcg_gen_gvec_or) +#define fGEN_TCG_V6_veqb_xor(SHORTCODE) \ + fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_8, 1, tcg_gen_gvec_xor) + #endif From b0c2c182b9f31626b09ea48d586f4d2a7dd6dba5 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 3 Aug 2021 13:38:26 -0500 Subject: [PATCH 1180/1334] Hexagon HVX (target/hexagon) helper overrides - vector splat and abs Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 32f8e209f7..435c7b5301 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -506,4 +506,30 @@ static inline void assert_vhist_tmp(DisasContext *ctx) #define fGEN_TCG_V6_veqb_xor(SHORTCODE) \ fGEN_TCG_VEC_CMP_OP(TCG_COND_EQ, MO_8, 1, tcg_gen_gvec_xor) +/* Vector splat - various forms */ +#define fGEN_TCG_V6_lvsplatw(SHORTCODE) \ + tcg_gen_gvec_dup_i32(MO_32, VdV_off, \ + sizeof(MMVector), sizeof(MMVector), RtV) + +#define fGEN_TCG_V6_lvsplath(SHORTCODE) \ + tcg_gen_gvec_dup_i32(MO_16, VdV_off, \ + sizeof(MMVector), sizeof(MMVector), RtV) + +#define fGEN_TCG_V6_lvsplatb(SHORTCODE) \ + tcg_gen_gvec_dup_i32(MO_8, VdV_off, \ + sizeof(MMVector), sizeof(MMVector), RtV) + +/* Vector absolute value - various forms */ +#define fGEN_TCG_V6_vabsb(SHORTCODE) \ + tcg_gen_gvec_abs(MO_8, VdV_off, VuV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vabsh(SHORTCODE) \ + tcg_gen_gvec_abs(MO_16, VdV_off, VuV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vabsw(SHORTCODE) \ + tcg_gen_gvec_abs(MO_32, VdV_off, VuV_off, \ + sizeof(MMVector), sizeof(MMVector)) + #endif From 5d67ff6c6ca0a8a80b50eb9f7d25bd0d2cc616bc Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 18 Aug 2021 20:43:50 -0500 Subject: [PATCH 1181/1334] Hexagon HVX (target/hexagon) helper overrides - vector loads Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 150 +++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 435c7b5301..2d1d778892 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -532,4 +532,154 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_gvec_abs(MO_32, VdV_off, VuV_off, \ sizeof(MMVector), sizeof(MMVector)) +/* Vector loads */ +#define fGEN_TCG_V6_vL32b_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32Ub_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_cur_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_tmp_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_cur_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_tmp_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32Ub_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_cur_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_tmp_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_cur_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_tmp_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32Ub_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_cur_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_tmp_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_cur_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vL32b_nt_tmp_ppu(SHORTCODE) SHORTCODE + +/* Predicated vector loads */ +#define fGEN_TCG_PRED_VEC_LOAD(GET_EA, PRED, DSTOFF, INC) \ + do { \ + TCGv LSB = tcg_temp_new(); \ + TCGLabel *false_label = gen_new_label(); \ + TCGLabel *end_label = gen_new_label(); \ + GET_EA; \ + PRED; \ + tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \ + tcg_temp_free(LSB); \ + gen_vreg_load(ctx, DSTOFF, EA, true); \ + INC; \ + tcg_gen_br(end_label); \ + gen_set_label(false_label); \ + tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \ + 1 << insn->slot); \ + gen_set_label(end_label); \ + } while (0) + +#define fGEN_TCG_PRED_VEC_LOAD_pred_pi \ + fGEN_TCG_PRED_VEC_LOAD(fLSBOLD(PvV), \ + fEA_REG(RxV), \ + VdV_off, \ + fPM_I(RxV, siV * sizeof(MMVector))) +#define fGEN_TCG_PRED_VEC_LOAD_npred_pi \ + fGEN_TCG_PRED_VEC_LOAD(fLSBOLDNOT(PvV), \ + fEA_REG(RxV), \ + VdV_off, \ + fPM_I(RxV, siV * sizeof(MMVector))) + +#define fGEN_TCG_V6_vL32b_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_pi +#define fGEN_TCG_V6_vL32b_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_pi +#define fGEN_TCG_V6_vL32b_cur_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_pi +#define fGEN_TCG_V6_vL32b_cur_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_pi +#define fGEN_TCG_V6_vL32b_tmp_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_pi +#define fGEN_TCG_V6_vL32b_tmp_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_pi +#define fGEN_TCG_V6_vL32b_nt_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_pi +#define fGEN_TCG_V6_vL32b_nt_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_pi +#define fGEN_TCG_V6_vL32b_nt_cur_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_pi +#define fGEN_TCG_V6_vL32b_nt_cur_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_pi +#define fGEN_TCG_V6_vL32b_nt_tmp_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_pi +#define fGEN_TCG_V6_vL32b_nt_tmp_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_pi + +#define fGEN_TCG_PRED_VEC_LOAD_pred_ai \ + fGEN_TCG_PRED_VEC_LOAD(fLSBOLD(PvV), \ + fEA_RI(RtV, siV * sizeof(MMVector)), \ + VdV_off, \ + do {} while (0)) +#define fGEN_TCG_PRED_VEC_LOAD_npred_ai \ + fGEN_TCG_PRED_VEC_LOAD(fLSBOLDNOT(PvV), \ + fEA_RI(RtV, siV * sizeof(MMVector)), \ + VdV_off, \ + do {} while (0)) + +#define fGEN_TCG_V6_vL32b_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ai +#define fGEN_TCG_V6_vL32b_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ai +#define fGEN_TCG_V6_vL32b_cur_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ai +#define fGEN_TCG_V6_vL32b_cur_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ai +#define fGEN_TCG_V6_vL32b_tmp_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ai +#define fGEN_TCG_V6_vL32b_tmp_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ai +#define fGEN_TCG_V6_vL32b_nt_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ai +#define fGEN_TCG_V6_vL32b_nt_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ai +#define fGEN_TCG_V6_vL32b_nt_cur_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ai +#define fGEN_TCG_V6_vL32b_nt_cur_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ai +#define fGEN_TCG_V6_vL32b_nt_tmp_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ai +#define fGEN_TCG_V6_vL32b_nt_tmp_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ai + +#define fGEN_TCG_PRED_VEC_LOAD_pred_ppu \ + fGEN_TCG_PRED_VEC_LOAD(fLSBOLD(PvV), \ + fEA_REG(RxV), \ + VdV_off, \ + fPM_M(RxV, MuV)) +#define fGEN_TCG_PRED_VEC_LOAD_npred_ppu \ + fGEN_TCG_PRED_VEC_LOAD(fLSBOLDNOT(PvV), \ + fEA_REG(RxV), \ + VdV_off, \ + fPM_M(RxV, MuV)) + +#define fGEN_TCG_V6_vL32b_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ppu +#define fGEN_TCG_V6_vL32b_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ppu +#define fGEN_TCG_V6_vL32b_cur_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ppu +#define fGEN_TCG_V6_vL32b_cur_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ppu +#define fGEN_TCG_V6_vL32b_tmp_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ppu +#define fGEN_TCG_V6_vL32b_tmp_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ppu +#define fGEN_TCG_V6_vL32b_nt_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ppu +#define fGEN_TCG_V6_vL32b_nt_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ppu +#define fGEN_TCG_V6_vL32b_nt_cur_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ppu +#define fGEN_TCG_V6_vL32b_nt_cur_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ppu +#define fGEN_TCG_V6_vL32b_nt_tmp_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_pred_ppu +#define fGEN_TCG_V6_vL32b_nt_tmp_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_LOAD_npred_ppu + #endif From 6b4f75975c3b014027bae288c56c3ce2b4d3b320 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 18 Aug 2021 20:47:02 -0500 Subject: [PATCH 1182/1334] Hexagon HVX (target/hexagon) helper overrides - vector stores Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/gen_tcg_hvx.h | 218 +++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 2d1d778892..cdcc9382bb 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -682,4 +682,222 @@ static inline void assert_vhist_tmp(DisasContext *ctx) #define fGEN_TCG_V6_vL32b_nt_tmp_npred_ppu(SHORTCODE) \ fGEN_TCG_PRED_VEC_LOAD_npred_ppu +/* Vector stores */ +#define fGEN_TCG_V6_vS32b_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32Ub_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32Ub_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32Ub_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_ppu(SHORTCODE) SHORTCODE + +/* New value vector stores */ +#define fGEN_TCG_NEWVAL_VEC_STORE(GET_EA, INC) \ + do { \ + GET_EA; \ + gen_vreg_store(ctx, insn, pkt, EA, OsN_off, insn->slot, true); \ + INC; \ + } while (0) + +#define fGEN_TCG_NEWVAL_VEC_STORE_pi \ + fGEN_TCG_NEWVAL_VEC_STORE(fEA_REG(RxV), fPM_I(RxV, siV * sizeof(MMVector))) + +#define fGEN_TCG_V6_vS32b_new_pi(SHORTCODE) \ + fGEN_TCG_NEWVAL_VEC_STORE_pi +#define fGEN_TCG_V6_vS32b_nt_new_pi(SHORTCODE) \ + fGEN_TCG_NEWVAL_VEC_STORE_pi + +#define fGEN_TCG_NEWVAL_VEC_STORE_ai \ + fGEN_TCG_NEWVAL_VEC_STORE(fEA_RI(RtV, siV * sizeof(MMVector)), \ + do { } while (0)) + +#define fGEN_TCG_V6_vS32b_new_ai(SHORTCODE) \ + fGEN_TCG_NEWVAL_VEC_STORE_ai +#define fGEN_TCG_V6_vS32b_nt_new_ai(SHORTCODE) \ + fGEN_TCG_NEWVAL_VEC_STORE_ai + +#define fGEN_TCG_NEWVAL_VEC_STORE_ppu \ + fGEN_TCG_NEWVAL_VEC_STORE(fEA_REG(RxV), fPM_M(RxV, MuV)) + +#define fGEN_TCG_V6_vS32b_new_ppu(SHORTCODE) \ + fGEN_TCG_NEWVAL_VEC_STORE_ppu +#define fGEN_TCG_V6_vS32b_nt_new_ppu(SHORTCODE) \ + fGEN_TCG_NEWVAL_VEC_STORE_ppu + +/* Predicated vector stores */ +#define fGEN_TCG_PRED_VEC_STORE(GET_EA, PRED, SRCOFF, ALIGN, INC) \ + do { \ + TCGv LSB = tcg_temp_new(); \ + TCGLabel *false_label = gen_new_label(); \ + TCGLabel *end_label = gen_new_label(); \ + GET_EA; \ + PRED; \ + tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \ + tcg_temp_free(LSB); \ + gen_vreg_store(ctx, insn, pkt, EA, SRCOFF, insn->slot, ALIGN); \ + INC; \ + tcg_gen_br(end_label); \ + gen_set_label(false_label); \ + tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \ + 1 << insn->slot); \ + gen_set_label(end_label); \ + } while (0) + +#define fGEN_TCG_PRED_VEC_STORE_pred_pi(ALIGN) \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLD(PvV), \ + fEA_REG(RxV), \ + VsV_off, ALIGN, \ + fPM_I(RxV, siV * sizeof(MMVector))) +#define fGEN_TCG_PRED_VEC_STORE_npred_pi(ALIGN) \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLDNOT(PvV), \ + fEA_REG(RxV), \ + VsV_off, ALIGN, \ + fPM_I(RxV, siV * sizeof(MMVector))) +#define fGEN_TCG_PRED_VEC_STORE_new_pred_pi \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLD(PvV), \ + fEA_REG(RxV), \ + OsN_off, true, \ + fPM_I(RxV, siV * sizeof(MMVector))) +#define fGEN_TCG_PRED_VEC_STORE_new_npred_pi \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLDNOT(PvV), \ + fEA_REG(RxV), \ + OsN_off, true, \ + fPM_I(RxV, siV * sizeof(MMVector))) + +#define fGEN_TCG_V6_vS32b_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_pi(true) +#define fGEN_TCG_V6_vS32b_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_pi(true) +#define fGEN_TCG_V6_vS32Ub_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_pi(false) +#define fGEN_TCG_V6_vS32Ub_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_pi(false) +#define fGEN_TCG_V6_vS32b_nt_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_pi(true) +#define fGEN_TCG_V6_vS32b_nt_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_pi(true) +#define fGEN_TCG_V6_vS32b_new_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_pred_pi +#define fGEN_TCG_V6_vS32b_new_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_npred_pi +#define fGEN_TCG_V6_vS32b_nt_new_pred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_pred_pi +#define fGEN_TCG_V6_vS32b_nt_new_npred_pi(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_npred_pi + +#define fGEN_TCG_PRED_VEC_STORE_pred_ai(ALIGN) \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLD(PvV), \ + fEA_RI(RtV, siV * sizeof(MMVector)), \ + VsV_off, ALIGN, \ + do { } while (0)) +#define fGEN_TCG_PRED_VEC_STORE_npred_ai(ALIGN) \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLDNOT(PvV), \ + fEA_RI(RtV, siV * sizeof(MMVector)), \ + VsV_off, ALIGN, \ + do { } while (0)) +#define fGEN_TCG_PRED_VEC_STORE_new_pred_ai \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLD(PvV), \ + fEA_RI(RtV, siV * sizeof(MMVector)), \ + OsN_off, true, \ + do { } while (0)) +#define fGEN_TCG_PRED_VEC_STORE_new_npred_ai \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLDNOT(PvV), \ + fEA_RI(RtV, siV * sizeof(MMVector)), \ + OsN_off, true, \ + do { } while (0)) + +#define fGEN_TCG_V6_vS32b_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_ai(true) +#define fGEN_TCG_V6_vS32b_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_ai(true) +#define fGEN_TCG_V6_vS32Ub_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_ai(false) +#define fGEN_TCG_V6_vS32Ub_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_ai(false) +#define fGEN_TCG_V6_vS32b_nt_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_ai(true) +#define fGEN_TCG_V6_vS32b_nt_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_ai(true) +#define fGEN_TCG_V6_vS32b_new_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_pred_ai +#define fGEN_TCG_V6_vS32b_new_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_npred_ai +#define fGEN_TCG_V6_vS32b_nt_new_pred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_pred_ai +#define fGEN_TCG_V6_vS32b_nt_new_npred_ai(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_npred_ai + +#define fGEN_TCG_PRED_VEC_STORE_pred_ppu(ALIGN) \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLD(PvV), \ + fEA_REG(RxV), \ + VsV_off, ALIGN, \ + fPM_M(RxV, MuV)) +#define fGEN_TCG_PRED_VEC_STORE_npred_ppu(ALIGN) \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLDNOT(PvV), \ + fEA_REG(RxV), \ + VsV_off, ALIGN, \ + fPM_M(RxV, MuV)) +#define fGEN_TCG_PRED_VEC_STORE_new_pred_ppu \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLD(PvV), \ + fEA_REG(RxV), \ + OsN_off, true, \ + fPM_M(RxV, MuV)) +#define fGEN_TCG_PRED_VEC_STORE_new_npred_ppu \ + fGEN_TCG_PRED_VEC_STORE(fLSBOLDNOT(PvV), \ + fEA_REG(RxV), \ + OsN_off, true, \ + fPM_M(RxV, MuV)) + +#define fGEN_TCG_V6_vS32b_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_ppu(true) +#define fGEN_TCG_V6_vS32b_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_ppu(true) +#define fGEN_TCG_V6_vS32Ub_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_ppu(false) +#define fGEN_TCG_V6_vS32Ub_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_ppu(false) +#define fGEN_TCG_V6_vS32b_nt_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_pred_ppu(true) +#define fGEN_TCG_V6_vS32b_nt_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_npred_ppu(true) +#define fGEN_TCG_V6_vS32b_new_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_pred_ppu +#define fGEN_TCG_V6_vS32b_new_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_npred_ppu +#define fGEN_TCG_V6_vS32b_nt_new_pred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_pred_ppu +#define fGEN_TCG_V6_vS32b_nt_new_npred_ppu(SHORTCODE) \ + fGEN_TCG_PRED_VEC_STORE_new_npred_ppu + +/* Masked vector stores */ +#define fGEN_TCG_V6_vS32b_qpred_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_qpred_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_qpred_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_qpred_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_qpred_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_qpred_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nqpred_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_nqpred_pi(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nqpred_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_nqpred_ai(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nqpred_ppu(SHORTCODE) SHORTCODE +#define fGEN_TCG_V6_vS32b_nt_nqpred_ppu(SHORTCODE) SHORTCODE + +/* Store release not modelled in qemu, but need to suppress compiler warnings */ +#define fGEN_TCG_V6_vS32b_srls_pi(SHORTCODE) \ + do { \ + siV = siV; \ + } while (0) +#define fGEN_TCG_V6_vS32b_srls_ai(SHORTCODE) \ + do { \ + RtV = RtV; \ + siV = siV; \ + } while (0) +#define fGEN_TCG_V6_vS32b_srls_ppu(SHORTCODE) \ + do { \ + MuV = MuV; \ + } while (0) + #endif From 887d61b288fd326191a20307ce6ca739fdc0b2e5 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Fri, 13 Aug 2021 14:39:10 -0500 Subject: [PATCH 1183/1334] Hexagon HVX (target/hexagon) import semantics Imported from the Hexagon architecture library imported/allext.idef Top level file for all extensions imported/mmvec/ext.idef HVX instruction definitions Support functions added to target/hexagon/genptr.c Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/genptr.c | 172 ++ target/hexagon/imported/allext.idef | 25 + target/hexagon/imported/allidefs.def | 1 + target/hexagon/imported/mmvec/ext.idef | 2606 ++++++++++++++++++++++++ 4 files changed, 2804 insertions(+) create mode 100644 target/hexagon/imported/allext.idef create mode 100644 target/hexagon/imported/mmvec/ext.idef diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 473438a6c7..4419d30e23 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -19,11 +19,13 @@ #include "cpu.h" #include "internal.h" #include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "insn.h" #include "opcodes.h" #include "translate.h" #define QEMU_GENERATE /* Used internally by macros.h */ #include "macros.h" +#include "mmvec/macros.h" #undef QEMU_GENERATE #include "gen_tcg.h" #include "gen_tcg_hvx.h" @@ -462,5 +464,175 @@ static TCGv gen_8bitsof(TCGv result, TCGv value) return result; } +static intptr_t vreg_src_off(DisasContext *ctx, int num) +{ + intptr_t offset = offsetof(CPUHexagonState, VRegs[num]); + + if (test_bit(num, ctx->vregs_select)) { + offset = ctx_future_vreg_off(ctx, num, 1, false); + } + if (test_bit(num, ctx->vregs_updated_tmp)) { + offset = ctx_tmp_vreg_off(ctx, num, 1, false); + } + return offset; +} + +static void gen_log_vreg_write(DisasContext *ctx, intptr_t srcoff, int num, + VRegWriteType type, int slot_num, + bool is_predicated) +{ + TCGLabel *label_end = NULL; + intptr_t dstoff; + + if (is_predicated) { + TCGv cancelled = tcg_temp_local_new(); + label_end = gen_new_label(); + + /* Don't do anything if the slot was cancelled */ + tcg_gen_extract_tl(cancelled, hex_slot_cancelled, slot_num, 1); + tcg_gen_brcondi_tl(TCG_COND_NE, cancelled, 0, label_end); + tcg_temp_free(cancelled); + } + + if (type != EXT_TMP) { + dstoff = ctx_future_vreg_off(ctx, num, 1, true); + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, + sizeof(MMVector), sizeof(MMVector)); + tcg_gen_ori_tl(hex_VRegs_updated, hex_VRegs_updated, 1 << num); + } else { + dstoff = ctx_tmp_vreg_off(ctx, num, 1, false); + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, + sizeof(MMVector), sizeof(MMVector)); + } + + if (is_predicated) { + gen_set_label(label_end); + } +} + +static void gen_log_vreg_write_pair(DisasContext *ctx, intptr_t srcoff, int num, + VRegWriteType type, int slot_num, + bool is_predicated) +{ + gen_log_vreg_write(ctx, srcoff, num ^ 0, type, slot_num, is_predicated); + srcoff += sizeof(MMVector); + gen_log_vreg_write(ctx, srcoff, num ^ 1, type, slot_num, is_predicated); +} + +static void gen_log_qreg_write(intptr_t srcoff, int num, int vnew, + int slot_num, bool is_predicated) +{ + TCGLabel *label_end = NULL; + intptr_t dstoff; + + if (is_predicated) { + TCGv cancelled = tcg_temp_local_new(); + label_end = gen_new_label(); + + /* Don't do anything if the slot was cancelled */ + tcg_gen_extract_tl(cancelled, hex_slot_cancelled, slot_num, 1); + tcg_gen_brcondi_tl(TCG_COND_NE, cancelled, 0, label_end); + tcg_temp_free(cancelled); + } + + dstoff = offsetof(CPUHexagonState, future_QRegs[num]); + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, sizeof(MMQReg), sizeof(MMQReg)); + + if (is_predicated) { + tcg_gen_ori_tl(hex_QRegs_updated, hex_QRegs_updated, 1 << num); + gen_set_label(label_end); + } +} + +static void gen_vreg_load(DisasContext *ctx, intptr_t dstoff, TCGv src, + bool aligned) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + if (aligned) { + tcg_gen_andi_tl(src, src, ~((int32_t)sizeof(MMVector) - 1)); + } + for (int i = 0; i < sizeof(MMVector) / 8; i++) { + tcg_gen_qemu_ld64(tmp, src, ctx->mem_idx); + tcg_gen_addi_tl(src, src, 8); + tcg_gen_st_i64(tmp, cpu_env, dstoff + i * 8); + } + tcg_temp_free_i64(tmp); +} + +static void gen_vreg_store(DisasContext *ctx, Insn *insn, Packet *pkt, + TCGv EA, intptr_t srcoff, int slot, bool aligned) +{ + intptr_t dstoff = offsetof(CPUHexagonState, vstore[slot].data); + intptr_t maskoff = offsetof(CPUHexagonState, vstore[slot].mask); + + if (is_gather_store_insn(insn, pkt)) { + TCGv sl = tcg_constant_tl(slot); + gen_helper_gather_store(cpu_env, EA, sl); + return; + } + + tcg_gen_movi_tl(hex_vstore_pending[slot], 1); + if (aligned) { + tcg_gen_andi_tl(hex_vstore_addr[slot], EA, + ~((int32_t)sizeof(MMVector) - 1)); + } else { + tcg_gen_mov_tl(hex_vstore_addr[slot], EA); + } + tcg_gen_movi_tl(hex_vstore_size[slot], sizeof(MMVector)); + + /* Copy the data to the vstore buffer */ + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, sizeof(MMVector), sizeof(MMVector)); + /* Set the mask to all 1's */ + tcg_gen_gvec_dup_imm(MO_64, maskoff, sizeof(MMQReg), sizeof(MMQReg), ~0LL); +} + +static void gen_vreg_masked_store(DisasContext *ctx, TCGv EA, intptr_t srcoff, + intptr_t bitsoff, int slot, bool invert) +{ + intptr_t dstoff = offsetof(CPUHexagonState, vstore[slot].data); + intptr_t maskoff = offsetof(CPUHexagonState, vstore[slot].mask); + + tcg_gen_movi_tl(hex_vstore_pending[slot], 1); + tcg_gen_andi_tl(hex_vstore_addr[slot], EA, + ~((int32_t)sizeof(MMVector) - 1)); + tcg_gen_movi_tl(hex_vstore_size[slot], sizeof(MMVector)); + + /* Copy the data to the vstore buffer */ + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, sizeof(MMVector), sizeof(MMVector)); + /* Copy the mask */ + tcg_gen_gvec_mov(MO_64, maskoff, bitsoff, sizeof(MMQReg), sizeof(MMQReg)); + if (invert) { + tcg_gen_gvec_not(MO_64, maskoff, maskoff, + sizeof(MMQReg), sizeof(MMQReg)); + } +} + +static void vec_to_qvec(size_t size, intptr_t dstoff, intptr_t srcoff) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 word = tcg_temp_new_i64(); + TCGv_i64 bits = tcg_temp_new_i64(); + TCGv_i64 mask = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 ones = tcg_constant_i64(~0); + + for (int i = 0; i < sizeof(MMVector) / 8; i++) { + tcg_gen_ld_i64(tmp, cpu_env, srcoff + i * 8); + tcg_gen_movi_i64(mask, 0); + + for (int j = 0; j < 8; j += size) { + tcg_gen_extract_i64(word, tmp, j * 8, size * 8); + tcg_gen_movcond_i64(TCG_COND_NE, bits, word, zero, ones, zero); + tcg_gen_deposit_i64(mask, mask, bits, j, size); + } + + tcg_gen_st8_i64(mask, cpu_env, dstoff + i); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(word); + tcg_temp_free_i64(bits); + tcg_temp_free_i64(mask); +} + #include "tcg_funcs_generated.c.inc" #include "tcg_func_table_generated.c.inc" diff --git a/target/hexagon/imported/allext.idef b/target/hexagon/imported/allext.idef new file mode 100644 index 0000000000..9d4b23e9de --- /dev/null +++ b/target/hexagon/imported/allext.idef @@ -0,0 +1,25 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Top level file for all instruction set extensions + */ +#define EXTNAME mmvec +#define EXTSTR "mmvec" +#include "mmvec/ext.idef" +#undef EXTNAME +#undef EXTSTR diff --git a/target/hexagon/imported/allidefs.def b/target/hexagon/imported/allidefs.def index 2aace29888..ee253b83a9 100644 --- a/target/hexagon/imported/allidefs.def +++ b/target/hexagon/imported/allidefs.def @@ -28,3 +28,4 @@ #include "shift.idef" #include "system.idef" #include "subinsns.idef" +#include "allext.idef" diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef new file mode 100644 index 0000000000..8ca5a606e1 --- /dev/null +++ b/target/hexagon/imported/mmvec/ext.idef @@ -0,0 +1,2606 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/****************************************************************************** + * + * HOYA: MULTI MEDIA INSTRUCITONS + * + ******************************************************************************/ + +#ifndef EXTINSN +#define EXTINSN Q6INSN +#define __SELF_DEF_EXTINSN 1 +#endif + +#ifndef NO_MMVEC + +#define DO_FOR_EACH_CODE(WIDTH, CODE) \ +{ \ + fHIDE(int i;) \ + fVFOREACH(WIDTH, i) {\ + CODE ;\ + } \ +} + + + + +#define ITERATOR_INSN_ANY_SLOT(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + + + +#define ITERATOR_INSN2_ANY_SLOT(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_ANY_SLOT(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA_DV), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + + +#define ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX2,DESCR,CODE) + + +#define ITERATOR_INSN_SHIFT_SLOT(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + + + +#define ITERATOR_INSN_SHIFT_SLOT_VV_LATE(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_SHIFT_SLOT(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_SHIFT_SLOT(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define ITERATOR_INSN_PERMUTE_SLOT(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_PERMUTE_SLOT(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_PERMUTE_SLOT(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define ITERATOR_INSN_PERMUTE_SLOT_DEP(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP), + + +#define ITERATOR_INSN2_PERMUTE_SLOT_DEP(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_PERMUTE_SLOT_DEP(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP_VS), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC_DEP(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP_VS), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_PERMUTE_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define ITERATOR_INSN_MPY_SLOT(WIDTH,TAG, SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN_MPY_SLOT_LATE(WIDTH,TAG, SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_MPY_SLOT(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_MPY_SLOT(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define ITERATOR_INSN2_MPY_SLOT_LATE(WIDTH,TAG, SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_MPY_SLOT_LATE(WIDTH,TAG, SYNTAX2,DESCR,CODE) + + +#define ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX_DV), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX2,DESCR,CODE) + + + + +#define ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC2(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX_DV,A_CVI_VX_VSRC0_IS_DST), DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN_SLOT2_DOUBLE_VEC(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX_DV,A_RESTRICT_SLOT2ONLY), DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN_VHISTLIKE(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_4SLOT), \ +DESCR, fHIDE(mmvector_t input;) input = fTMPVDATA(); DO_FOR_EACH_CODE(WIDTH, CODE)) + + + + + +/****************************************************************************************** +* +* MMVECTOR MEMORY OPERATIONS - NO NAPALI V1 +* +*******************************************************************************************/ + + + +#define ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC_NOV1(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX_DV), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC_NOV1(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC_NOV1(WIDTH,TAG,SYNTAX2,DESCR,CODE) + + + +#define ITERATOR_INSN_SHIFT_SLOT_NOV1(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_SHIFT_SLOT_NOV1(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_SHIFT_SLOT_NOV1(WIDTH,TAG,SYNTAX2,DESCR,CODE) + + +#define ITERATOR_INSN_ANY_SLOT_NOV1(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_ANY_SLOT_NOV1(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_ANY_SLOT_NOV1(WIDTH,TAG,SYNTAX2,DESCR,CODE) + + +#define ITERATOR_INSN_MPY_SLOT_NOV1(WIDTH,TAG, SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN_PERMUTE_SLOT_NOV1(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_PERMUTE_SLOTT_NOV1(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_PERMUTE_SLOT(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define ITERATOR_INSN_PERMUTE_SLOT_DEPT_NOV1(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP), + + +#define ITERATOR_INSN2_PERMUTE_SLOT_DEPT_NOV1(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_PERMUTE_SLOT_DEP_NOV1(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC_NOV1(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP_VS), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC_DEPT_NOV1(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP_VS), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +#define ITERATOR_INSN2_PERMUTE_SLOT_DOUBLE_VEC_NOV1(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC_NOV1(WIDTH,TAG,SYNTAX2,DESCR,CODE) + +#define NARROWING_SHIFT_NOV1(ITERSIZE,TAG,DSTM,DSTTYPE,SRCTYPE,SYNOPTS,SATFUNC,RNDFUNC,SHAMTMASK) \ +ITERATOR_INSN_SHIFT_SLOT_NOV1(ITERSIZE,TAG, \ +"Vd32." #DSTTYPE "=vasr(Vu32." #SRCTYPE ",Vv32." #SRCTYPE ",Rt8)" #SYNOPTS, \ +"Vector shift right and shuffle", \ + fHIDE(int )shamt = RtV & SHAMTMASK; \ + DSTM(0,VdV.SRCTYPE[i],SATFUNC(RNDFUNC(VvV.SRCTYPE[i],shamt) >> shamt)); \ + DSTM(1,VdV.SRCTYPE[i],SATFUNC(RNDFUNC(VuV.SRCTYPE[i],shamt) >> shamt))) + +#define MMVEC_AVGS_NOV1(TYPE,TYPE2,DESCR, WIDTH, DEST,SRC)\ +ITERATOR_INSN2_ANY_SLOT_NOV1(WIDTH,vavg##TYPE, "Vd32=vavg"TYPE2"(Vu32,Vv32)", "Vd32."#DEST"=vavg(Vu32."#SRC",Vv32."#SRC")", "Vector Average "DESCR, VdV.DEST[i] = fVAVGS( WIDTH, VuV.SRC[i], VvV.SRC[i])) \ +ITERATOR_INSN2_ANY_SLOT_NOV1(WIDTH,vavg##TYPE##rnd, "Vd32=vavg"TYPE2"(Vu32,Vv32):rnd", "Vd32."#DEST"=vavg(Vu32."#SRC",Vv32."#SRC"):rnd", "Vector Average % Round"DESCR, VdV.DEST[i] = fVAVGSRND( WIDTH, VuV.SRC[i], VvV.SRC[i])) \ +ITERATOR_INSN2_ANY_SLOT_NOV1(WIDTH,vnavg##TYPE, "Vd32=vnavg"TYPE2"(Vu32,Vv32)", "Vd32."#DEST"=vnavg(Vu32."#SRC",Vv32."#SRC")", "Vector Negative Average "DESCR, VdV.DEST[i] = fVNAVGS( WIDTH, VuV.SRC[i], VvV.SRC[i])) + + #define MMVEC_AVGU_NOV1(TYPE,TYPE2,DESCR, WIDTH, DEST,SRC)\ +ITERATOR_INSN2_ANY_SLOT_NOV1(WIDTH,vavg##TYPE, "Vd32=vavg"TYPE2"(Vu32,Vv32)", "Vd32."#DEST"=vavg(Vu32."#SRC",Vv32."#SRC")", "Vector Average "DESCR, VdV.DEST[i] = fVAVGU( WIDTH, VuV.SRC[i], VvV.SRC[i])) \ +ITERATOR_INSN2_ANY_SLOT_NOV1(WIDTH,vavg##TYPE##rnd, "Vd32=vavg"TYPE2"(Vu32,Vv32):rnd", "Vd32."#DEST"=vavg(Vu32."#SRC",Vv32."#SRC"):rnd", "Vector Average % Round"DESCR, VdV.DEST[i] = fVAVGURND(WIDTH, VuV.SRC[i], VvV.SRC[i])) + + + +/****************************************************************************************** +* +* MMVECTOR MEMORY OPERATIONS +* +*******************************************************************************************/ + +#define MMVEC_EACH_EA(TAG,DESCR,ATTRIB,NT,SYNTAXA,SYNTAXB,BEH) \ +EXTINSN(V6_##TAG##_pi, SYNTAXA "(Rx32++#s3)" NT SYNTAXB,ATTRIB,DESCR,{ fEA_REG(RxV); BEH; fPM_I(RxV,VEC_SCALE(siV)); }) \ +EXTINSN(V6_##TAG##_ai, SYNTAXA "(Rt32+#s4)" NT SYNTAXB,ATTRIB,DESCR,{ fEA_RI(RtV,VEC_SCALE(siV)); BEH;}) \ +EXTINSN(V6_##TAG##_ppu, SYNTAXA "(Rx32++Mu2)" NT SYNTAXB,ATTRIB,DESCR,{ fEA_REG(RxV); BEH; fPM_M(RxV,MuV); }) \ + + +#define MMVEC_COND_EACH_EA_TRUE(TAG,DESCR,ATTRIB,NT,SYNTAXA,SYNTAXB,SYNTAXP,BEH) \ +EXTINSN(V6_##TAG##_pred_pi, "if (" #SYNTAXP "4) " SYNTAXA "(Rx32++#s3)" NT SYNTAXB, ATTRIB,DESCR, { if (fLSBOLD(SYNTAXP##V)) { fEA_REG(RxV); BEH; fPM_I(RxV,siV*fVECSIZE()); } else {CANCEL;}}) \ +EXTINSN(V6_##TAG##_pred_ai, "if (" #SYNTAXP "4) " SYNTAXA "(Rt32+#s4)" NT SYNTAXB, ATTRIB,DESCR, { if (fLSBOLD(SYNTAXP##V)) { fEA_RI(RtV,siV*fVECSIZE()); BEH;} else {CANCEL;}}) \ +EXTINSN(V6_##TAG##_pred_ppu, "if (" #SYNTAXP "4) " SYNTAXA "(Rx32++Mu2)" NT SYNTAXB,ATTRIB,DESCR, { if (fLSBOLD(SYNTAXP##V)) { fEA_REG(RxV); BEH; fPM_M(RxV,MuV); } else {CANCEL;}}) \ + +#define MMVEC_COND_EACH_EA_FALSE(TAG,DESCR,ATTRIB,NT,SYNTAXA,SYNTAXB,SYNTAXP,BEH) \ +EXTINSN(V6_##TAG##_npred_pi, "if (!" #SYNTAXP "4) " SYNTAXA "(Rx32++#s3)" NT SYNTAXB,ATTRIB,DESCR,{ if (fLSBOLDNOT(SYNTAXP##V)) { fEA_REG(RxV); BEH; fPM_I(RxV,siV*fVECSIZE()); } else {CANCEL;}}) \ +EXTINSN(V6_##TAG##_npred_ai, "if (!" #SYNTAXP "4) " SYNTAXA "(Rt32+#s4)" NT SYNTAXB,ATTRIB,DESCR, { if (fLSBOLDNOT(SYNTAXP##V)) { fEA_RI(RtV,siV*fVECSIZE()); BEH;} else {CANCEL;}}) \ +EXTINSN(V6_##TAG##_npred_ppu, "if (!" #SYNTAXP "4) " SYNTAXA "(Rx32++Mu2)" NT SYNTAXB,ATTRIB,DESCR,{ if (fLSBOLDNOT(SYNTAXP##V)) { fEA_REG(RxV); BEH; fPM_M(RxV,MuV); } else {CANCEL;}}) + +#define MMVEC_COND_EACH_EA(TAG,DESCR,ATTRIB,NT,SYNTAXA,SYNTAXB,SYNTAXP,BEH) \ +MMVEC_COND_EACH_EA_TRUE(TAG,DESCR,ATTRIB,NT,SYNTAXA,SYNTAXB,SYNTAXP,BEH) \ +MMVEC_COND_EACH_EA_FALSE(TAG,DESCR,ATTRIB,NT,SYNTAXA,SYNTAXB,SYNTAXP,BEH) + + +#define VEC_SCALE(X) X*fVECSIZE() + + +#define MMVEC_LD(TAG,DESCR,ATTRIB,NT) MMVEC_EACH_EA(TAG,DESCR,ATTRIB,NT,"Vd32=vmem","",fLOADMMV(EA,VdV)) +#define MMVEC_LDC(TAG,DESCR,ATTRIB,NT) MMVEC_EACH_EA(TAG##_cur,DESCR,ATTRIB,NT,"Vd32.cur=vmem","",fLOADMMV(EA,VdV)) +#define MMVEC_LDT(TAG,DESCR,ATTRIB,NT) MMVEC_EACH_EA(TAG##_tmp,DESCR,ATTRIB,NT,"Vd32.tmp=vmem","",fLOADMMV(EA,VdV)) +#define MMVEC_LDU(TAG,DESCR,ATTRIB,NT) MMVEC_EACH_EA(TAG,DESCR,ATTRIB,NT,"Vd32=vmemu","",fLOADMMVU(EA,VdV)) + + +#define MMVEC_STQ(TAG,DESCR,ATTRIB,NT) \ +MMVEC_EACH_EA(TAG##_qpred,DESCR,ATTRIB,NT,"if (Qv4) vmem","=Vs32",fSTOREMMVQ(EA,VsV,QvV)) \ +MMVEC_EACH_EA(TAG##_nqpred,DESCR,ATTRIB,NT,"if (!Qv4) vmem","=Vs32",fSTOREMMVNQ(EA,VsV,QvV)) + +/**************************************************************** +* MAPPING FOR VMEMs +****************************************************************/ + +#define ATTR_VMEM A_EXTENSION,A_CVI,A_CVI_VM +#define ATTR_VMEMU A_EXTENSION,A_CVI,A_CVI_VM,A_CVI_VP + + +MMVEC_LD(vL32b, "Aligned Vector Load", ATTRIBS(ATTR_VMEM,A_LOAD,A_CVI_VA),) +MMVEC_LDC(vL32b, "Aligned Vector Load Cur", ATTRIBS(ATTR_VMEM,A_LOAD,A_CVI_NEW,A_CVI_VA),) +MMVEC_LDT(vL32b, "Aligned Vector Load Tmp", ATTRIBS(ATTR_VMEM,A_LOAD,A_CVI_TMP),) + +MMVEC_COND_EACH_EA(vL32b,"Conditional Aligned Vector Load",ATTRIBS(ATTR_VMEM,A_LOAD,A_CVI_VA),,"Vd32=vmem",,Pv,fLOADMMV(EA,VdV);) +MMVEC_COND_EACH_EA(vL32b_cur,"Conditional Aligned Vector Load Cur",ATTRIBS(ATTR_VMEM,A_LOAD,A_CVI_VA,A_CVI_NEW),,"Vd32.cur=vmem",,Pv,fLOADMMV(EA,VdV);) +MMVEC_COND_EACH_EA(vL32b_tmp,"Conditional Aligned Vector Load Tmp",ATTRIBS(ATTR_VMEM,A_LOAD,A_CVI_TMP),,"Vd32.tmp=vmem",,Pv,fLOADMMV(EA,VdV);) + +MMVEC_EACH_EA(vS32b,"Aligned Vector Store",ATTRIBS(ATTR_VMEM,A_STORE,A_RESTRICT_SLOT0ONLY,A_CVI_VA),,"vmem","=Vs32",fSTOREMMV(EA,VsV)) +MMVEC_COND_EACH_EA(vS32b,"Aligned Vector Store",ATTRIBS(ATTR_VMEM,A_STORE,A_RESTRICT_SLOT0ONLY,A_CVI_VA),,"vmem","=Vs32",Pv,fSTOREMMV(EA,VsV)) + + +MMVEC_STQ(vS32b, "Aligned Vector Store", ATTRIBS(ATTR_VMEM,A_STORE,A_RESTRICT_SLOT0ONLY,A_CVI_VA),) + +MMVEC_LDU(vL32Ub, "Unaligned Vector Load", ATTRIBS(ATTR_VMEMU,A_LOAD,A_RESTRICT_NOSLOT1),) + +MMVEC_EACH_EA(vS32Ub,"Unaligned Vector Store",ATTRIBS(ATTR_VMEMU,A_STORE,A_RESTRICT_NOSLOT1),,"vmemu","=Vs32",fSTOREMMVU(EA,VsV)) + +MMVEC_COND_EACH_EA(vS32Ub,"Unaligned Vector Store",ATTRIBS(ATTR_VMEMU,A_STORE,A_RESTRICT_NOSLOT1),,"vmemu","=Vs32",Pv,fSTOREMMVU(EA,VsV)) + +MMVEC_EACH_EA(vS32b_new,"Aligned Vector Store New",ATTRIBS(ATTR_VMEM,A_STORE,A_CVI_NEW,A_DOTNEWVALUE,A_RESTRICT_SLOT0ONLY),,"vmem","=Os8.new",fSTOREMMV(EA,fNEWVREG(OsN))) + +// V65 store relase, zero byte store +MMVEC_EACH_EA(vS32b_srls,"Aligned Vector Scatter Release",ATTRIBS(ATTR_VMEM,A_STORE,A_CVI_SCATTER_RELEASE,A_CVI_NEW,A_RESTRICT_SLOT0ONLY),,"vmem",":scatter_release",fSTORERELEASE(EA,0)) + + + +MMVEC_COND_EACH_EA(vS32b_new,"Aligned Vector Store New",ATTRIBS(ATTR_VMEM,A_STORE,A_CVI_NEW,A_DOTNEWVALUE,A_RESTRICT_SLOT0ONLY),,"vmem","=Os8.new",Pv,fSTOREMMV(EA,fNEWVREG(OsN))) + + +/****************************************************************************************** +* +* MMVECTOR MEMORY OPERATIONS - NON TEMPORAL +* +*******************************************************************************************/ + +#define ATTR_VMEM_NT A_EXTENSION,A_CVI,A_CVI_VM + +MMVEC_EACH_EA(vS32b_nt,"Aligned Vector Store - Non temporal",ATTRIBS(ATTR_VMEM_NT,A_STORE,A_RESTRICT_SLOT0ONLY,A_CVI_VA),":nt","vmem","=Vs32",fSTOREMMV(EA,VsV)) +MMVEC_COND_EACH_EA(vS32b_nt,"Aligned Vector Store - Non temporal",ATTRIBS(ATTR_VMEM_NT,A_STORE,A_RESTRICT_SLOT0ONLY,A_CVI_VA),":nt","vmem","=Vs32",Pv,fSTOREMMV(EA,VsV)) + +MMVEC_EACH_EA(vS32b_nt_new,"Aligned Vector Store New - Non temporal",ATTRIBS(ATTR_VMEM_NT,A_STORE,A_CVI_NEW,A_DOTNEWVALUE,A_RESTRICT_SLOT0ONLY),":nt","vmem","=Os8.new",fSTOREMMV(EA,fNEWVREG(OsN))) +MMVEC_COND_EACH_EA(vS32b_nt_new,"Aligned Vector Store New - Non temporal",ATTRIBS(ATTR_VMEM_NT,A_STORE,A_CVI_NEW,A_DOTNEWVALUE,A_RESTRICT_SLOT0ONLY),":nt","vmem","=Os8.new",Pv,fSTOREMMV(EA,fNEWVREG(OsN))) + + +MMVEC_STQ(vS32b_nt, "Aligned Vector Store - Non temporal", ATTRIBS(ATTR_VMEM_NT,A_STORE,A_RESTRICT_SLOT0ONLY,A_CVI_VA),":nt") + +MMVEC_LD(vL32b_nt, "Aligned Vector Load - Non temporal", ATTRIBS(ATTR_VMEM_NT,A_LOAD,A_CVI_VA),":nt") +MMVEC_LDC(vL32b_nt, "Aligned Vector Load Cur - Non temporal", ATTRIBS(ATTR_VMEM_NT,A_LOAD,A_CVI_NEW,A_CVI_VA),":nt") +MMVEC_LDT(vL32b_nt, "Aligned Vector Load Tmp - Non temporal", ATTRIBS(ATTR_VMEM_NT,A_LOAD,A_CVI_TMP),":nt") + +MMVEC_COND_EACH_EA(vL32b_nt,"Conditional Aligned Vector Load",ATTRIBS(ATTR_VMEM_NT,A_CVI_VA),,"Vd32=vmem",":nt",Pv,fLOADMMV(EA,VdV);) +MMVEC_COND_EACH_EA(vL32b_nt_cur,"Conditional Aligned Vector Load Cur",ATTRIBS(ATTR_VMEM_NT,A_CVI_VA,A_CVI_NEW),,"Vd32.cur=vmem",":nt",Pv,fLOADMMV(EA,VdV);) +MMVEC_COND_EACH_EA(vL32b_nt_tmp,"Conditional Aligned Vector Load Tmp",ATTRIBS(ATTR_VMEM_NT,A_CVI_TMP),,"Vd32.tmp=vmem",":nt",Pv,fLOADMMV(EA,VdV);) + + +#undef VEC_SCALE + + +/*************************************************** + * Vector Alignment + ************************************************/ + +#define VALIGNB(SHIFT) \ + fHIDE(int i;) \ + for(i = 0; i < fVBYTES(); i++) {\ + VdV.ub[i] = (i+SHIFT>=fVBYTES()) ? VuV.ub[i+SHIFT-fVBYTES()] : VvV.ub[i+SHIFT];\ + } + +EXTINSN(V6_valignb, "Vd32=valign(Vu32,Vv32,Rt8)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP),"Align Two vectors by Rt8 as control", +{ + unsigned shift = RtV & (fVBYTES()-1); + VALIGNB(shift) +}) +EXTINSN(V6_vlalignb, "Vd32=vlalign(Vu32,Vv32,Rt8)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP),"Align Two vectors by Rt8 as control", +{ + unsigned shift = fVBYTES() - (RtV & (fVBYTES()-1)); + VALIGNB(shift) +}) +EXTINSN(V6_valignbi, "Vd32=valign(Vu32,Vv32,#u3)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP),"Align Two vectors by #u3 as control", +{ + VALIGNB(uiV) +}) +EXTINSN(V6_vlalignbi,"Vd32=vlalign(Vu32,Vv32,#u3)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP),"Align Two vectors by #u3 as control", +{ + unsigned shift = fVBYTES() - uiV; + VALIGNB(shift) +}) + +EXTINSN(V6_vror, "Vd32=vror(Vu32,Rt32)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP), +"Align Two vectors by Rt32 as control", +{ + fHIDE(int k;) + for (k=0;k> (RtV & (SIZE-1)))) \ +ITERATOR_INSN2_SHIFT_SLOT(SIZE,vasl##TYPE, "Vd32=vasl" #TYPE "(Vu32,Rt32)","Vd32."#TYPE"=vasl(Vu32."#TYPE",Rt32)", "Vector arithmetic shift left " DESC, VdV.TYPE[i] = (VuV.TYPE[i] << (RtV & (SIZE-1)))) \ +ITERATOR_INSN2_SHIFT_SLOT(SIZE,vlsr##TYPE, "Vd32=vlsr" #TYPE "(Vu32,Rt32)","Vd32.u"#TYPE"=vlsr(Vu32.u"#TYPE",Rt32)", "Vector logical shift right " DESC, VdV.u##TYPE[i] = (VuV.u##TYPE[i] >> (RtV & (SIZE-1)))) \ +ITERATOR_INSN2_SHIFT_SLOT(SIZE,vasr##TYPE##v,"Vd32=vasr" #TYPE "(Vu32,Vv32)","Vd32."#TYPE"=vasr(Vu32."#TYPE",Vv32."#TYPE")", "Vector arithmetic shift right " DESC, VdV.TYPE[i] = fBIDIR_ASHIFTR(VuV.TYPE[i], fSXTN((LOGSIZE+1),SIZE,VvV.TYPE[i]),CASTTYPE)) \ +ITERATOR_INSN2_SHIFT_SLOT(SIZE,vasl##TYPE##v,"Vd32=vasl" #TYPE "(Vu32,Vv32)","Vd32."#TYPE"=vasl(Vu32."#TYPE",Vv32."#TYPE")", "Vector arithmetic shift left " DESC, VdV.TYPE[i] = fBIDIR_ASHIFTL(VuV.TYPE[i], fSXTN((LOGSIZE+1),SIZE,VvV.TYPE[i]),CASTTYPE)) \ +ITERATOR_INSN2_SHIFT_SLOT(SIZE,vlsr##TYPE##v,"Vd32=vlsr" #TYPE "(Vu32,Vv32)","Vd32."#TYPE"=vlsr(Vu32."#TYPE",Vv32."#TYPE")", "Vector logical shift right " DESC, VdV.u##TYPE[i] = fBIDIR_LSHIFTR(VuV.u##TYPE[i], fSXTN((LOGSIZE+1),SIZE,VvV.TYPE[i]),CASTTYPE)) \ + +V_SHIFT(w, "word", 32,5,4_4) +V_SHIFT(h, "halfword", 16,4,2_2) + +ITERATOR_INSN_SHIFT_SLOT(8,vlsrb,"Vd32.ub=vlsr(Vu32.ub,Rt32)","vec log shift right bytes", VdV.b[i] = VuV.ub[i] >> (RtV & 0x7)) + +ITERATOR_INSN2_SHIFT_SLOT(32,vrotr,"Vd32=vrotr(Vu32,Vv32)","Vd32.uw=vrotr(Vu32.uw,Vv32.uw)","Vector word rotate right", VdV.uw[i] = ((VuV.uw[i] >> (VvV.uw[i] & 0x1f)) | (VuV.uw[i] << (32 - (VvV.uw[i] & 0x1f))))) + +/********************************************************************* + * MMVECTOR SHIFT AND PERMUTE + * ******************************************************************/ + +ITERATOR_INSN2_PERMUTE_SLOT_DOUBLE_VEC(32,vasr_into,"Vxx32=vasrinto(Vu32,Vv32)","Vxx32.w=vasrinto(Vu32.w,Vv32.w)","ASR vector 1 elements and overlay dropping bits to MSB of vector 2 elements", + fHIDE(int64_t ) shift = (fSE32_64(VuV.w[i]) << 32); + fHIDE(int64_t ) mask = (((fSE32_64(VxxV.v[0].w[i])) << 32) | fZE32_64(VxxV.v[0].w[i])); + fHIDE(int64_t) lomask = (((fSE32_64(1)) << 32) - 1); + fHIDE(int ) count = -(0x40 & VvV.w[i]) + (VvV.w[i] & 0x3f); + fHIDE(int64_t ) result = (count == -0x40) ? 0 : (((count < 0) ? ((shift << -(count)) | (mask & (lomask << -(count)))) : ((shift >> count) | (mask & (lomask >> count))))); + VxxV.v[1].w[i] = ((result >> 32) & 0xffffffff); + VxxV.v[0].w[i] = (result & 0xffffffff)) + +#define NEW_NARROWING_SHIFT 1 + +#if NEW_NARROWING_SHIFT +#define NARROWING_SHIFT(ITERSIZE,TAG,DSTM,DSTTYPE,SRCTYPE,SYNOPTS,SATFUNC,RNDFUNC,SHAMTMASK) \ +ITERATOR_INSN_SHIFT_SLOT(ITERSIZE,TAG, \ +"Vd32." #DSTTYPE "=vasr(Vu32." #SRCTYPE ",Vv32." #SRCTYPE ",Rt8)" #SYNOPTS, \ +"Vector shift right and shuffle", \ + fHIDE(int )shamt = RtV & SHAMTMASK; \ + DSTM(0,VdV.SRCTYPE[i],SATFUNC(RNDFUNC(VvV.SRCTYPE[i],shamt) >> shamt)); \ + DSTM(1,VdV.SRCTYPE[i],SATFUNC(RNDFUNC(VuV.SRCTYPE[i],shamt) >> shamt))) + + + + + +/* WORD TO HALF*/ + +NARROWING_SHIFT(32,vasrwh,fSETHALF,h,w,,fECHO,fVNOROUND,0xF) +NARROWING_SHIFT(32,vasrwhsat,fSETHALF,h,w,:sat,fVSATH,fVNOROUND,0xF) +NARROWING_SHIFT(32,vasrwhrndsat,fSETHALF,h,w,:rnd:sat,fVSATH,fVROUND,0xF) +NARROWING_SHIFT(32,vasrwuhrndsat,fSETHALF,uh,w,:rnd:sat,fVSATUH,fVROUND,0xF) +NARROWING_SHIFT(32,vasrwuhsat,fSETHALF,uh,w,:sat,fVSATUH,fVNOROUND,0xF) +NARROWING_SHIFT(32,vasruwuhrndsat,fSETHALF,uh,uw,:rnd:sat,fVSATUH,fVROUND,0xF) + +NARROWING_SHIFT_NOV1(32,vasruwuhsat,fSETHALF,uh,uw,:sat,fVSATUH,fVNOROUND,0xF) +NARROWING_SHIFT(16,vasrhubsat,fSETBYTE,ub,h,:sat,fVSATUB,fVNOROUND,0x7) +NARROWING_SHIFT(16,vasrhubrndsat,fSETBYTE,ub,h,:rnd:sat,fVSATUB,fVROUND,0x7) +NARROWING_SHIFT(16,vasrhbsat,fSETBYTE,b,h,:sat,fVSATB,fVNOROUND,0x7) +NARROWING_SHIFT(16,vasrhbrndsat,fSETBYTE,b,h,:rnd:sat,fVSATB,fVROUND,0x7) + +NARROWING_SHIFT_NOV1(16,vasruhubsat,fSETBYTE,ub,uh,:sat,fVSATUB,fVNOROUND,0x7) +NARROWING_SHIFT_NOV1(16,vasruhubrndsat,fSETBYTE,ub,uh,:rnd:sat,fVSATUB,fVROUND,0x7) + +#else +ITERATOR_INSN2_SHIFT_SLOT(32,vasrwh,"Vd32=vasrwh(Vu32,Vv32,Rt8)","Vd32.h=vasr(Vu32.w,Vv32.w,Rt8)", +"Vector arithmetic shift right words, shuffle even halfwords", + fSETHALF(0,VdV.w[i], (VvV.w[i] >> (RtV & 0xF))); + fSETHALF(1,VdV.w[i], (VuV.w[i] >> (RtV & 0xF)))) + + +ITERATOR_INSN2_SHIFT_SLOT(32,vasrwhsat,"Vd32=vasrwh(Vu32,Vv32,Rt8):sat","Vd32.h=vasr(Vu32.w,Vv32.w,Rt8):sat", +"Vector arithmetic shift right words, shuffle even halfwords", + fSETHALF(0,VdV.w[i], fVSATH(VvV.w[i] >> (RtV & 0xF))); + fSETHALF(1,VdV.w[i], fVSATH(VuV.w[i] >> (RtV & 0xF)))) + +ITERATOR_INSN2_SHIFT_SLOT(32,vasrwhrndsat,"Vd32=vasrwh(Vu32,Vv32,Rt8):rnd:sat","Vd32.h=vasr(Vu32.w,Vv32.w,Rt8):rnd:sat", +"Vector arithmetic shift right words, shuffle even halfwords", + fHIDE(int ) shamt = RtV & 0xF; + fSETHALF(0,VdV.w[i], fVSATH( (VvV.w[i] + fBIDIR_ASHIFTL(1,(shamt-1),4_8) ) >> shamt)); + fSETHALF(1,VdV.w[i], fVSATH( (VuV.w[i] + fBIDIR_ASHIFTL(1,(shamt-1),4_8) ) >> shamt))) + +ITERATOR_INSN2_SHIFT_SLOT(32,vasrwuhrndsat,"Vd32=vasrwuh(Vu32,Vv32,Rt8):rnd:sat","Vd32.uh=vasr(Vu32.w,Vv32.w,Rt8):rnd:sat", +"Vector arithmetic shift right words, shuffle even halfwords", + fHIDE(int ) shamt = RtV & 0xF; + fSETHALF(0,VdV.w[i], fVSATUH( (VvV.w[i] + fBIDIR_ASHIFTL(1,(shamt-1),4_8) ) >> shamt)); + fSETHALF(1,VdV.w[i], fVSATUH( (VuV.w[i] + fBIDIR_ASHIFTL(1,(shamt-1),4_8) ) >> shamt))) + +ITERATOR_INSN2_SHIFT_SLOT(32,vasrwuhsat,"Vd32=vasrwuh(Vu32,Vv32,Rt8):sat","Vd32.uh=vasr(Vu32.w,Vv32.w,Rt8):sat", +"Vector arithmetic shift right words, shuffle even halfwords", + fSETHALF(0, VdV.uw[i], fVSATUH(VvV.w[i] >> (RtV & 0xF))); + fSETHALF(1, VdV.uw[i], fVSATUH(VuV.w[i] >> (RtV & 0xF)))) + +ITERATOR_INSN2_SHIFT_SLOT(32,vasruwuhrndsat,"Vd32=vasruwuh(Vu32,Vv32,Rt8):rnd:sat","Vd32.uh=vasr(Vu32.uw,Vv32.uw,Rt8):rnd:sat", +"Vector arithmetic shift right words, shuffle even halfwords", + fHIDE(int ) shamt = RtV & 0xF; + fSETHALF(0,VdV.w[i], fVSATUH( (VvV.uw[i] + fBIDIR_ASHIFTL(1,(shamt-1),4_8) ) >> shamt)); + fSETHALF(1,VdV.w[i], fVSATUH( (VuV.uw[i] + fBIDIR_ASHIFTL(1,(shamt-1),4_8) ) >> shamt))) +#endif + + + +ITERATOR_INSN2_SHIFT_SLOT(32,vroundwh,"Vd32=vroundwh(Vu32,Vv32):sat","Vd32.h=vround(Vu32.w,Vv32.w):sat", +"Vector round words to halves, shuffle resultant halfwords", + fSETHALF(0, VdV.uw[i], fVSATH((VvV.w[i] + fCONSTLL(0x8000)) >> 16)); + fSETHALF(1, VdV.uw[i], fVSATH((VuV.w[i] + fCONSTLL(0x8000)) >> 16))) + +ITERATOR_INSN2_SHIFT_SLOT(32,vroundwuh,"Vd32=vroundwuh(Vu32,Vv32):sat","Vd32.uh=vround(Vu32.w,Vv32.w):sat", +"Vector round words to halves, shuffle resultant halfwords", + fSETHALF(0, VdV.uw[i], fVSATUH((VvV.w[i] + fCONSTLL(0x8000)) >> 16)); + fSETHALF(1, VdV.uw[i], fVSATUH((VuV.w[i] + fCONSTLL(0x8000)) >> 16))) + +ITERATOR_INSN2_SHIFT_SLOT(32,vrounduwuh,"Vd32=vrounduwuh(Vu32,Vv32):sat","Vd32.uh=vround(Vu32.uw,Vv32.uw):sat", +"Vector round words to halves, shuffle resultant halfwords", + fSETHALF(0, VdV.uw[i], fVSATUH((VvV.uw[i] + fCONSTLL(0x8000)) >> 16)); + fSETHALF(1, VdV.uw[i], fVSATUH((VuV.uw[i] + fCONSTLL(0x8000)) >> 16))) + + + + + +/* HALF TO BYTE*/ + +ITERATOR_INSN2_SHIFT_SLOT(16,vroundhb,"Vd32=vroundhb(Vu32,Vv32):sat","Vd32.b=vround(Vu32.h,Vv32.h):sat", +"Vector round words to halves, shuffle resultant halfwords", + fSETBYTE(0, VdV.uh[i], fVSATB((VvV.h[i] + 0x80) >> 8)); + fSETBYTE(1, VdV.uh[i], fVSATB((VuV.h[i] + 0x80) >> 8))) + +ITERATOR_INSN2_SHIFT_SLOT(16,vroundhub,"Vd32=vroundhub(Vu32,Vv32):sat","Vd32.ub=vround(Vu32.h,Vv32.h):sat", +"Vector round words to halves, shuffle resultant halfwords", + fSETBYTE(0, VdV.uh[i], fVSATUB((VvV.h[i] + 0x80) >> 8)); + fSETBYTE(1, VdV.uh[i], fVSATUB((VuV.h[i] + 0x80) >> 8))) + +ITERATOR_INSN2_SHIFT_SLOT(16,vrounduhub,"Vd32=vrounduhub(Vu32,Vv32):sat","Vd32.ub=vround(Vu32.uh,Vv32.uh):sat", +"Vector round words to halves, shuffle resultant halfwords", + fSETBYTE(0, VdV.uh[i], fVSATUB((VvV.uh[i] + 0x80) >> 8)); + fSETBYTE(1, VdV.uh[i], fVSATUB((VuV.uh[i] + 0x80) >> 8))) + + +ITERATOR_INSN2_SHIFT_SLOT(32,vaslw_acc,"Vx32+=vaslw(Vu32,Rt32)","Vx32.w+=vasl(Vu32.w,Rt32)", +"Vector shift add word", + VxV.w[i] += (VuV.w[i] << (RtV & (32-1)))) + +ITERATOR_INSN2_SHIFT_SLOT(32,vasrw_acc,"Vx32+=vasrw(Vu32,Rt32)","Vx32.w+=vasr(Vu32.w,Rt32)", +"Vector shift add word", + VxV.w[i] += (VuV.w[i] >> (RtV & (32-1)))) + +ITERATOR_INSN2_SHIFT_SLOT_NOV1(16,vaslh_acc,"Vx32+=vaslh(Vu32,Rt32)","Vx32.h+=vasl(Vu32.h,Rt32)", +"Vector shift add halfword", + VxV.h[i] += (VuV.h[i] << (RtV & (16-1)))) + +ITERATOR_INSN2_SHIFT_SLOT_NOV1(16,vasrh_acc,"Vx32+=vasrh(Vu32,Rt32)","Vx32.h+=vasr(Vu32.h,Rt32)", +"Vector shift add halfword", + VxV.h[i] += (VuV.h[i] >> (RtV & (16-1)))) + +/************************************************************************** +* +* MMVECTOR ELEMENT-WISE ARITHMETIC +* +**************************************************************************/ + +/************************************************************************** +* MACROS GO IN MACROS.DEF NOT HERE!!! +**************************************************************************/ + + +#define MMVEC_ABSDIFF(TYPE,TYPE2,DESCR, WIDTH, DEST,SRC)\ +ITERATOR_INSN2_MPY_SLOT(WIDTH, vabsdiff##TYPE, "Vd32=vabsdiff"TYPE2"(Vu32,Vv32)" ,"Vd32."#DEST"=vabsdiff(Vu32."#SRC",Vv32."#SRC")" , "Vector Absolute of Difference "DESCR, VdV.DEST[i] = (VuV.SRC[i] > VvV.SRC[i]) ? (VuV.SRC[i] - VvV.SRC[i]) : (VvV.SRC[i] - VuV.SRC[i])) + +#define MMVEC_ADDU_SAT(TYPE,TYPE2,DESCR, WIDTH, DEST,SRC)\ +ITERATOR_INSN2_ANY_SLOT(WIDTH, vadd##TYPE##sat, "Vd32=vadd"TYPE2"(Vu32,Vv32):sat" , "Vd32."#DEST"=vadd(Vu32."#SRC",Vv32."#SRC"):sat", "Vector Add & Saturate "DESCR, VdV.DEST[i] = fVUADDSAT(WIDTH, VuV.SRC[i], VvV.SRC[i]))\ +ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(WIDTH, vadd##TYPE##sat_dv, "Vdd32=vadd"TYPE2"(Vuu32,Vvv32):sat", "Vdd32."#DEST"=vadd(Vuu32."#SRC",Vvv32."#SRC"):sat", "Double Vector Add & Saturate "DESCR, VddV.v[0].DEST[i] = fVUADDSAT(WIDTH, VuuV.v[0].SRC[i],VvvV.v[0].SRC[i]); VddV.v[1].DEST[i] = fVUADDSAT(WIDTH, VuuV.v[1].SRC[i],VvvV.v[1].SRC[i]))\ +ITERATOR_INSN2_ANY_SLOT(WIDTH, vsub##TYPE##sat, "Vd32=vsub"TYPE2"(Vu32,Vv32):sat", "Vd32."#DEST"=vsub(Vu32."#SRC",Vv32."#SRC"):sat", "Vector Add & Saturate "DESCR, VdV.DEST[i] = fVUSUBSAT(WIDTH, VuV.SRC[i], VvV.SRC[i]))\ +ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(WIDTH, vsub##TYPE##sat_dv, "Vdd32=vsub"TYPE2"(Vuu32,Vvv32):sat", "Vdd32."#DEST"=vsub(Vuu32."#SRC",Vvv32."#SRC"):sat", "Double Vector Add & Saturate "DESCR, VddV.v[0].DEST[i] = fVUSUBSAT(WIDTH, VuuV.v[0].SRC[i],VvvV.v[0].SRC[i]); VddV.v[1].DEST[i] = fVUSUBSAT(WIDTH, VuuV.v[1].SRC[i],VvvV.v[1].SRC[i]))\ + +#define MMVEC_ADDS_SAT(TYPE,TYPE2,DESCR, WIDTH,DEST,SRC)\ +ITERATOR_INSN2_ANY_SLOT(WIDTH, vadd##TYPE##sat, "Vd32=vadd"TYPE2"(Vu32,Vv32):sat" , "Vd32."#DEST"=vadd(Vu32."#SRC",Vv32."#SRC"):sat", "Vector Add & Saturate "DESCR, VdV.DEST[i] = fVSADDSAT(WIDTH, VuV.SRC[i], VvV.SRC[i]))\ +ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(WIDTH, vadd##TYPE##sat_dv, "Vdd32=vadd"TYPE2"(Vuu32,Vvv32):sat", "Vdd32."#DEST"=vadd(Vuu32."#SRC",Vvv32."#SRC"):sat", "Double Vector Add & Saturate "DESCR, VddV.v[0].DEST[i] = fVSADDSAT(WIDTH, VuuV.v[0].SRC[i], VvvV.v[0].SRC[i]); VddV.v[1].DEST[i] = fVSADDSAT(WIDTH, VuuV.v[1].SRC[i], VvvV.v[1].SRC[i]))\ +ITERATOR_INSN2_ANY_SLOT(WIDTH, vsub##TYPE##sat, "Vd32=vsub"TYPE2"(Vu32,Vv32):sat", "Vd32."#DEST"=vsub(Vu32."#SRC",Vv32."#SRC"):sat", "Vector Add & Saturate "DESCR, VdV.DEST[i] = fVSSUBSAT(WIDTH, VuV.SRC[i], VvV.SRC[i]))\ +ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(WIDTH, vsub##TYPE##sat_dv, "Vdd32=vsub"TYPE2"(Vuu32,Vvv32):sat", "Vdd32."#DEST"=vsub(Vuu32."#SRC",Vvv32."#SRC"):sat", "Double Vector Add & Saturate "DESCR, VddV.v[0].DEST[i] = fVSSUBSAT(WIDTH, VuuV.v[0].SRC[i], VvvV.v[0].SRC[i]); VddV.v[1].DEST[i] = fVSSUBSAT(WIDTH, VuuV.v[1].SRC[i], VvvV.v[1].SRC[i]))\ + +#define MMVEC_AVGU(TYPE,TYPE2,DESCR, WIDTH, DEST,SRC)\ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vavg##TYPE, "Vd32=vavg"TYPE2"(Vu32,Vv32)", "Vd32."#DEST"=vavg(Vu32."#SRC",Vv32."#SRC")", "Vector Average "DESCR, VdV.DEST[i] = fVAVGU( WIDTH, VuV.SRC[i], VvV.SRC[i])) \ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vavg##TYPE##rnd, "Vd32=vavg"TYPE2"(Vu32,Vv32):rnd", "Vd32."#DEST"=vavg(Vu32."#SRC",Vv32."#SRC"):rnd", "Vector Average % Round"DESCR, VdV.DEST[i] = fVAVGURND(WIDTH, VuV.SRC[i], VvV.SRC[i])) + + + +#define MMVEC_AVGS(TYPE,TYPE2,DESCR, WIDTH, DEST,SRC)\ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vavg##TYPE, "Vd32=vavg"TYPE2"(Vu32,Vv32)", "Vd32."#DEST"=vavg(Vu32."#SRC",Vv32."#SRC")", "Vector Average "DESCR, VdV.DEST[i] = fVAVGS( WIDTH, VuV.SRC[i], VvV.SRC[i])) \ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vavg##TYPE##rnd, "Vd32=vavg"TYPE2"(Vu32,Vv32):rnd", "Vd32."#DEST"=vavg(Vu32."#SRC",Vv32."#SRC"):rnd", "Vector Average % Round"DESCR, VdV.DEST[i] = fVAVGSRND( WIDTH, VuV.SRC[i], VvV.SRC[i])) \ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vnavg##TYPE, "Vd32=vnavg"TYPE2"(Vu32,Vv32)", "Vd32."#DEST"=vnavg(Vu32."#SRC",Vv32."#SRC")", "Vector Negative Average "DESCR, VdV.DEST[i] = fVNAVGS( WIDTH, VuV.SRC[i], VvV.SRC[i])) + + + + + + + +#define MMVEC_ADDWRAP(TYPE,TYPE2, DESCR, WIDTH , DEST,SRC)\ +ITERATOR_INSN2_ANY_SLOT(WIDTH, vadd##TYPE, "Vd32=vadd"TYPE2"(Vu32,Vv32)" , "Vd32."#DEST"=vadd(Vu32."#SRC",Vv32."#SRC")", "Vector Add "DESCR, VdV.DEST[i] = VuV.SRC[i] + VvV.SRC[i])\ +ITERATOR_INSN2_ANY_SLOT(WIDTH, vsub##TYPE, "Vd32=vsub"TYPE2"(Vu32,Vv32)" , "Vd32."#DEST"=vsub(Vu32."#SRC",Vv32."#SRC")", "Vector Sub "DESCR, VdV.DEST[i] = VuV.SRC[i] - VvV.SRC[i])\ +ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(WIDTH, vadd##TYPE##_dv, "Vdd32=vadd"TYPE2"(Vuu32,Vvv32)" , "Vdd32."#DEST"=vadd(Vuu32."#SRC",Vvv32."#SRC")", "Double Vector Add "DESCR, VddV.v[0].DEST[i] = VuuV.v[0].SRC[i] + VvvV.v[0].SRC[i]; VddV.v[1].DEST[i] = VuuV.v[1].SRC[i] + VvvV.v[1].SRC[i])\ +ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(WIDTH, vsub##TYPE##_dv, "Vdd32=vsub"TYPE2"(Vuu32,Vvv32)" , "Vdd32."#DEST"=vsub(Vuu32."#SRC",Vvv32."#SRC")", "Double Vector Sub "DESCR, VddV.v[0].DEST[i] = VuuV.v[0].SRC[i] - VvvV.v[0].SRC[i]; VddV.v[1].DEST[i] = VuuV.v[1].SRC[i] - VvvV.v[1].SRC[i]) \ + + + + + +/* Wrapping Adds */ +MMVEC_ADDWRAP(b, "b", "Byte", 8, b, b) +MMVEC_ADDWRAP(h, "h", "Halfword", 16, h, h) +MMVEC_ADDWRAP(w, "w", "Word", 32, w, w) + +/* Saturating Adds */ +MMVEC_ADDU_SAT(ub, "ub", "Unsigned Byte", 8, ub, ub) +MMVEC_ADDU_SAT(uh, "uh", "Unsigned Halfword", 16, uh, uh) +MMVEC_ADDU_SAT(uw, "uw", "Unsigned word", 32, uw, uw) +MMVEC_ADDS_SAT(b, "b", "byte", 8, b, b) +MMVEC_ADDS_SAT(h, "h", "Halfword", 16, h, h) +MMVEC_ADDS_SAT(w, "w", "Word", 32, w, w) + + +/* Averaging Instructions */ +MMVEC_AVGU(ub,"ub", "Unsigned Byte", 8, ub, ub) +MMVEC_AVGU(uh,"uh", "Unsigned Halfword", 16, uh, uh) +MMVEC_AVGU_NOV1(uw,"uw", "Unsigned Word", 32, uw, uw) +MMVEC_AVGS_NOV1(b, "b", "Byte", 8, b, b) +MMVEC_AVGS(h, "h", "Halfword", 16, h, h) +MMVEC_AVGS(w, "w", "Word", 32, w, w) + + +/* Absolute Difference */ +MMVEC_ABSDIFF(ub,"ub", "Unsigned Byte", 8, ub, ub) +MMVEC_ABSDIFF(uh,"uh", "Unsigned Halfword", 16, uh, uh) +MMVEC_ABSDIFF(h,"h", "Halfword", 16, uh, h) +MMVEC_ABSDIFF(w,"w", "Word", 32, uw, w) + +ITERATOR_INSN2_ANY_SLOT(8,vnavgub, "Vd32=vnavgub(Vu32,Vv32)", "Vd32.b=vnavg(Vu32.ub,Vv32.ub)", +"Vector Negative Average Unsigned Byte", VdV.b[i] = fVNAVGU(8, VuV.ub[i], VvV.ub[i])) + +ITERATOR_INSN_ANY_SLOT(32,vaddcarrysat,"Vd32.w=vadd(Vu32.w,Vv32.w,Qs4):carry:sat","add w/carry and saturate", +VdV.w[i] = fVSATW(VuV.w[i]+VvV.w[i]+fGETQBIT(QsV,i*4))) + +ITERATOR_INSN_ANY_SLOT(32,vaddcarry,"Vd32.w=vadd(Vu32.w,Vv32.w,Qx4):carry","add w/carry", +VdV.w[i] = VuV.w[i]+VvV.w[i]+fGETQBIT(QxV,i*4); +fSETQBITS(QxV,4,0xF,4*i,-fCARRY_FROM_ADD32(VuV.w[i],VvV.w[i],fGETQBIT(QxV,i*4)))) + +ITERATOR_INSN_ANY_SLOT(32,vsubcarry,"Vd32.w=vsub(Vu32.w,Vv32.w,Qx4):carry","add w/carry", +VdV.w[i] = VuV.w[i]+~VvV.w[i]+fGETQBIT(QxV,i*4); +fSETQBITS(QxV,4,0xF,4*i,-fCARRY_FROM_ADD32(VuV.w[i],~VvV.w[i],fGETQBIT(QxV,i*4)))) + +ITERATOR_INSN_ANY_SLOT(32,vaddcarryo,"Vd32.w,Qe4=vadd(Vu32.w,Vv32.w):carry","add w/carry out-only", +VdV.w[i] = VuV.w[i]+VvV.w[i]; +fSETQBITS(QeV,4,0xF,4*i,-fCARRY_FROM_ADD32(VuV.w[i],VvV.w[i],0))) + +ITERATOR_INSN_ANY_SLOT(32,vsubcarryo,"Vd32.w,Qe4=vsub(Vu32.w,Vv32.w):carry","subtract w/carry out-only", +VdV.w[i] = VuV.w[i]+~VvV.w[i]+1; +fSETQBITS(QeV,4,0xF,4*i,-fCARRY_FROM_ADD32(VuV.w[i],~VvV.w[i],1))) + + +ITERATOR_INSN_ANY_SLOT(32,vsatdw,"Vd32.w=vsatdw(Vu32.w,Vv32.w)","Saturate from 64-bits (higher 32-bits come from first vector) to 32-bits",VdV.w[i] = fVSATDW(VuV.w[i],VvV.w[i])) + + +#define MMVEC_ADDSAT_MIX(TAGEND,SATF,WIDTH,DEST,SRC1,SRC2)\ +ITERATOR_INSN_ANY_SLOT(WIDTH, vadd##TAGEND,"Vd32."#DEST"=vadd(Vu32."#SRC1",Vv32."#SRC2"):sat", "Vector Add mixed", VdV.DEST[i] = SATF(VuV.SRC1[i] + VvV.SRC2[i]))\ +ITERATOR_INSN_ANY_SLOT(WIDTH, vsub##TAGEND,"Vd32."#DEST"=vsub(Vu32."#SRC1",Vv32."#SRC2"):sat", "Vector Sub mixed", VdV.DEST[i] = SATF(VuV.SRC1[i] - VvV.SRC2[i]))\ + +MMVEC_ADDSAT_MIX(ububb_sat,fVSATUB,8,ub,ub,b) + +/**************************** +* WIDENING +****************************/ + + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vaddubh,"Vdd32=vaddub(Vu32,Vv32)","Vdd32.h=vadd(Vu32.ub,Vv32.ub)", +"Vector addition with widen into two vectors", + VddV.v[0].h[i] = fZE8_16(fGETUBYTE(0, VuV.uh[i])) + fZE8_16(fGETUBYTE(0, VvV.uh[i])); + VddV.v[1].h[i] = fZE8_16(fGETUBYTE(1, VuV.uh[i])) + fZE8_16(fGETUBYTE(1, VvV.uh[i]))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vsububh,"Vdd32=vsubub(Vu32,Vv32)","Vdd32.h=vsub(Vu32.ub,Vv32.ub)", +"Vector subtraction with widen into two vectors", + VddV.v[0].h[i] = fZE8_16(fGETUBYTE(0, VuV.uh[i])) - fZE8_16(fGETUBYTE(0, VvV.uh[i])); + VddV.v[1].h[i] = fZE8_16(fGETUBYTE(1, VuV.uh[i])) - fZE8_16(fGETUBYTE(1, VvV.uh[i]))) + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vaddhw,"Vdd32=vaddh(Vu32,Vv32)","Vdd32.w=vadd(Vu32.h,Vv32.h)", +"Vector addition with widen into two vectors", + VddV.v[0].w[i] = fGETHALF(0, VuV.w[i]) + fGETHALF(0, VvV.w[i]); + VddV.v[1].w[i] = fGETHALF(1, VuV.w[i]) + fGETHALF(1, VvV.w[i])) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vsubhw,"Vdd32=vsubh(Vu32,Vv32)","Vdd32.w=vsub(Vu32.h,Vv32.h)", +"Vector subtraction with widen into two vectors", + VddV.v[0].w[i] = fGETHALF(0, VuV.w[i]) - fGETHALF(0, VvV.w[i]); + VddV.v[1].w[i] = fGETHALF(1, VuV.w[i]) - fGETHALF(1, VvV.w[i])) + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vadduhw,"Vdd32=vadduh(Vu32,Vv32)","Vdd32.w=vadd(Vu32.uh,Vv32.uh)", +"Vector addition with widen into two vectors", + VddV.v[0].w[i] = fZE16_32(fGETUHALF(0, VuV.uw[i])) + fZE16_32(fGETUHALF(0, VvV.uw[i])); + VddV.v[1].w[i] = fZE16_32(fGETUHALF(1, VuV.uw[i])) + fZE16_32(fGETUHALF(1, VvV.uw[i]))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vsubuhw,"Vdd32=vsubuh(Vu32,Vv32)","Vdd32.w=vsub(Vu32.uh,Vv32.uh)", +"Vector subtraction with widen into two vectors", + VddV.v[0].w[i] = fZE16_32(fGETUHALF(0, VuV.uw[i])) - fZE16_32(fGETUHALF(0, VvV.uw[i])); + VddV.v[1].w[i] = fZE16_32(fGETUHALF(1, VuV.uw[i])) - fZE16_32(fGETUHALF(1, VvV.uw[i]))) + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vaddhw_acc,"Vxx32+=vaddh(Vu32,Vv32)","Vxx32.w+=vadd(Vu32.h,Vv32.h)", +"Vector addition with widen into two vectors", + VxxV.v[0].w[i] += fGETHALF(0, VuV.w[i]) + fGETHALF(0, VvV.w[i]); + VxxV.v[1].w[i] += fGETHALF(1, VuV.w[i]) + fGETHALF(1, VvV.w[i])) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vadduhw_acc,"Vxx32+=vadduh(Vu32,Vv32)","Vxx32.w+=vadd(Vu32.uh,Vv32.uh)", +"Vector addition with widen into two vectors", + VxxV.v[0].w[i] += fGETUHALF(0, VuV.w[i]) + fGETUHALF(0, VvV.w[i]); + VxxV.v[1].w[i] += fGETUHALF(1, VuV.w[i]) + fGETUHALF(1, VvV.w[i])) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vaddubh_acc,"Vxx32+=vaddub(Vu32,Vv32)","Vxx32.h+=vadd(Vu32.ub,Vv32.ub)", +"Vector addition with widen into two vectors", + VxxV.v[0].h[i] += fGETUBYTE(0, VuV.h[i]) + fGETUBYTE(0, VvV.h[i]); + VxxV.v[1].h[i] += fGETUBYTE(1, VuV.h[i]) + fGETUBYTE(1, VvV.h[i])) + + +/**************************** +* Conditional +****************************/ + +#define CONDADDSUB(WIDTH,TAGEND,LHSYN,RHSYN,DESCR,LHBEH,RHBEH) \ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vadd##TAGEND##q,"if (Qv4."#TAGEND") "LHSYN"+="RHSYN,"if (Qv4) "LHSYN"+="RHSYN,DESCR,LHBEH=fCONDMASK##WIDTH(QvV,i,LHBEH+RHBEH,LHBEH)) \ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vsub##TAGEND##q,"if (Qv4."#TAGEND") "LHSYN"-="RHSYN,"if (Qv4) "LHSYN"-="RHSYN,DESCR,LHBEH=fCONDMASK##WIDTH(QvV,i,LHBEH-RHBEH,LHBEH)) \ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vadd##TAGEND##nq,"if (!Qv4."#TAGEND") "LHSYN"+="RHSYN,"if (!Qv4) "LHSYN"+="RHSYN,DESCR,LHBEH=fCONDMASK##WIDTH(QvV,i,LHBEH,LHBEH+RHBEH)) \ +ITERATOR_INSN2_ANY_SLOT(WIDTH,vsub##TAGEND##nq,"if (!Qv4."#TAGEND") "LHSYN"-="RHSYN,"if (!Qv4) "LHSYN"-="RHSYN,DESCR,LHBEH=fCONDMASK##WIDTH(QvV,i,LHBEH,LHBEH-RHBEH)) \ + +CONDADDSUB(8,b,"Vx32.b","Vu32.b","Conditional add/sub Byte",VxV.ub[i],VuV.ub[i]) +CONDADDSUB(16,h,"Vx32.h","Vu32.h","Conditional add/sub Half",VxV.h[i],VuV.h[i]) +CONDADDSUB(32,w,"Vx32.w","Vu32.w","Conditional add/sub Word",VxV.w[i],VuV.w[i]) + +/***************************************************** + ABSOLUTE VALUES +*****************************************************/ +// V65 +ITERATOR_INSN2_ANY_SLOT_NOV1(8,vabsb, "Vd32=vabsb(Vu32)", "Vd32.b=vabs(Vu32.b)", "Vector absolute value of bytes", VdV.b[i] = fABS(VuV.b[i])) +ITERATOR_INSN2_ANY_SLOT_NOV1(8,vabsb_sat, "Vd32=vabsb(Vu32):sat", "Vd32.b=vabs(Vu32.b):sat", "Vector absolute value of bytes", VdV.b[i] = fVSATB(fABS(fSE8_16(VuV.b[i])))) + + +ITERATOR_INSN2_ANY_SLOT(16,vabsh, "Vd32=vabsh(Vu32)", "Vd32.h=vabs(Vu32.h)", "Vector absolute value of halfwords", VdV.h[i] = fABS(VuV.h[i])) +ITERATOR_INSN2_ANY_SLOT(16,vabsh_sat, "Vd32=vabsh(Vu32):sat", "Vd32.h=vabs(Vu32.h):sat", "Vector absolute value of halfwords", VdV.h[i] = fVSATH(fABS(fSE16_32(VuV.h[i])))) +ITERATOR_INSN2_ANY_SLOT(32,vabsw, "Vd32=vabsw(Vu32)", "Vd32.w=vabs(Vu32.w)", "Vector absolute value of words", VdV.w[i] = fABS(VuV.w[i])) +ITERATOR_INSN2_ANY_SLOT(32,vabsw_sat, "Vd32=vabsw(Vu32):sat", "Vd32.w=vabs(Vu32.w):sat", "Vector absolute value of words", VdV.w[i] = fVSATW(fABS(fSE32_64(VuV.w[i])))) + + +/************************************************************************** + * MMVECTOR MULTIPLICATIONS + * ************************************************************************/ + + +/* Byte by Byte */ +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpybv,"Vdd32=vmpyb(Vu32,Vv32)","Vdd32.h=vmpy(Vu32.b,Vv32.b)", +"Vector absolute value of words", + VddV.v[0].h[i] = fMPY8SS(fGETBYTE(0, VuV.h[i]), fGETBYTE(0, VvV.h[i])); + VddV.v[1].h[i] = fMPY8SS(fGETBYTE(1, VuV.h[i]), fGETBYTE(1, VvV.h[i]))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpybv_acc,"Vxx32+=vmpyb(Vu32,Vv32)","Vxx32.h+=vmpy(Vu32.b,Vv32.b)", +"Vector absolute value of words", + VxxV.v[0].h[i] += fMPY8SS(fGETBYTE(0, VuV.h[i]), fGETBYTE(0, VvV.h[i])); + VxxV.v[1].h[i] += fMPY8SS(fGETBYTE(1, VuV.h[i]), fGETBYTE(1, VvV.h[i]))) + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpyubv,"Vdd32=vmpyub(Vu32,Vv32)","Vdd32.uh=vmpy(Vu32.ub,Vv32.ub)", +"Vector absolute value of words", + VddV.v[0].uh[i] = fMPY8UU(fGETUBYTE(0, VuV.uh[i]), fGETUBYTE(0, VvV.uh[i]) ); + VddV.v[1].uh[i] = fMPY8UU(fGETUBYTE(1, VuV.uh[i]), fGETUBYTE(1, VvV.uh[i]) )) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpyubv_acc,"Vxx32+=vmpyub(Vu32,Vv32)","Vxx32.uh+=vmpy(Vu32.ub,Vv32.ub)", +"Vector absolute value of words", + VxxV.v[0].uh[i] += fMPY8UU(fGETUBYTE(0, VuV.uh[i]), fGETUBYTE(0, VvV.uh[i]) ); + VxxV.v[1].uh[i] += fMPY8UU(fGETUBYTE(1, VuV.uh[i]), fGETUBYTE(1, VvV.uh[i]) )) + + + + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpybusv,"Vdd32=vmpybus(Vu32,Vv32)","Vdd32.h=vmpy(Vu32.ub,Vv32.b)", +"Vector absolute value of words", + VddV.v[0].h[i] = fMPY8US(fGETUBYTE(0, VuV.uh[i]), fGETBYTE(0, VvV.h[i])); + VddV.v[1].h[i] = fMPY8US(fGETUBYTE(1, VuV.uh[i]), fGETBYTE(1, VvV.h[i]))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpybusv_acc,"Vxx32+=vmpybus(Vu32,Vv32)","Vxx32.h+=vmpy(Vu32.ub,Vv32.b)", +"Vector absolute value of words", + VxxV.v[0].h[i] += fMPY8US(fGETUBYTE(0, VuV.uh[i]), fGETBYTE(0, VvV.h[i])); + VxxV.v[1].h[i] += fMPY8US(fGETUBYTE(1, VuV.uh[i]), fGETBYTE(1, VvV.h[i]))) + + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpabusv,"Vdd32=vmpabus(Vuu32,Vvv32)","Vdd32.h=vmpa(Vuu32.ub,Vvv32.b)", +"Vertical Byte Multiply", + VddV.v[0].h[i] = fMPY8US(fGETUBYTE(0, VuuV.v[0].uh[i]), fGETBYTE(0, VvvV.v[0].uh[i])) + fMPY8US(fGETUBYTE(0, VuuV.v[1].uh[i]), fGETBYTE(0, VvvV.v[1].uh[i])); + VddV.v[1].h[i] = fMPY8US(fGETUBYTE(1, VuuV.v[0].uh[i]), fGETBYTE(1, VvvV.v[0].uh[i])) + fMPY8US(fGETUBYTE(1, VuuV.v[1].uh[i]), fGETBYTE(1, VvvV.v[1].uh[i]))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpabuuv,"Vdd32=vmpabuu(Vuu32,Vvv32)","Vdd32.h=vmpa(Vuu32.ub,Vvv32.ub)", +"Vertical Byte Multiply", + VddV.v[0].h[i] = fMPY8UU(fGETUBYTE(0, VuuV.v[0].uh[i]), fGETUBYTE(0, VvvV.v[0].uh[i])) + fMPY8UU(fGETUBYTE(0, VuuV.v[1].uh[i]), fGETUBYTE(0, VvvV.v[1].uh[i])); + VddV.v[1].h[i] = fMPY8UU(fGETUBYTE(1, VuuV.v[0].uh[i]), fGETUBYTE(1, VvvV.v[0].uh[i])) + fMPY8UU(fGETUBYTE(1, VuuV.v[1].uh[i]), fGETUBYTE(1, VvvV.v[1].uh[i]))) + + + + + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyhv,"Vdd32=vmpyh(Vu32,Vv32)","Vdd32.w=vmpy(Vu32.h,Vv32.h)", +"Vector by Vector Halfword Multiply", + VddV.v[0].w[i] = fMPY16SS(fGETHALF(0, VuV.w[i]), fGETHALF(0, VvV.w[i])); + VddV.v[1].w[i] = fMPY16SS(fGETHALF(1, VuV.w[i]), fGETHALF(1, VvV.w[i]))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyhv_acc,"Vxx32+=vmpyh(Vu32,Vv32)","Vxx32.w+=vmpy(Vu32.h,Vv32.h)", +"Vector by Vector Halfword Multiply", + VxxV.v[0].w[i] += fMPY16SS(fGETHALF(0, VuV.w[i]), fGETHALF(0, VvV.w[i])); + VxxV.v[1].w[i] += fMPY16SS(fGETHALF(1, VuV.w[i]), fGETHALF(1, VvV.w[i]))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyuhv,"Vdd32=vmpyuh(Vu32,Vv32)","Vdd32.uw=vmpy(Vu32.uh,Vv32.uh)", +"Vector by Vector Unsigned Halfword Multiply", + VddV.v[0].uw[i] = fMPY16UU(fGETUHALF(0, VuV.uw[i]), fGETUHALF(0, VvV.uw[i])); + VddV.v[1].uw[i] = fMPY16UU(fGETUHALF(1, VuV.uw[i]), fGETUHALF(1, VvV.uw[i]))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyuhv_acc,"Vxx32+=vmpyuh(Vu32,Vv32)","Vxx32.uw+=vmpy(Vu32.uh,Vv32.uh)", +"Vector by Vector Unsigned Halfword Multiply", + VxxV.v[0].uw[i] += fMPY16UU(fGETUHALF(0, VuV.uw[i]), fGETUHALF(0, VvV.uw[i])); + VxxV.v[1].uw[i] += fMPY16UU(fGETUHALF(1, VuV.uw[i]), fGETUHALF(1, VvV.uw[i]))) + + + +/* Vector by Vector */ +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpyhvsrs,"Vd32=vmpyh(Vu32,Vv32):<<1:rnd:sat","Vd32.h=vmpy(Vu32.h,Vv32.h):<<1:rnd:sat", +"Vector halfword multiply with round, shift, and sat16", + VdV.h[i] = fVSATH(fGETHALF(1,fVSAT(fROUND((fMPY16SS(VuV.h[i],VvV.h[i] )<<1)))))) + + + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyhus, "Vdd32=vmpyhus(Vu32,Vv32)","Vdd32.w=vmpy(Vu32.h,Vv32.uh)", +"Vector by Vector Halfword Multiply", + VddV.v[0].w[i] = fMPY16SU(fGETHALF(0, VuV.w[i]), fGETUHALF(0, VvV.uw[i])); + VddV.v[1].w[i] = fMPY16SU(fGETHALF(1, VuV.w[i]), fGETUHALF(1, VvV.uw[i]))) + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyhus_acc, "Vxx32+=vmpyhus(Vu32,Vv32)","Vxx32.w+=vmpy(Vu32.h,Vv32.uh)", +"Vector by Vector Halfword Multiply", + VxxV.v[0].w[i] += fMPY16SU(fGETHALF(0, VuV.w[i]), fGETUHALF(0, VvV.uw[i])); + VxxV.v[1].w[i] += fMPY16SU(fGETHALF(1, VuV.w[i]), fGETUHALF(1, VvV.uw[i]))) + + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpyih,"Vd32=vmpyih(Vu32,Vv32)","Vd32.h=vmpyi(Vu32.h,Vv32.h)", +"Vector by Vector Halfword Multiply", + VdV.h[i] = fMPY16SS(VuV.h[i], VvV.h[i])) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpyih_acc,"Vx32+=vmpyih(Vu32,Vv32)","Vx32.h+=vmpyi(Vu32.h,Vv32.h)", +"Vector by Vector Halfword Multiply", + VxV.h[i] += fMPY16SS(VuV.h[i], VvV.h[i])) + + + +/* 32x32 high half / frac */ + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyewuh,"Vd32=vmpyewuh(Vu32,Vv32)","Vd32.w=vmpye(Vu32.w,Vv32.uh)", +"Vector by Vector Halfword Multiply", +VdV.w[i] = fMPY3216SU(VuV.w[i], fGETUHALF(0, VvV.w[i])) >> 16) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyowh,"Vd32=vmpyowh(Vu32,Vv32):<<1:sat","Vd32.w=vmpyo(Vu32.w,Vv32.h):<<1:sat", +"Vector by Vector Halfword Multiply", +VdV.w[i] = fVSATW((((fMPY3216SS(VuV.w[i], fGETHALF(1, VvV.w[i])) >> 14) + 0) >> 1))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyowh_rnd,"Vd32=vmpyowh(Vu32,Vv32):<<1:rnd:sat","Vd32.w=vmpyo(Vu32.w,Vv32.h):<<1:rnd:sat", +"Vector by Vector Halfword Multiply", +VdV.w[i] = fVSATW((((fMPY3216SS(VuV.w[i], fGETHALF(1, VvV.w[i])) >> 14) + 1) >> 1))) + +ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC(32,vmpyewuh_64,"Vdd32=vmpye(Vu32.w,Vv32.uh)", +"Word times Halfword Multiply, 64-bit result", + fHIDE(size8s_t prod;) + prod = fMPY32SU(VuV.w[i],fGETUHALF(0,VvV.w[i])); + VddV.v[1].w[i] = prod >> 16; + VddV.v[0].w[i] = prod << 16) + +ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC(32,vmpyowh_64_acc,"Vxx32+=vmpyo(Vu32.w,Vv32.h)", +"Word times Halfword Multiply, 64-bit result", + fHIDE(size8s_t prod;) + prod = fMPY32SS(VuV.w[i],fGETHALF(1,VvV.w[i])) + fSE32_64(VxxV.v[1].w[i]); + VxxV.v[1].w[i] = prod >> 16; + fSETHALF(0, VxxV.v[0].w[i], VxxV.v[0].w[i] >> 16); + fSETHALF(1, VxxV.v[0].w[i], prod & 0x0000ffff)) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyowh_sacc,"Vx32+=vmpyowh(Vu32,Vv32):<<1:sat:shift","Vx32.w+=vmpyo(Vu32.w,Vv32.h):<<1:sat:shift", +"Vector by Vector Halfword Multiply", +IV1DEAD() VxV.w[i] = fVSATW(((((VxV.w[i] + fMPY3216SS(VuV.w[i], fGETHALF(1, VvV.w[i]))) >> 14) + 0) >> 1))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyowh_rnd_sacc,"Vx32+=vmpyowh(Vu32,Vv32):<<1:rnd:sat:shift","Vx32.w+=vmpyo(Vu32.w,Vv32.h):<<1:rnd:sat:shift", +"Vector by Vector Halfword Multiply", +IV1DEAD() VxV.w[i] = fVSATW(((((VxV.w[i] + fMPY3216SS(VuV.w[i], fGETHALF(1, VvV.w[i]))) >> 14) + 1) >> 1))) + +/* For 32x32 integer / low half */ + +ITERATOR_INSN_MPY_SLOT(32,vmpyieoh,"Vd32.w=vmpyieo(Vu32.h,Vv32.h)","Odd/Even multiply for 32x32 low half", + VdV.w[i] = (fGETHALF(0,VuV.w[i])*fGETHALF(1,VvV.w[i])) << 16) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyiewuh,"Vd32=vmpyiewuh(Vu32,Vv32)","Vd32.w=vmpyie(Vu32.w,Vv32.uh)", +"Vector by Vector Word by Halfword Multiply", +IV1DEAD() VdV.w[i] = fMPY3216SU(VuV.w[i], fGETUHALF(0, VvV.w[i])) ) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyiowh,"Vd32=vmpyiowh(Vu32,Vv32)","Vd32.w=vmpyio(Vu32.w,Vv32.h)", +"Vector by Vector Word by Halfword Multiply", +IV1DEAD() VdV.w[i] = fMPY3216SS(VuV.w[i], fGETHALF(1, VvV.w[i])) ) + +/* Add back these... */ + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyiewh_acc,"Vx32+=vmpyiewh(Vu32,Vv32)","Vx32.w+=vmpyie(Vu32.w,Vv32.h)", +"Vector by Vector Word by Halfword Multiply", +VxV.w[i] = VxV.w[i] + fMPY3216SS(VuV.w[i], fGETHALF(0, VvV.w[i])) ) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyiewuh_acc,"Vx32+=vmpyiewuh(Vu32,Vv32)","Vx32.w+=vmpyie(Vu32.w,Vv32.uh)", +"Vector by Vector Word by Halfword Multiply", +VxV.w[i] = VxV.w[i] + fMPY3216SU(VuV.w[i], fGETUHALF(0, VvV.w[i])) ) + + + + + + + +/* Vector by Scalar */ +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpyub,"Vdd32=vmpyub(Vu32,Rt32)","Vdd32.uh=vmpy(Vu32.ub,Rt32.ub)", +"Vector absolute value of words", + VddV.v[0].uh[i] = fMPY8UU(fGETUBYTE(0, VuV.uh[i]), fGETUBYTE((2*i+0)%4, RtV)); + VddV.v[1].uh[i] = fMPY8UU(fGETUBYTE(1, VuV.uh[i]), fGETUBYTE((2*i+1)%4, RtV))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpyub_acc,"Vxx32+=vmpyub(Vu32,Rt32)","Vxx32.uh+=vmpy(Vu32.ub,Rt32.ub)", +"Vector absolute value of words", + VxxV.v[0].uh[i] += fMPY8UU(fGETUBYTE(0, VuV.uh[i]), fGETUBYTE((2*i+0)%4, RtV)); + VxxV.v[1].uh[i] += fMPY8UU(fGETUBYTE(1, VuV.uh[i]), fGETUBYTE((2*i+1)%4, RtV))) + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpybus,"Vdd32=vmpybus(Vu32,Rt32)","Vdd32.h=vmpy(Vu32.ub,Rt32.b)", +"Vector absolute value of words", + VddV.v[0].h[i] = fMPY8US(fGETUBYTE(0, VuV.uh[i]), fGETBYTE((2*i+0)%4, RtV)); + VddV.v[1].h[i] = fMPY8US(fGETUBYTE(1, VuV.uh[i]), fGETBYTE((2*i+1)%4, RtV))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpybus_acc,"Vxx32+=vmpybus(Vu32,Rt32)","Vxx32.h+=vmpy(Vu32.ub,Rt32.b)", +"Vector absolute value of words", + VxxV.v[0].h[i] += fMPY8US(fGETUBYTE(0, VuV.uh[i]), fGETBYTE((2*i+0)%4, RtV)); + VxxV.v[1].h[i] += fMPY8US(fGETUBYTE(1, VuV.uh[i]), fGETBYTE((2*i+1)%4, RtV))) + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpabus,"Vdd32=vmpabus(Vuu32,Rt32)","Vdd32.h=vmpa(Vuu32.ub,Rt32.b)", +"Vertical Byte Multiply", + VddV.v[0].h[i] = fMPY8US(fGETUBYTE(0, VuuV.v[0].uh[i]), fGETBYTE(0, RtV)) + fMPY16SS(fGETUBYTE(0, VuuV.v[1].uh[i]), fGETBYTE(1, RtV)); + VddV.v[1].h[i] = fMPY8US(fGETUBYTE(1, VuuV.v[0].uh[i]), fGETBYTE(2, RtV)) + fMPY16SS(fGETUBYTE(1, VuuV.v[1].uh[i]), fGETBYTE(3, RtV))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpabus_acc,"Vxx32+=vmpabus(Vuu32,Rt32)","Vxx32.h+=vmpa(Vuu32.ub,Rt32.b)", +"Vertical Byte Multiply", + VxxV.v[0].h[i] += fMPY8US(fGETUBYTE(0, VuuV.v[0].uh[i]), fGETBYTE(0, RtV)) + fMPY16SS(fGETUBYTE(0, VuuV.v[1].uh[i]), fGETBYTE(1, RtV)); + VxxV.v[1].h[i] += fMPY8US(fGETUBYTE(1, VuuV.v[0].uh[i]), fGETBYTE(2, RtV)) + fMPY16SS(fGETUBYTE(1, VuuV.v[1].uh[i]), fGETBYTE(3, RtV))) + +// V65 + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC_NOV1(16,vmpabuu,"Vdd32=vmpabuu(Vuu32,Rt32)","Vdd32.h=vmpa(Vuu32.ub,Rt32.ub)", +"Vertical Byte Multiply", + VddV.v[0].uh[i] = fMPY8UU(fGETUBYTE(0, VuuV.v[0].uh[i]), fGETUBYTE(0, RtV)) + fMPY8UU(fGETUBYTE(0, VuuV.v[1].uh[i]), fGETUBYTE(1, RtV)); + VddV.v[1].uh[i] = fMPY8UU(fGETUBYTE(1, VuuV.v[0].uh[i]), fGETUBYTE(2, RtV)) + fMPY8UU(fGETUBYTE(1, VuuV.v[1].uh[i]), fGETUBYTE(3, RtV))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC_NOV1(16,vmpabuu_acc,"Vxx32+=vmpabuu(Vuu32,Rt32)","Vxx32.h+=vmpa(Vuu32.ub,Rt32.ub)", +"Vertical Byte Multiply", + VxxV.v[0].uh[i] += fMPY8UU(fGETUBYTE(0, VuuV.v[0].uh[i]), fGETUBYTE(0, RtV)) + fMPY8UU(fGETUBYTE(0, VuuV.v[1].uh[i]), fGETUBYTE(1, RtV)); + VxxV.v[1].uh[i] += fMPY8UU(fGETUBYTE(1, VuuV.v[0].uh[i]), fGETUBYTE(2, RtV)) + fMPY8UU(fGETUBYTE(1, VuuV.v[1].uh[i]), fGETUBYTE(3, RtV))) + + + + +/* Half by Byte */ +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpahb,"Vdd32=vmpahb(Vuu32,Rt32)","Vdd32.w=vmpa(Vuu32.h,Rt32.b)", +"Vertical Byte Multiply", + VddV.v[0].w[i] = fMPY16SS(fGETHALF(0, VuuV.v[0].w[i]), fSE8_16(fGETBYTE(0, RtV))) + fMPY16SS(fGETHALF(0, VuuV.v[1].w[i]), fSE8_16(fGETBYTE(1, RtV))); + VddV.v[1].w[i] = fMPY16SS(fGETHALF(1, VuuV.v[0].w[i]), fSE8_16(fGETBYTE(2, RtV))) + fMPY16SS(fGETHALF(1, VuuV.v[1].w[i]), fSE8_16(fGETBYTE(3, RtV)))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpahb_acc,"Vxx32+=vmpahb(Vuu32,Rt32)","Vxx32.w+=vmpa(Vuu32.h,Rt32.b)", +"Vertical Byte Multiply", + VxxV.v[0].w[i] += fMPY16SS(fGETHALF(0, VuuV.v[0].w[i]), fSE8_16(fGETBYTE(0, RtV))) + fMPY16SS(fGETHALF(0, VuuV.v[1].w[i]), fSE8_16(fGETBYTE(1, RtV))); + VxxV.v[1].w[i] += fMPY16SS(fGETHALF(1, VuuV.v[0].w[i]), fSE8_16(fGETBYTE(2, RtV))) + fMPY16SS(fGETHALF(1, VuuV.v[1].w[i]), fSE8_16(fGETBYTE(3, RtV)))) + +/* Half by Byte */ +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpauhb,"Vdd32=vmpauhb(Vuu32,Rt32)","Vdd32.w=vmpa(Vuu32.uh,Rt32.b)", +"Vertical Byte Multiply", + VddV.v[0].w[i] = fMPY16US(fGETUHALF(0, VuuV.v[0].w[i]), fSE8_16(fGETBYTE(0, RtV))) + fMPY16US(fGETUHALF(0, VuuV.v[1].w[i]), fSE8_16(fGETBYTE(1, RtV))); + VddV.v[1].w[i] = fMPY16US(fGETUHALF(1, VuuV.v[0].w[i]), fSE8_16(fGETBYTE(2, RtV))) + fMPY16US(fGETUHALF(1, VuuV.v[1].w[i]), fSE8_16(fGETBYTE(3, RtV)))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpauhb_acc,"Vxx32+=vmpauhb(Vuu32,Rt32)","Vxx32.w+=vmpa(Vuu32.uh,Rt32.b)", +"Vertical Byte Multiply", + VxxV.v[0].w[i] += fMPY16US(fGETUHALF(0, VuuV.v[0].w[i]), fSE8_16(fGETBYTE(0, RtV))) + fMPY16US(fGETUHALF(0, VuuV.v[1].w[i]), fSE8_16(fGETBYTE(1, RtV))); + VxxV.v[1].w[i] += fMPY16US(fGETUHALF(1, VuuV.v[0].w[i]), fSE8_16(fGETBYTE(2, RtV))) + fMPY16US(fGETUHALF(1, VuuV.v[1].w[i]), fSE8_16(fGETBYTE(3, RtV)))) + + + + + + + +/* Half by Half */ +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyh,"Vdd32=vmpyh(Vu32,Rt32)","Vdd32.w=vmpy(Vu32.h,Rt32.h)", +"Vector absolute value of words", + VddV.v[0].w[i] = fMPY16SS(fGETHALF(0, VuV.w[i]), fGETHALF(0, RtV)); + VddV.v[1].w[i] = fMPY16SS(fGETHALF(1, VuV.w[i]), fGETHALF(1, RtV))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC_NOV1(32,vmpyh_acc,"Vxx32+=vmpyh(Vu32,Rt32)","Vxx32.w+=vmpy(Vu32.h,Rt32.h)", +"Vector even halfwords with scalar lower halfword multiply with shift and sat32", + VxxV.v[0].w[i] = fCAST8s(VxxV.v[0].w[i]) + fMPY16SS(fGETHALF(0, VuV.w[i]), fGETHALF(0, RtV)); + VxxV.v[1].w[i] = fCAST8s(VxxV.v[1].w[i]) + fMPY16SS(fGETHALF(1, VuV.w[i]), fGETHALF(1, RtV))) + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyhsat_acc,"Vxx32+=vmpyh(Vu32,Rt32):sat","Vxx32.w+=vmpy(Vu32.h,Rt32.h):sat", +"Vector even halfwords with scalar lower halfword multiply with shift and sat32", + VxxV.v[0].w[i] = fVSATW(fCAST8s(VxxV.v[0].w[i]) + fMPY16SS(fGETHALF(0, VuV.w[i]), fGETHALF(0, RtV))); + VxxV.v[1].w[i] = fVSATW(fCAST8s(VxxV.v[1].w[i]) + fMPY16SS(fGETHALF(1, VuV.w[i]), fGETHALF(1, RtV)))) + + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyhss,"Vd32=vmpyh(Vu32,Rt32):<<1:sat","Vd32.h=vmpy(Vu32.h,Rt32.h):<<1:sat", +"Vector halfword by halfword multiply, shift by 1, and take upper 16 msb", + fSETHALF(0,VdV.w[i],fVSATH(fGETHALF(1,fVSAT((fMPY16SS(fGETHALF(0,VuV.w[i]),fGETHALF(0,RtV))<<1))))); + fSETHALF(1,VdV.w[i],fVSATH(fGETHALF(1,fVSAT((fMPY16SS(fGETHALF(1,VuV.w[i]),fGETHALF(1,RtV))<<1))))); +) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyhsrs,"Vd32=vmpyh(Vu32,Rt32):<<1:rnd:sat","Vd32.h=vmpy(Vu32.h,Rt32.h):<<1:rnd:sat", +"Vector halfword with scalar halfword multiply with round, shift, and sat16", + fSETHALF(0,VdV.w[i],fVSATH(fGETHALF(1,fVSAT(fROUND((fMPY16SS(fGETHALF(0,VuV.w[i]),fGETHALF(0,RtV))<<1)))))); + fSETHALF(1,VdV.w[i],fVSATH(fGETHALF(1,fVSAT(fROUND((fMPY16SS(fGETHALF(1,VuV.w[i]),fGETHALF(1,RtV))<<1)))))); +) + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyuh,"Vdd32=vmpyuh(Vu32,Rt32)","Vdd32.uw=vmpy(Vu32.uh,Rt32.uh)", +"Vector even halfword unsigned multiply by scalar", + VddV.v[0].uw[i] = fMPY16UU(fGETUHALF(0, VuV.uw[i]),fGETUHALF(0,RtV)); + VddV.v[1].uw[i] = fMPY16UU(fGETUHALF(1, VuV.uw[i]),fGETUHALF(1,RtV))) + + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyuh_acc,"Vxx32+=vmpyuh(Vu32,Rt32)","Vxx32.uw+=vmpy(Vu32.uh,Rt32.uh)", +"Vector even halfword unsigned multiply by scalar", + VxxV.v[0].uw[i] += fMPY16UU(fGETUHALF(0, VuV.uw[i]),fGETUHALF(0,RtV)); + VxxV.v[1].uw[i] += fMPY16UU(fGETUHALF(1, VuV.uw[i]),fGETUHALF(1,RtV))) + + + + +/******************************************** +* HALF BY BYTE +********************************************/ +ITERATOR_INSN2_MPY_SLOT(16,vmpyihb,"Vd32=vmpyihb(Vu32,Rt32)","Vd32.h=vmpyi(Vu32.h,Rt32.b)", +"Vector word by byte multiply, keep lower result", +VdV.h[i] = fMPY16SS(VuV.h[i], fGETBYTE(i % 4, RtV) )) + +ITERATOR_INSN2_MPY_SLOT(16,vmpyihb_acc,"Vx32+=vmpyihb(Vu32,Rt32)","Vx32.h+=vmpyi(Vu32.h,Rt32.b)", +"Vector word by byte multiply, keep lower result", +VxV.h[i] += fMPY16SS(VuV.h[i], fGETBYTE(i % 4, RtV) )) + + +/******************************************** +* WORD BY BYTE +********************************************/ +ITERATOR_INSN2_MPY_SLOT(32,vmpyiwb,"Vd32=vmpyiwb(Vu32,Rt32)","Vd32.w=vmpyi(Vu32.w,Rt32.b)", +"Vector word by byte multiply, keep lower result", +VdV.w[i] = fMPY32SS(VuV.w[i], fGETBYTE(i % 4, RtV) )) + +ITERATOR_INSN2_MPY_SLOT(32,vmpyiwb_acc,"Vx32+=vmpyiwb(Vu32,Rt32)","Vx32.w+=vmpyi(Vu32.w,Rt32.b)", +"Vector word by byte multiply, keep lower result", +VxV.w[i] += fMPY32SS(VuV.w[i], fGETBYTE(i % 4, RtV) )) + +ITERATOR_INSN2_MPY_SLOT(32,vmpyiwub,"Vd32=vmpyiwub(Vu32,Rt32)","Vd32.w=vmpyi(Vu32.w,Rt32.ub)", +"Vector word by byte multiply, keep lower result", +VdV.w[i] = fMPY32SS(VuV.w[i], fGETUBYTE(i % 4, RtV) )) + +ITERATOR_INSN2_MPY_SLOT(32,vmpyiwub_acc,"Vx32+=vmpyiwub(Vu32,Rt32)","Vx32.w+=vmpyi(Vu32.w,Rt32.ub)", +"Vector word by byte multiply, keep lower result", +VxV.w[i] += fMPY32SS(VuV.w[i], fGETUBYTE(i % 4, RtV) )) + + +/******************************************** +* WORD BY HALF +********************************************/ +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyiwh,"Vd32=vmpyiwh(Vu32,Rt32)","Vd32.w=vmpyi(Vu32.w,Rt32.h)", +"Vector word by byte multiply, keep lower result", +VdV.w[i] = fMPY32SS(VuV.w[i], fGETHALF(i % 2, RtV))) + +ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyiwh_acc,"Vx32+=vmpyiwh(Vu32,Rt32)","Vx32.w+=vmpyi(Vu32.w,Rt32.h)", +"Vector word by byte multiply, keep lower result", +VxV.w[i] += fMPY32SS(VuV.w[i], fGETHALF(i % 2, RtV))) + + + + + + + + + + + + + + + + + + + +/************************************************************************** + * MMVECTOR LOGICAL OPERATIONS + * ************************************************************************/ +ITERATOR_INSN_ANY_SLOT(16,vand,"Vd32=vand(Vu32,Vv32)", "Vector Logical And", VdV.uh[i] = VuV.uh[i] & VvV.h[i]) +ITERATOR_INSN_ANY_SLOT(16,vor, "Vd32=vor(Vu32,Vv32)", "Vector Logical Or", VdV.uh[i] = VuV.uh[i] | VvV.h[i]) +ITERATOR_INSN_ANY_SLOT(16,vxor,"Vd32=vxor(Vu32,Vv32)", "Vector Logical XOR", VdV.uh[i] = VuV.uh[i] ^ VvV.h[i]) +ITERATOR_INSN_ANY_SLOT(16,vnot,"Vd32=vnot(Vu32)", "Vector Logical NOT", VdV.uh[i] = ~VuV.uh[i]) + + + + + +ITERATOR_INSN2_MPY_SLOT_LATE(8, vandqrt, +"Vd32.ub=vand(Qu4.ub,Rt32.ub)", "Vd32=vand(Qu4,Rt32)", "Insert Predicate into Vector", + VdV.ub[i] = fGETQBIT(QuV,i) ? fGETUBYTE(i % 4, RtV) : 0) + +ITERATOR_INSN2_MPY_SLOT_LATE(8, vandqrt_acc, +"Vx32.ub|=vand(Qu4.ub,Rt32.ub)", "Vx32|=vand(Qu4,Rt32)", "Insert Predicate into Vector", + VxV.ub[i] |= (fGETQBIT(QuV,i)) ? fGETUBYTE(i % 4, RtV) : 0) + +ITERATOR_INSN2_MPY_SLOT_LATE(8, vandnqrt, +"Vd32.ub=vand(!Qu4.ub,Rt32.ub)", "Vd32=vand(!Qu4,Rt32)", "Insert Predicate into Vector", + VdV.ub[i] = !fGETQBIT(QuV,i) ? fGETUBYTE(i % 4, RtV) : 0) + +ITERATOR_INSN2_MPY_SLOT_LATE(8, vandnqrt_acc, +"Vx32.ub|=vand(!Qu4.ub,Rt32.ub)", "Vx32|=vand(!Qu4,Rt32)", "Insert Predicate into Vector", + VxV.ub[i] |= !(fGETQBIT(QuV,i)) ? fGETUBYTE(i % 4, RtV) : 0) + + +ITERATOR_INSN2_MPY_SLOT_LATE(8, vandvrt, +"Qd4.ub=vand(Vu32.ub,Rt32.ub)", "Qd4=vand(Vu32,Rt32)", "Insert into Predicate", + fSETQBIT(QdV,i,((VuV.ub[i] & fGETUBYTE(i % 4, RtV)) != 0) ? 1 : 0)) + +ITERATOR_INSN2_MPY_SLOT_LATE(8, vandvrt_acc, +"Qx4.ub|=vand(Vu32.ub,Rt32.ub)", "Qx4|=vand(Vu32,Rt32)", "Insert into Predicate ", + fSETQBIT(QxV,i,fGETQBIT(QxV,i)|(((VuV.ub[i] & fGETUBYTE(i % 4, RtV)) != 0) ? 1 : 0))) + +ITERATOR_INSN_ANY_SLOT(8,vandvqv,"Vd32=vand(Qv4,Vu32)","Mask off bytes", +VdV.b[i] = fGETQBIT(QvV,i) ? VuV.b[i] : 0) +ITERATOR_INSN_ANY_SLOT(8,vandvnqv,"Vd32=vand(!Qv4,Vu32)","Mask off bytes", +VdV.b[i] = !fGETQBIT(QvV,i) ? VuV.b[i] : 0) + + + /*************************************************** + * Compare Vector with Vector + ***************************************************/ +#define VCMP(DEST, ASRC, ASRCOP, CMP, N, SRC, MASK, WIDTH) \ +{ \ + for(fHIDE(int) i = 0; i < fVBYTES(); i += WIDTH) { \ + fSETQBITS(DEST,WIDTH,MASK,i,ASRC ASRCOP ((VuV.SRC[i/WIDTH] CMP VvV.SRC[i/WIDTH]) ? MASK : 0)); \ + } \ + } + + +#define MMVEC_CMPGT(TYPE,TYPE2,TYPE3,DESCR,N,MASK,WIDTH,SRC) \ +EXTINSN(V6_vgt##TYPE, "Qd4=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), DESCR" greater than", \ + VCMP(QdV, , , >, N, SRC, MASK, WIDTH)) \ +EXTINSN(V6_vgt##TYPE##_and, "Qx4&=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), DESCR" greater than with predicate-and", \ + VCMP(QxV, fGETQBITS(QxV,WIDTH,MASK,i), &, >, N, SRC, MASK, WIDTH)) \ +EXTINSN(V6_vgt##TYPE##_or, "Qx4|=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), DESCR" greater than with predicate-or", \ + VCMP(QxV, fGETQBITS(QxV,WIDTH,MASK,i), |, >, N, SRC, MASK, WIDTH)) \ +EXTINSN(V6_vgt##TYPE##_xor, "Qx4^=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), DESCR" greater than with predicate-xor", \ + VCMP(QxV, fGETQBITS(QxV,WIDTH,MASK,i), ^, >, N, SRC, MASK, WIDTH)) + +#define MMVEC_CMP(TYPE,TYPE2,TYPE3,DESCR,N,MASK, WIDTH, SRC)\ +MMVEC_CMPGT(TYPE,TYPE2,TYPE3,DESCR,N,MASK,WIDTH,SRC) \ +EXTINSN(V6_veq##TYPE, "Qd4=vcmp.eq(Vu32." TYPE2 ",Vv32." TYPE2 ")", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), DESCR" equal to", \ + VCMP(QdV, , , ==, N, SRC, MASK, WIDTH)) \ +EXTINSN(V6_veq##TYPE##_and, "Qx4&=vcmp.eq(Vu32." TYPE2 ",Vv32." TYPE2 ")", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), DESCR" equalto with predicate-and", \ + VCMP(QxV, fGETQBITS(QxV,WIDTH,MASK,i), &, ==, N, SRC, MASK, WIDTH)) \ +EXTINSN(V6_veq##TYPE##_or, "Qx4|=vcmp.eq(Vu32." TYPE2 ",Vv32." TYPE2 ")", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), DESCR" equalto with predicate-or", \ + VCMP(QxV, fGETQBITS(QxV,WIDTH,MASK,i), |, ==, N, SRC, MASK, WIDTH)) \ +EXTINSN(V6_veq##TYPE##_xor, "Qx4^=vcmp.eq(Vu32." TYPE2 ",Vv32." TYPE2 ")", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), DESCR" equalto with predicate-xor", \ + VCMP(QxV, fGETQBITS(QxV,WIDTH,MASK,i), ^, ==, N, SRC, MASK, WIDTH)) + + +MMVEC_CMP(w,"w","","Vector Word Compare ", fVELEM(32), 0xF, 4, w) +MMVEC_CMP(h,"h","","Vector Half Compare ", fVELEM(16), 0x3, 2, h) +MMVEC_CMP(b,"b","","Vector Half Compare ", fVELEM(8), 0x1, 1, b) +MMVEC_CMPGT(uw,"uw","","Vector Unsigned Half Compare ", fVELEM(32), 0xF, 4,uw) +MMVEC_CMPGT(uh,"uh","","Vector Unsigned Half Compare ", fVELEM(16), 0x3, 2,uh) +MMVEC_CMPGT(ub,"ub","","Vector Unsigned Byte Compare ", fVELEM(8), 0x1, 1,ub) + +/*************************************************** +* Predicate Operations +***************************************************/ + +EXTINSN(V6_pred_scalar2, "Qd4=vsetq(Rt32)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP), "Set Vector Predicate ", +{ + fHIDE(int i;) + for(i = 0; i < fVBYTES(); i++) fSETQBIT(QdV,i,(i < (RtV & (fVBYTES()-1))) ? 1 : 0); +}) + +EXTINSN(V6_pred_scalar2v2, "Qd4=vsetq2(Rt32)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP), "Set Vector Predicate ", +{ + fHIDE(int i;) + for(i = 0; i < fVBYTES(); i++) fSETQBIT(QdV,i,(i <= ((RtV-1) & (fVBYTES()-1))) ? 1 : 0); +}) + + +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8, shuffeqw, "Qd4.h=vshuffe(Qs4.w,Qt4.w)","Shrink Predicate", fSETQBIT(QdV,i, (i & 2) ? fGETQBIT(QsV,i-2) : fGETQBIT(QtV,i) ) ) +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8, shuffeqh, "Qd4.b=vshuffe(Qs4.h,Qt4.h)","Shrink Predicate", fSETQBIT(QdV,i, (i & 1) ? fGETQBIT(QsV,i-1) : fGETQBIT(QtV,i) ) ) +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8, pred_or, "Qd4=or(Qs4,Qt4)","Vector Predicate Or", fSETQBIT(QdV,i,fGETQBIT(QsV,i) || fGETQBIT(QtV,i) ) ) +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8, pred_and, "Qd4=and(Qs4,Qt4)","Vector Predicate And", fSETQBIT(QdV,i,fGETQBIT(QsV,i) && fGETQBIT(QtV,i) ) ) +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8, pred_xor, "Qd4=xor(Qs4,Qt4)","Vector Predicate Xor", fSETQBIT(QdV,i,fGETQBIT(QsV,i) ^ fGETQBIT(QtV,i) ) ) +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8, pred_or_n, "Qd4=or(Qs4,!Qt4)","Vector Predicate Or with not", fSETQBIT(QdV,i,fGETQBIT(QsV,i) || !fGETQBIT(QtV,i) ) ) +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8, pred_and_n, "Qd4=and(Qs4,!Qt4)","Vector Predicate And with not", fSETQBIT(QdV,i,fGETQBIT(QsV,i) && !fGETQBIT(QtV,i) ) ) +ITERATOR_INSN_ANY_SLOT(8, pred_not, "Qd4=not(Qs4)","Vector Predicate Not", fSETQBIT(QdV,i,!fGETQBIT(QsV,i) ) ) + + + +EXTINSN(V6_vcmov, "if (Ps4) Vd32=Vu32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), "Conditional Mov", +{ +if (fLSBOLD(PsV)) { + fHIDE(int i;) + fVFOREACH(8, i) { + VdV.ub[i] = VuV.ub[i]; + } + } else {CANCEL;} +}) + +EXTINSN(V6_vncmov, "if (!Ps4) Vd32=Vu32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), "Conditional Mov", +{ +if (fLSBOLDNOT(PsV)) { + fHIDE(int i;) + fVFOREACH(8, i) { + VdV.ub[i] = VuV.ub[i]; + } + } else {CANCEL;} +}) + +EXTINSN(V6_vccombine, "if (Ps4) Vdd32=vcombine(Vu32,Vv32)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA_DV), "Conditional Combine", +{ +if (fLSBOLD(PsV)) { + fHIDE(int i;) + fVFOREACH(8, i) { + VddV.v[0].ub[i] = VvV.ub[i]; + VddV.v[1].ub[i] = VuV.ub[i]; + } + } else {CANCEL;} +}) + +EXTINSN(V6_vnccombine, "if (!Ps4) Vdd32=vcombine(Vu32,Vv32)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA_DV), "Conditional Combine", +{ +if (fLSBOLDNOT(PsV)) { + fHIDE(int i;) + fVFOREACH(8, i) { + VddV.v[0].ub[i] = VvV.ub[i]; + VddV.v[1].ub[i] = VuV.ub[i]; + } + } else {CANCEL;} +}) + + + +ITERATOR_INSN_ANY_SLOT(8,vmux,"Vd32=vmux(Qt4,Vu32,Vv32)", +"Vector Select Element 8-bit", + VdV.ub[i] = fGETQBIT(QtV,i) ? VuV.ub[i] : VvV.ub[i]) + +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8,vswap,"Vdd32=vswap(Qt4,Vu32,Vv32)", +"Vector Swap Element 8-bit", + VddV.v[0].ub[i] = fGETQBIT(QtV,i) ? VuV.ub[i] : VvV.ub[i]; + VddV.v[1].ub[i] = !fGETQBIT(QtV,i) ? VuV.ub[i] : VvV.ub[i]) + + +/*************************************************************************** +* +* MMVECTOR SORTING +* +****************************************************************************/ + +#define MMVEC_SORT(TYPE,TYPE2,DESCR,ELEMENTSIZE,SRC)\ +ITERATOR_INSN2_ANY_SLOT(ELEMENTSIZE,vmax##TYPE, "Vd32=vmax" TYPE2 "(Vu32,Vv32)", "Vd32."#SRC"=vmax(Vu32."#SRC",Vv32."#SRC")", "Vector " DESCR " max", VdV.SRC[i] = (VuV.SRC[i] > VvV.SRC[i]) ? VuV.SRC[i] : VvV.SRC[i]) \ +ITERATOR_INSN2_ANY_SLOT(ELEMENTSIZE,vmin##TYPE, "Vd32=vmin" TYPE2 "(Vu32,Vv32)", "Vd32."#SRC"=vmin(Vu32."#SRC",Vv32."#SRC")", "Vector " DESCR " min", VdV.SRC[i] = (VuV.SRC[i] < VvV.SRC[i]) ? VuV.SRC[i] : VvV.SRC[i]) + +MMVEC_SORT(b,"b", "signed byte", 8, b) +MMVEC_SORT(ub,"ub", "unsigned byte", 8, ub) +MMVEC_SORT(uh,"uh", "unsigned halfword",16, uh) +MMVEC_SORT(h, "h", "halfword", 16, h) +MMVEC_SORT(w, "w", "word", 32, w) + + + + + + + + + +/************************************************************* +* SHUFFLES +****************************************************************/ + +ITERATOR_INSN2_ANY_SLOT(16,vsathub,"Vd32=vsathub(Vu32,Vv32)","Vd32.ub=vsat(Vu32.h,Vv32.h)", +"Saturate and pack 32 halfwords to 32 unsigned bytes, and interleave them", + fSETBYTE(0, VdV.uh[i], fVSATUB(VvV.h[i])); + fSETBYTE(1, VdV.uh[i], fVSATUB(VuV.h[i]))) + +ITERATOR_INSN2_ANY_SLOT(32,vsatwh,"Vd32=vsatwh(Vu32,Vv32)","Vd32.h=vsat(Vu32.w,Vv32.w)", +"Saturate and pack 16 words to 16 halfwords, and interleave them", + fSETHALF(0, VdV.w[i], fVSATH(VvV.w[i])); + fSETHALF(1, VdV.w[i], fVSATH(VuV.w[i]))) + +ITERATOR_INSN2_ANY_SLOT(32,vsatuwuh,"Vd32=vsatuwuh(Vu32,Vv32)","Vd32.uh=vsat(Vu32.uw,Vv32.uw)", +"Saturate and pack 16 words to 16 halfwords, and interleave them", + fSETHALF(0, VdV.w[i], fVSATUH(VvV.uw[i])); + fSETHALF(1, VdV.w[i], fVSATUH(VuV.uw[i]))) + +ITERATOR_INSN2_ANY_SLOT(16,vshuffeb,"Vd32=vshuffeb(Vu32,Vv32)","Vd32.b=vshuffe(Vu32.b,Vv32.b)", +"Shuffle half words with in a lane", + fSETBYTE(0, VdV.uh[i], fGETUBYTE(0, VvV.uh[i])); + fSETBYTE(1, VdV.uh[i], fGETUBYTE(0, VuV.uh[i]))) + +ITERATOR_INSN2_ANY_SLOT(16,vshuffob,"Vd32=vshuffob(Vu32,Vv32)","Vd32.b=vshuffo(Vu32.b,Vv32.b)", +"Shuffle half words with in a lane", + fSETBYTE(0, VdV.uh[i], fGETUBYTE(1, VvV.uh[i])); + fSETBYTE(1, VdV.uh[i], fGETUBYTE(1, VuV.uh[i]))) + +ITERATOR_INSN2_ANY_SLOT(32,vshufeh,"Vd32=vshuffeh(Vu32,Vv32)","Vd32.h=vshuffe(Vu32.h,Vv32.h)", +"Shuffle half words with in a lane", + fSETHALF(0, VdV.uw[i], fGETUHALF(0, VvV.uw[i])); + fSETHALF(1, VdV.uw[i], fGETUHALF(0, VuV.uw[i]))) + +ITERATOR_INSN2_ANY_SLOT(32,vshufoh,"Vd32=vshuffoh(Vu32,Vv32)","Vd32.h=vshuffo(Vu32.h,Vv32.h)", +"Shuffle half words with in a lane", + fSETHALF(0, VdV.uw[i], fGETUHALF(1, VvV.uw[i])); + fSETHALF(1, VdV.uw[i], fGETUHALF(1, VuV.uw[i]))) + + + + +/************************************************************************** +* Double Vector Shuffles +**************************************************************************/ + +EXTINSN(V6_vshuff, "vshuff(Vy32,Vx32,Rt32)", +ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP_VS), +"2x2->2x2 transpose, for multiple data sizes, inplace", +{ + fHIDE(int offset;) + for (offset=1; offset2x2 transpose for multiple data sizes", +{ + fHIDE(int offset;) + VddV.v[0] = VvV; + VddV.v[1] = VuV; + for (offset=1; offset>1; offset>0; offset>>=1) { + if ( RtV & offset) { + fHIDE(int k;) \ + fVFOREACH(8, k) {\ + if (!( k & offset)) { + fSWAPB(VyV.ub[k], VxV.ub[k+offset]); + } + } + } + } + }) + +EXTINSN(V6_vdealvdd, "Vdd32=vdeal(Vu32,Vv32,Rt8)", +ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP_VS), +" vector - vector deal - or deinterleave, for multiple data sizes", +{ + fHIDE(int offset;) + VddV.v[0] = VvV; + VddV.v[1] = VuV; + for (offset=fVBYTES()>>1; offset>0; offset>>=1) { + if ( RtV & offset) { + fHIDE(int k;) \ + fVFOREACH(8, k) {\ + if (!( k & offset)) { + fSWAPB(VddV.v[1].ub[k], VddV.v[0].ub[k+offset]); + } + } + } + } + }) + +/**************************************************************************/ + + + +ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(32,vshufoeh,"Vdd32=vshuffoeh(Vu32,Vv32)","Vdd32.h=vshuffoe(Vu32.h,Vv32.h)", +"Vector Shuffle half words", + fSETHALF(0, VddV.v[0].uw[i], fGETUHALF(0, VvV.uw[i])); + fSETHALF(1, VddV.v[0].uw[i], fGETUHALF(0, VuV.uw[i])); + fSETHALF(0, VddV.v[1].uw[i], fGETUHALF(1, VvV.uw[i])); + fSETHALF(1, VddV.v[1].uw[i], fGETUHALF(1, VuV.uw[i]))) + +ITERATOR_INSN2_ANY_SLOT_DOUBLE_VEC(16,vshufoeb,"Vdd32=vshuffoeb(Vu32,Vv32)","Vdd32.b=vshuffoe(Vu32.b,Vv32.b)", +"Vector Shuffle bytes", + fSETBYTE(0, VddV.v[0].uh[i], fGETUBYTE(0, VvV.uh[i])); + fSETBYTE(1, VddV.v[0].uh[i], fGETUBYTE(0, VuV.uh[i])); + fSETBYTE(0, VddV.v[1].uh[i], fGETUBYTE(1, VvV.uh[i])); + fSETBYTE(1, VddV.v[1].uh[i], fGETUBYTE(1, VuV.uh[i]))) + + +/*************************************************************** +* Deal +***************************************************************/ + +ITERATOR_INSN2_PERMUTE_SLOT(32, vdealh, "Vd32=vdealh(Vu32)", "Vd32.h=vdeal(Vu32.h)", +"Deal Halfwords", + VdV.uh[i ] = fGETUHALF(0, VuV.uw[i]); + VdV.uh[i+fVELEM(32)] = fGETUHALF(1, VuV.uw[i])) + +ITERATOR_INSN2_PERMUTE_SLOT(16, vdealb, "Vd32=vdealb(Vu32)", "Vd32.b=vdeal(Vu32.b)", +"Deal Halfwords", + VdV.ub[i ] = fGETUBYTE(0, VuV.uh[i]); + VdV.ub[i+fVELEM(16)] = fGETUBYTE(1, VuV.uh[i])) + +ITERATOR_INSN2_PERMUTE_SLOT(32, vdealb4w, "Vd32=vdealb4w(Vu32,Vv32)", "Vd32.b=vdeale(Vu32.b,Vv32.b)", +"Deal Two Vectors Bytes", + VdV.ub[0+i ] = fGETUBYTE(0, VvV.uw[i]); + VdV.ub[fVELEM(32)+i ] = fGETUBYTE(2, VvV.uw[i]); + VdV.ub[2*fVELEM(32)+i] = fGETUBYTE(0, VuV.uw[i]); + VdV.ub[3*fVELEM(32)+i] = fGETUBYTE(2, VuV.uw[i])) + +/*************************************************************** +* shuffle +***************************************************************/ + +ITERATOR_INSN2_PERMUTE_SLOT(32, vshuffh, "Vd32=vshuffh(Vu32)", "Vd32.h=vshuff(Vu32.h)", +"Deal Halfwords", + fSETHALF(0, VdV.uw[i], VuV.uh[i]); + fSETHALF(1, VdV.uw[i], VuV.uh[i+fVELEM(32)])) + +ITERATOR_INSN2_PERMUTE_SLOT(16, vshuffb, "Vd32=vshuffb(Vu32)", "Vd32.b=vshuff(Vu32.b)", +"Deal Halfwords", + fSETBYTE(0, VdV.uh[i], VuV.ub[i]); + fSETBYTE(1, VdV.uh[i], VuV.ub[i+fVELEM(16)])) + + + + + +/*********************************************************** +* INSERT AND EXTRACT +*********************************************************/ +EXTINSN(V6_extractw, "Rd32=vextract(Vu32,Rs32)", +ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_MEMLIKE,A_RESTRICT_SLOT0ONLY), +"Extract an element from a vector to scalar", +fHIDE(warn("RdN=%d VuN=%d RsN=%d RsV=0x%08x widx=%d",RdN,VuN,RsN,RsV,((RsV & (fVBYTES()-1)) >> 2));) +RdV = VuV.uw[ (RsV & (fVBYTES()-1)) >> 2]; +fHIDE(warn("RdV=0x%08x",RdV);)) + +EXTINSN(V6_vinsertwr, "Vx32.w=vinsert(Rt32)", +ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX), +"Insert Word Scalar into Vector", +VxV.uw[0] = RtV;) + + + + +ITERATOR_INSN_MPY_SLOT_LATE(32,lvsplatw, "Vd32=vsplat(Rt32)", "Replicates scalar accross words in vector", VdV.uw[i] = RtV) + +ITERATOR_INSN_MPY_SLOT_LATE(16,lvsplath, "Vd32.h=vsplat(Rt32)", "Replicates scalar accross halves in vector", VdV.uh[i] = RtV) + +ITERATOR_INSN_MPY_SLOT_LATE(8,lvsplatb, "Vd32.b=vsplat(Rt32)", "Replicates scalar accross bytes in vector", VdV.ub[i] = RtV) + + +ITERATOR_INSN_ANY_SLOT(32,vassign,"Vd32=Vu32","Copy a vector",VdV.w[i]=VuV.w[i]) + + +ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8,vcombine,"Vdd32=vcombine(Vu32,Vv32)", +"Vector assign, Any two to Vector Pair", + VddV.v[0].ub[i] = VvV.ub[i]; + VddV.v[1].ub[i] = VuV.ub[i]) + + + +/////////////////////////////////////////////////////////////////////////// + + +/********************************************************* +* GENERAL PERMUTE NETWORKS +*********************************************************/ + + +EXTINSN(V6_vdelta, "Vd32=vdelta(Vu32,Vv32)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP), +"Reverse Benes Butterfly network ", +{ + fHIDE(int offset;) + fHIDE(int k;) + fHIDE(mmvector_t tmp;) + tmp = VuV; + for (offset=fVBYTES(); (offset>>=1)>0; ) { + for (k = 0; k>3; \ + unsigned char element = value & 7; \ + READ_EXT_VREG(regno,tmp,0); \ + tmp.uh[(128/16)*lane+(element)]++; \ + WRITE_EXT_VREG(regno,tmp,EXT_NEW); \ + } \ + } + +#define fHISTQ(INPUTVEC,QVAL) \ + fUARCH_NOTE_PUMP_4X(); \ + fHIDE(int lane;) \ + fHIDE(mmvector_t tmp;) \ + fVFOREACH(128, lane) { \ + for (fHIDE(int )i=0; i<128/8; ++i) { \ + unsigned char value = INPUTVEC.ub[(128/8)*lane+i]; \ + unsigned char regno = value>>3; \ + unsigned char element = value & 7; \ + READ_EXT_VREG(regno,tmp,0); \ + if (fGETQBIT(QVAL,128/8*lane+i)) tmp.uh[(128/16)*lane+(element)]++; \ + WRITE_EXT_VREG(regno,tmp,EXT_NEW); \ + } \ + } + + + +EXTINSN(V6_vhist, "vhist",ATTRIBS(A_EXTENSION,A_CVI,A_CVI_4SLOT), "vhist instruction",{ fHIDE(mmvector_t inputVec;) inputVec=fTMPVDATA(); fHIST(inputVec); }) +EXTINSN(V6_vhistq, "vhist(Qv4)",ATTRIBS(A_EXTENSION,A_CVI,A_CVI_4SLOT), "vhist instruction",{ fHIDE(mmvector_t inputVec;) inputVec=fTMPVDATA(); fHISTQ(inputVec,QvV); }) + +#undef fHIST +#undef fHISTQ + + +/* **** WEIGHTED HISTOGRAM **** */ + + +#if 1 +#define WHIST(EL,MASK,BSHIFT,COND,SATF) \ + fHIDE(unsigned int) bucket = fGETUBYTE(0,input.h[i]); \ + fHIDE(unsigned int) weight = fGETUBYTE(1,input.h[i]); \ + fHIDE(unsigned int) vindex = (bucket >> 3) & 0x1F; \ + fHIDE(unsigned int) elindex = ((i>>BSHIFT) & (~MASK)) | ((bucket>>BSHIFT) & MASK); \ + fHIDE(mmvector_t tmp;) \ + READ_EXT_VREG(vindex,tmp,0); \ + COND tmp.EL[elindex] = SATF(tmp.EL[elindex] + weight); \ + WRITE_EXT_VREG(vindex,tmp,EXT_NEW); \ + fUARCH_NOTE_PUMP_2X(); + +ITERATOR_INSN_VHISTLIKE(16,vwhist256,"vwhist256","vector weighted histogram halfword counters", WHIST(uh,7,0,,)) +ITERATOR_INSN_VHISTLIKE(16,vwhist256q,"vwhist256(Qv4)","vector weighted histogram halfword counters", WHIST(uh,7,0,if (fGETQBIT(QvV,2*i)),)) +ITERATOR_INSN_VHISTLIKE(16,vwhist256_sat,"vwhist256:sat","vector weighted histogram halfword counters", WHIST(uh,7,0,,fVSATUH)) +ITERATOR_INSN_VHISTLIKE(16,vwhist256q_sat,"vwhist256(Qv4):sat","vector weighted histogram halfword counters", WHIST(uh,7,0,if (fGETQBIT(QvV,2*i)),fVSATUH)) +ITERATOR_INSN_VHISTLIKE(16,vwhist128,"vwhist128","vector weighted histogram word counters", WHIST(uw,3,1,,)) +ITERATOR_INSN_VHISTLIKE(16,vwhist128q,"vwhist128(Qv4)","vector weighted histogram word counters", WHIST(uw,3,1,if (fGETQBIT(QvV,2*i)),)) +ITERATOR_INSN_VHISTLIKE(16,vwhist128m,"vwhist128(#u1)","vector weighted histogram word counters", WHIST(uw,3,1,if ((bucket & 1) == uiV),)) +ITERATOR_INSN_VHISTLIKE(16,vwhist128qm,"vwhist128(Qv4,#u1)","vector weighted histogram word counters", WHIST(uw,3,1,if (((bucket & 1) == uiV) && fGETQBIT(QvV,2*i)),)) + + +#endif + + + +/* ****** lookup table instructions *********** */ + +/* Use low bits from idx to choose next-bigger elements from vector, then use LSB from idx to choose odd or even element */ + +ITERATOR_INSN_PERMUTE_SLOT(8,vlutvvb,"Vd32.b=vlut32(Vu32.b,Vv32.b,Rt8)","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int matchval;) fHIDE(int oddhalf;) +matchval = RtV & 0x7; +oddhalf = (RtV >> (fVECLOGSIZE()-6)) & 0x1; +idx = VuV.ub[i]; +VdV.b[i] = ((idx & 0xE0) == (matchval << 5)) ? fGETBYTE(oddhalf,VvV.h[idx % fVELEM(16)]) : 0) + + +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(8,vlutvvb_oracc,"Vx32.b|=vlut32(Vu32.b,Vv32.b,Rt8)","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int matchval;) fHIDE(int oddhalf;) +matchval = RtV & 0x7; +oddhalf = (RtV >> (fVECLOGSIZE()-6)) & 0x1; +idx = VuV.ub[i]; +VxV.b[i] |= ((idx & 0xE0) == (matchval << 5)) ? fGETBYTE(oddhalf,VvV.h[idx % fVELEM(16)]) : 0) + +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(16,vlutvwh,"Vdd32.h=vlut16(Vu32.b,Vv32.h,Rt8)","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int matchval;) fHIDE(int oddhalf;) +matchval = RtV & 0xF; +oddhalf = (RtV >> (fVECLOGSIZE()-6)) & 0x1; +idx = fGETUBYTE(0,VuV.uh[i]); +VddV.v[0].h[i] = ((idx & 0xF0) == (matchval << 4)) ? fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]) : 0; +idx = fGETUBYTE(1,VuV.uh[i]); +VddV.v[1].h[i] = ((idx & 0xF0) == (matchval << 4)) ? fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]) : 0) + +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(16,vlutvwh_oracc,"Vxx32.h|=vlut16(Vu32.b,Vv32.h,Rt8)","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int matchval;) fHIDE(int oddhalf;) +matchval = fGETUBYTE(0,RtV) & 0xF; +oddhalf = (RtV >> (fVECLOGSIZE()-6)) & 0x1; +idx = fGETUBYTE(0,VuV.uh[i]); +VxxV.v[0].h[i] |= ((idx & 0xF0) == (matchval << 4)) ? fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]) : 0; +idx = fGETUBYTE(1,VuV.uh[i]); +VxxV.v[1].h[i] |= ((idx & 0xF0) == (matchval << 4)) ? fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]) : 0) + +ITERATOR_INSN_PERMUTE_SLOT(8,vlutvvbi,"Vd32.b=vlut32(Vu32.b,Vv32.b,#u3)","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int matchval;) fHIDE(int oddhalf;) +matchval = uiV & 0x7; +oddhalf = (uiV >> (fVECLOGSIZE()-6)) & 0x1; +idx = VuV.ub[i]; +VdV.b[i] = ((idx & 0xE0) == (matchval << 5)) ? fGETBYTE(oddhalf,VvV.h[idx % fVELEM(16)]) : 0) + + +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(8,vlutvvb_oracci,"Vx32.b|=vlut32(Vu32.b,Vv32.b,#u3)","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int matchval;) fHIDE(int oddhalf;) +matchval = uiV & 0x7; +oddhalf = (uiV >> (fVECLOGSIZE()-6)) & 0x1; +idx = VuV.ub[i]; +VxV.b[i] |= ((idx & 0xE0) == (matchval << 5)) ? fGETBYTE(oddhalf,VvV.h[idx % fVELEM(16)]) : 0) + +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(16,vlutvwhi,"Vdd32.h=vlut16(Vu32.b,Vv32.h,#u3)","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int matchval;) fHIDE(int oddhalf;) +matchval = uiV & 0xF; +oddhalf = (uiV >> (fVECLOGSIZE()-6)) & 0x1; +idx = fGETUBYTE(0,VuV.uh[i]); +VddV.v[0].h[i] = ((idx & 0xF0) == (matchval << 4)) ? fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]) : 0; +idx = fGETUBYTE(1,VuV.uh[i]); +VddV.v[1].h[i] = ((idx & 0xF0) == (matchval << 4)) ? fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]) : 0) + +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(16,vlutvwh_oracci,"Vxx32.h|=vlut16(Vu32.b,Vv32.h,#u3)","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int matchval;) fHIDE(int oddhalf;) +matchval = uiV & 0xF; +oddhalf = (uiV >> (fVECLOGSIZE()-6)) & 0x1; +idx = fGETUBYTE(0,VuV.uh[i]); +VxxV.v[0].h[i] |= ((idx & 0xF0) == (matchval << 4)) ? fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]) : 0; +idx = fGETUBYTE(1,VuV.uh[i]); +VxxV.v[1].h[i] |= ((idx & 0xF0) == (matchval << 4)) ? fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]) : 0) + +ITERATOR_INSN_PERMUTE_SLOT(8,vlutvvb_nm,"Vd32.b=vlut32(Vu32.b,Vv32.b,Rt8):nomatch","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int oddhalf;) fHIDE(int matchval;) + matchval = RtV & 0x7; + oddhalf = (RtV >> (fVECLOGSIZE()-6)) & 0x1; + idx = VuV.ub[i]; + idx = (idx&0x1F) | (matchval<<5); + VdV.b[i] = fGETBYTE(oddhalf,VvV.h[idx % fVELEM(16)])) + +ITERATOR_INSN_PERMUTE_SLOT_DOUBLE_VEC(16,vlutvwh_nm,"Vdd32.h=vlut16(Vu32.b,Vv32.h,Rt8):nomatch","vector-vector table lookup", +fHIDE(unsigned int idx;) fHIDE(int oddhalf;) fHIDE(int matchval;) + matchval = RtV & 0xF; + oddhalf = (RtV >> (fVECLOGSIZE()-6)) & 0x1; + idx = fGETUBYTE(0,VuV.uh[i]); + idx = (idx&0x0F) | (matchval<<4); + VddV.v[0].h[i] = fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)]); + idx = fGETUBYTE(1,VuV.uh[i]); + idx = (idx&0x0F) | (matchval<<4); + VddV.v[1].h[i] = fGETHALF(oddhalf,VvV.w[idx % fVELEM(32)])) + + + + +/****************************************************************************** +NON LINEAR - V65 + ******************************************************************************/ + +ITERATOR_INSN_SLOT2_DOUBLE_VEC(16,vmpahhsat,"Vx32.h=vmpa(Vx32.h,Vu32.h,Rtt32.h):sat","piecewise linear approximation", + VxV.h[i]= fVSATH( ( ( fMPY16SS(VxV.h[i],VuV.h[i])<<1) + (fGETHALF(( (VuV.h[i]>>14)&0x3), RttV )<<15))>>16)) + + +ITERATOR_INSN_SLOT2_DOUBLE_VEC(16,vmpauhuhsat,"Vx32.h=vmpa(Vx32.h,Vu32.uh,Rtt32.uh):sat","piecewise linear approximation", + VxV.h[i]= fVSATH( ( fMPY16SU(VxV.h[i],VuV.uh[i]) + (fGETUHALF(((VuV.uh[i]>>14)&0x3), RttV )<<15))>>16)) + +ITERATOR_INSN_SLOT2_DOUBLE_VEC(16,vmpsuhuhsat,"Vx32.h=vmps(Vx32.h,Vu32.uh,Rtt32.uh):sat","piecewise linear approximation", + VxV.h[i]= fVSATH( ( fMPY16SU(VxV.h[i],VuV.uh[i]) - (fGETUHALF(((VuV.uh[i]>>14)&0x3), RttV )<<15))>>16)) + + +ITERATOR_INSN_SLOT2_DOUBLE_VEC(16,vlut4,"Vd32.h=vlut4(Vu32.uh,Rtt32.h)","4 entry lookup table", + VdV.h[i]= fGETHALF( ((VuV.h[i]>>14)&0x3), RttV )) + + + +/****************************************************************************** +V65 + ******************************************************************************/ + +ITERATOR_INSN_MPY_SLOT_NOV1(32,vmpyuhe,"Vd32.uw=vmpye(Vu32.uh,Rt32.uh)", +"Vector even halfword unsigned multiply by scalar", + VdV.uw[i] = fMPY16UU(fGETUHALF(0, VuV.uw[i]),fGETUHALF(0,RtV))) + + +ITERATOR_INSN_MPY_SLOT_NOV1(32,vmpyuhe_acc,"Vx32.uw+=vmpye(Vu32.uh,Rt32.uh)", +"Vector even halfword unsigned multiply by scalar", + VxV.uw[i] += fMPY16UU(fGETUHALF(0, VuV.uw[i]),fGETUHALF(0,RtV))) + + + + +EXTINSN(V6_vgathermw, "vtmp.w=vgather(Rt32,Mu2,Vv32.w).w", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_GATHER,A_CVI_VA,A_CVI_VM,A_CVI_TMP_DST,A_MEMLIKE), "Gather Words", +{ + fHIDE(int i;) + fHIDE(int element_size = 4;) + fHIDE(fGATHER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + EA = RtV+VvV.uw[i]; + fVLOG_VTCM_GATHER_WORD(EA, VvV.uw[i], i,MuV); + } + fGATHER_FINISH() +}) +EXTINSN(V6_vgathermh, "vtmp.h=vgather(Rt32,Mu2,Vv32.h).h", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_GATHER,A_CVI_VA,A_CVI_VM,A_CVI_TMP_DST,A_MEMLIKE), "Gather halfwords", +{ + fHIDE(int i;) + fHIDE(int element_size = 2;) + fHIDE(fGATHER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(16, i) { + EA = RtV+VvV.uh[i]; + fVLOG_VTCM_GATHER_HALFWORD(EA, VvV.uh[i], i,MuV); + } + fGATHER_FINISH() +}) + + + +EXTINSN(V6_vgathermhw, "vtmp.h=vgather(Rt32,Mu2,Vvv32.w).h", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_GATHER,A_CVI_VA_DV,A_CVI_VM,A_CVI_TMP_DST,A_MEMLIKE), "Gather halfwords", +{ + fHIDE(int i;) + fHIDE(int j;) + fHIDE(int element_size = 2;) + fHIDE(fGATHER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + for(j = 0; j < 2; j++) { + EA = RtV+VvvV.v[j].uw[i]; + fVLOG_VTCM_GATHER_HALFWORD_DV(EA, VvvV.v[j].uw[i], (2*i+j),i,j,MuV); + } + } + fGATHER_FINISH() +}) + + +EXTINSN(V6_vgathermwq, "if (Qs4) vtmp.w=vgather(Rt32,Mu2,Vv32.w).w", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_GATHER,A_CVI_VA,A_CVI_VM,A_CVI_TMP_DST,A_MEMLIKE), "Gather Words", +{ + fHIDE(int i;) + fHIDE(int element_size = 4;) + fHIDE(fGATHER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + EA = RtV+VvV.uw[i]; + fVLOG_VTCM_GATHER_WORDQ(EA, VvV.uw[i], i,QsV,MuV); + } + fGATHER_FINISH() +}) +EXTINSN(V6_vgathermhq, "if (Qs4) vtmp.h=vgather(Rt32,Mu2,Vv32.h).h", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_GATHER,A_CVI_VA,A_CVI_VM,A_CVI_TMP_DST,A_MEMLIKE), "Gather halfwords", +{ + fHIDE(int i;) + fHIDE(int element_size = 2;) + fHIDE(fGATHER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(16, i) { + EA = RtV+VvV.uh[i]; + fVLOG_VTCM_GATHER_HALFWORDQ(EA, VvV.uh[i], i,QsV,MuV); + } + fGATHER_FINISH() +}) + + + +EXTINSN(V6_vgathermhwq, "if (Qs4) vtmp.h=vgather(Rt32,Mu2,Vvv32.w).h", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_GATHER,A_CVI_VA_DV,A_CVI_VM,A_CVI_TMP_DST,A_MEMLIKE), "Gather halfwords", +{ + fHIDE(int i;) + fHIDE(int j;) + fHIDE(int element_size = 2;) + fHIDE(fGATHER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + for(j = 0; j < 2; j++) { + EA = RtV+VvvV.v[j].uw[i]; + fVLOG_VTCM_GATHER_HALFWORDQ_DV(EA, VvvV.v[j].uw[i], (2*i+j),i,j,QsV,MuV); + } + } + fGATHER_FINISH() +}) + + + +EXTINSN(V6_vscattermw , "vscatter(Rt32,Mu2,Vv32.w).w=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA,A_CVI_VM,A_MEMLIKE), "Scatter Words", +{ + fHIDE(int i;) + fHIDE(int element_size = 4;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + EA = RtV+VvV.uw[i]; + fVLOG_VTCM_WORD(EA, VvV.uw[i], VwV,i,MuV); + } + fSCATTER_FINISH(0) +}) + + + +EXTINSN(V6_vscattermh , "vscatter(Rt32,Mu2,Vv32.h).h=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA,A_CVI_VM,A_MEMLIKE), "Scatter halfWords", +{ + fHIDE(int i;) + fHIDE(int element_size = 2;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(16, i) { + EA = RtV+VvV.uh[i]; + fVLOG_VTCM_HALFWORD(EA,VvV.uh[i],VwV,i,MuV); + } + fSCATTER_FINISH(0) +}) + + +EXTINSN(V6_vscattermw_add, "vscatter(Rt32,Mu2,Vv32.w).w+=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA,A_CVI_VM,A_MEMLIKE), "Scatter Words-Add", +{ + fHIDE(int i;) + fHIDE(int ALIGNMENT=4;) + fHIDE(int element_size = 4;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + EA = (RtV+fVALIGN(VvV.uw[i],ALIGNMENT)); + fVLOG_VTCM_WORD_INCREMENT(EA,VvV.uw[i],VwV,i,ALIGNMENT,MuV); + } + fHIDE(fLOG_SCATTER_OP(4);) + fSCATTER_FINISH(1) +}) + +EXTINSN(V6_vscattermh_add, "vscatter(Rt32,Mu2,Vv32.h).h+=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA,A_CVI_VM,A_MEMLIKE), "Scatter halfword-Add", +{ + fHIDE(int i;) + fHIDE(int ALIGNMENT=2;) + fHIDE(int element_size = 2;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(16, i) { + EA = (RtV+fVALIGN(VvV.uh[i],ALIGNMENT)); + fVLOG_VTCM_HALFWORD_INCREMENT(EA,VvV.uh[i],VwV,i,ALIGNMENT,MuV); + } + fHIDE(fLOG_SCATTER_OP(2);) + fSCATTER_FINISH(1) +}) + + +EXTINSN(V6_vscattermwq, "if (Qs4) vscatter(Rt32,Mu2,Vv32.w).w=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA,A_CVI_VM,A_MEMLIKE), "Scatter Words conditional", +{ + fHIDE(int i;) + fHIDE(int element_size = 4;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + EA = RtV+VvV.uw[i]; + fVLOG_VTCM_WORDQ(EA,VvV.uw[i], VwV,i,QsV,MuV); + } + fSCATTER_FINISH(0) +}) + +EXTINSN(V6_vscattermhq, "if (Qs4) vscatter(Rt32,Mu2,Vv32.h).h=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA,A_CVI_VM,A_MEMLIKE), "Scatter HalfWords conditional", +{ + fHIDE(int i;) + fHIDE(int element_size = 2;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(16, i) { + EA = RtV+VvV.uh[i]; + fVLOG_VTCM_HALFWORDQ(EA,VvV.uh[i],VwV,i,QsV,MuV); + } + fSCATTER_FINISH(0) +}) + + + + +EXTINSN(V6_vscattermhw , "vscatter(Rt32,Mu2,Vvv32.w).h=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA_DV,A_CVI_VM,A_MEMLIKE), "Scatter Words", +{ + fHIDE(int i;) + fHIDE(int j;) + fHIDE(int element_size = 2;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + for(j = 0; j < 2; j++) { + EA = RtV+VvvV.v[j].uw[i]; + fVLOG_VTCM_HALFWORD_DV(EA,VvvV.v[j].uw[i],VwV,(2*i+j),i,j,MuV); + } + } + fSCATTER_FINISH(0) +}) + + + +EXTINSN(V6_vscattermhwq, "if (Qs4) vscatter(Rt32,Mu2,Vvv32.w).h=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA_DV,A_CVI_VM,A_MEMLIKE), "Scatter halfwords conditional", +{ + fHIDE(int i;) + fHIDE(int j;) + fHIDE(int element_size = 2;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + for(j = 0; j < 2; j++) { + EA = RtV+VvvV.v[j].uw[i]; + fVLOG_VTCM_HALFWORDQ_DV(EA,VvvV.v[j].uw[i],VwV,(2*i+j),QsV,i,j,MuV); + } + } + fSCATTER_FINISH(0) +}) + +EXTINSN(V6_vscattermhw_add, "vscatter(Rt32,Mu2,Vvv32.w).h+=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA_DV,A_CVI_VM,A_MEMLIKE), "Scatter halfwords-add", +{ + fHIDE(int i;) + fHIDE(int j;) + fHIDE(int ALIGNMENT=2;) + fHIDE(int element_size = 2;) + fHIDE(fSCATTER_INIT( RtV, MuV, element_size);) + fVLASTBYTE(MuV, element_size); + fVALIGN(RtV, element_size); + fVFOREACH(32, i) { + for(j = 0; j < 2; j++) { + EA = RtV + fVALIGN(VvvV.v[j].uw[i],ALIGNMENT);; + fVLOG_VTCM_HALFWORD_INCREMENT_DV(EA,VvvV.v[j].uw[i],VwV,(2*i+j),i,j,ALIGNMENT,MuV); + } + } + fHIDE(fLOG_SCATTER_OP(2);) + fSCATTER_FINISH(1) +}) + +EXTINSN(V6_vprefixqb,"Vd32.b=prefixsum(Qv4)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), "parallel prefix sum of Q into byte", +{ + fHIDE(int i;) + fHIDE(size1u_t acc = 0;) + fVFOREACH(8, i) { + acc += fGETQBIT(QvV,i); + VdV.ub[i] = acc; + } + } ) +EXTINSN(V6_vprefixqh,"Vd32.h=prefixsum(Qv4)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), "parallel prefix sum of Q into halfwords", +{ + fHIDE(int i;) + fHIDE(size2u_t acc = 0;) + fVFOREACH(16, i) { + acc += fGETQBIT(QvV,i*2+0); + acc += fGETQBIT(QvV,i*2+1); + VdV.uh[i] = acc; + } + } ) +EXTINSN(V6_vprefixqw,"Vd32.w=prefixsum(Qv4)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), "parallel prefix sum of Q into words", +{ + fHIDE(int i;) + fHIDE(size4u_t acc = 0;) + fVFOREACH(32, i) { + acc += fGETQBIT(QvV,i*4+0); + acc += fGETQBIT(QvV,i*4+1); + acc += fGETQBIT(QvV,i*4+2); + acc += fGETQBIT(QvV,i*4+3); + VdV.uw[i] = acc; + } + } ) + + + + + +/****************************************************************************** + DEBUG Vector/Register Printing + ******************************************************************************/ + +#define PRINT_VU(TYPE, TYPE2, COUNT)\ + int i; \ + size4u_t vec_len = fVBYTES();\ + fprintf(stdout,"V%2d: ",VuN); \ + for (i=0;i>COUNT;i++) { \ + fprintf(stdout,TYPE2 " ", VuV.TYPE[i]); \ + }; \ + fprintf(stdout,"\\n"); \ + fflush(stdout);\ + +#undef ATTR_VMEM +#undef ATTR_VMEMU +#undef ATTR_VMEM_NT + +#endif /* NO_MMVEC */ + +#ifdef __SELF_DEF_EXTINSN +#undef EXTINSN +#undef __SELF_DEF_EXTINSN +#endif From 60d1180b68944c3dbaad8239e60515e4c5c4b00f Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Sun, 7 Feb 2021 12:42:57 -0600 Subject: [PATCH 1184/1334] Hexagon HVX (target/hexagon) instruction decoding Add new file to target/hexagon/meson.build Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/decode.c | 24 ++- target/hexagon/meson.build | 1 + target/hexagon/mmvec/decode_ext_mmvec.c | 236 ++++++++++++++++++++++++ target/hexagon/mmvec/decode_ext_mmvec.h | 24 +++ 4 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 target/hexagon/mmvec/decode_ext_mmvec.c create mode 100644 target/hexagon/mmvec/decode_ext_mmvec.h diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index d424245598..653bfd751e 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -22,6 +22,7 @@ #include "decode.h" #include "insn.h" #include "printinsn.h" +#include "mmvec/decode_ext_mmvec.h" #define fZXTN(N, M, VAL) ((VAL) & ((1LL << (N)) - 1)) @@ -566,8 +567,12 @@ static void decode_remove_extenders(Packet *packet) static SlotMask get_valid_slots(const Packet *pkt, unsigned int slot) { - return find_iclass_slots(pkt->insn[slot].opcode, - pkt->insn[slot].iclass); + if (GET_ATTRIB(pkt->insn[slot].opcode, A_EXTENSION)) { + return mmvec_ext_decode_find_iclass_slots(pkt->insn[slot].opcode); + } else { + return find_iclass_slots(pkt->insn[slot].opcode, + pkt->insn[slot].iclass); + } } #define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) /* NOTHING */ @@ -728,6 +733,11 @@ decode_insns_tablewalk(Insn *insn, const DectreeTable *table, } decode_op(insn, opc, encoding); return 1; + } else if (table->table[i].type == DECTREE_EXTSPACE) { + /* + * For now, HVX will be the only coproc + */ + return decode_insns_tablewalk(insn, ext_trees[EXT_IDX_mmvec], encoding); } else { return 0; } @@ -874,6 +884,7 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt, int words_read = 0; bool end_of_packet = false; int new_insns = 0; + int i; uint32_t encoding32; /* Initialize */ @@ -901,6 +912,11 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt, return 0; } pkt->encod_pkt_size_in_bytes = words_read * 4; + pkt->pkt_has_hvx = false; + for (i = 0; i < num_insns; i++) { + pkt->pkt_has_hvx |= + GET_ATTRIB(pkt->insn[i].opcode, A_CVI); + } /* * Check for :endloop in the parse bits @@ -931,6 +947,10 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt, decode_set_slot_number(pkt); decode_fill_newvalue_regno(pkt); + if (pkt->pkt_has_hvx) { + mmvec_ext_decode_checks(pkt, disas_only); + } + if (!disas_only) { decode_shuffle_for_execution(pkt); decode_split_cmpjump(pkt); diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index a35eb2877e..b61243103f 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -175,6 +175,7 @@ hexagon_ss.add(files( 'printinsn.c', 'arch.c', 'fma_emu.c', + 'mmvec/decode_ext_mmvec.c', 'mmvec/system_ext_mmvec.c', )) diff --git a/target/hexagon/mmvec/decode_ext_mmvec.c b/target/hexagon/mmvec/decode_ext_mmvec.c new file mode 100644 index 0000000000..061a65ab88 --- /dev/null +++ b/target/hexagon/mmvec/decode_ext_mmvec.c @@ -0,0 +1,236 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "decode.h" +#include "opcodes.h" +#include "insn.h" +#include "iclass.h" +#include "mmvec/mmvec.h" +#include "mmvec/decode_ext_mmvec.h" + +static void +check_new_value(Packet *pkt) +{ + /* .new value for a MMVector store */ + int i, j; + const char *reginfo; + const char *destletters; + const char *dststr = NULL; + uint16_t def_opcode; + char letter; + int def_regnum; + + for (i = 1; i < pkt->num_insns; i++) { + uint16_t use_opcode = pkt->insn[i].opcode; + if (GET_ATTRIB(use_opcode, A_DOTNEWVALUE) && + GET_ATTRIB(use_opcode, A_CVI) && + GET_ATTRIB(use_opcode, A_STORE)) { + int use_regidx = strchr(opcode_reginfo[use_opcode], 's') - + opcode_reginfo[use_opcode]; + /* + * What's encoded at the N-field is the offset to who's producing + * the value. + * Shift off the LSB which indicates odd/even register. + */ + int def_off = ((pkt->insn[i].regno[use_regidx]) >> 1); + int def_oreg = pkt->insn[i].regno[use_regidx] & 1; + int def_idx = -1; + for (j = i - 1; (j >= 0) && (def_off >= 0); j--) { + if (!GET_ATTRIB(pkt->insn[j].opcode, A_CVI)) { + continue; + } + def_off--; + if (def_off == 0) { + def_idx = j; + break; + } + } + /* + * Check for a badly encoded N-field which points to an instruction + * out-of-range + */ + g_assert(!((def_off != 0) || (def_idx < 0) || + (def_idx > (pkt->num_insns - 1)))); + + /* def_idx is the index of the producer */ + def_opcode = pkt->insn[def_idx].opcode; + reginfo = opcode_reginfo[def_opcode]; + destletters = "dexy"; + for (j = 0; (letter = destletters[j]) != 0; j++) { + dststr = strchr(reginfo, letter); + if (dststr != NULL) { + break; + } + } + if ((dststr == NULL) && GET_ATTRIB(def_opcode, A_CVI_GATHER)) { + def_regnum = 0; + pkt->insn[i].regno[use_regidx] = def_oreg; + pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot; + } else { + if (dststr == NULL) { + /* still not there, we have a bad packet */ + g_assert_not_reached(); + } + def_regnum = pkt->insn[def_idx].regno[dststr - reginfo]; + /* Now patch up the consumer with the register number */ + pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg; + /* special case for (Vx,Vy) */ + dststr = strchr(reginfo, 'y'); + if (def_oreg && strchr(reginfo, 'x') && dststr) { + def_regnum = pkt->insn[def_idx].regno[dststr - reginfo]; + pkt->insn[i].regno[use_regidx] = def_regnum; + } + /* + * We need to remember who produces this value to later + * check if it was dynamically cancelled + */ + pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot; + } + } + } +} + +/* + * We don't want to reorder slot1/slot0 with respect to each other. + * So in our shuffling, we don't want to move the .cur / .tmp vmem earlier + * Instead, we should move the producing instruction later + * But the producing instruction might feed a .new store! + * So we may need to move that even later. + */ + +static void +decode_mmvec_move_cvi_to_end(Packet *pkt, int max) +{ + int i; + for (i = 0; i < max; i++) { + if (GET_ATTRIB(pkt->insn[i].opcode, A_CVI)) { + int last_inst = pkt->num_insns - 1; + uint16_t last_opcode = pkt->insn[last_inst].opcode; + + /* + * If the last instruction is an endloop, move to the one before it + * Keep endloop as the last thing always + */ + if ((last_opcode == J2_endloop0) || + (last_opcode == J2_endloop1) || + (last_opcode == J2_endloop01)) { + last_inst--; + } + + decode_send_insn_to(pkt, i, last_inst); + max--; + i--; /* Retry this index now that packet has rotated */ + } + } +} + +static void +decode_shuffle_for_execution_vops(Packet *pkt) +{ + /* + * Sort for .new + */ + int i; + for (i = 0; i < pkt->num_insns; i++) { + uint16_t opcode = pkt->insn[i].opcode; + if (GET_ATTRIB(opcode, A_LOAD) && + (GET_ATTRIB(opcode, A_CVI_NEW) || + GET_ATTRIB(opcode, A_CVI_TMP))) { + /* + * Find prior consuming vector instructions + * Move to end of packet + */ + decode_mmvec_move_cvi_to_end(pkt, i); + break; + } + } + + /* Move HVX new value stores to the end of the packet */ + for (i = 0; i < pkt->num_insns - 1; i++) { + uint16_t opcode = pkt->insn[i].opcode; + if (GET_ATTRIB(opcode, A_STORE) && + GET_ATTRIB(opcode, A_CVI_NEW) && + !GET_ATTRIB(opcode, A_CVI_SCATTER_RELEASE)) { + int last_inst = pkt->num_insns - 1; + uint16_t last_opcode = pkt->insn[last_inst].opcode; + + /* + * If the last instruction is an endloop, move to the one before it + * Keep endloop as the last thing always + */ + if ((last_opcode == J2_endloop0) || + (last_opcode == J2_endloop1) || + (last_opcode == J2_endloop01)) { + last_inst--; + } + + decode_send_insn_to(pkt, i, last_inst); + break; + } + } +} + +static void +check_for_vhist(Packet *pkt) +{ + pkt->vhist_insn = NULL; + for (int i = 0; i < pkt->num_insns; i++) { + Insn *insn = &pkt->insn[i]; + int opcode = insn->opcode; + if (GET_ATTRIB(opcode, A_CVI) && GET_ATTRIB(opcode, A_CVI_4SLOT)) { + pkt->vhist_insn = insn; + return; + } + } +} + +/* + * Public Functions + */ + +SlotMask mmvec_ext_decode_find_iclass_slots(int opcode) +{ + if (GET_ATTRIB(opcode, A_CVI_VM)) { + /* HVX memory instruction */ + if (GET_ATTRIB(opcode, A_RESTRICT_SLOT0ONLY)) { + return SLOTS_0; + } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT1ONLY)) { + return SLOTS_1; + } + return SLOTS_01; + } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT2ONLY)) { + return SLOTS_2; + } else if (GET_ATTRIB(opcode, A_CVI_VX)) { + /* HVX multiply instruction */ + return SLOTS_23; + } else if (GET_ATTRIB(opcode, A_CVI_VS_VX)) { + /* HVX permute/shift instruction */ + return SLOTS_23; + } else { + return SLOTS_0123; + } +} + +void mmvec_ext_decode_checks(Packet *pkt, bool disas_only) +{ + check_new_value(pkt); + if (!disas_only) { + decode_shuffle_for_execution_vops(pkt); + } + check_for_vhist(pkt); +} diff --git a/target/hexagon/mmvec/decode_ext_mmvec.h b/target/hexagon/mmvec/decode_ext_mmvec.h new file mode 100644 index 0000000000..3664b68cf9 --- /dev/null +++ b/target/hexagon/mmvec/decode_ext_mmvec.h @@ -0,0 +1,24 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HEXAGON_DECODE_EXT_MMVEC_H +#define HEXAGON_DECODE_EXT_MMVEC_H + +void mmvec_ext_decode_checks(Packet *pkt, bool disas_only); +SlotMask mmvec_ext_decode_find_iclass_slots(int opcode); + +#endif From 61c9aab09b0e7d9a57c2d031d7bd30dc9fe2bb41 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Thu, 23 Jan 2020 13:03:33 -0600 Subject: [PATCH 1185/1334] Hexagon HVX (target/hexagon) import instruction encodings Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- target/hexagon/decode.c | 4 + target/hexagon/imported/allextenc.def | 20 + target/hexagon/imported/encode.def | 1 + target/hexagon/imported/mmvec/encode_ext.def | 794 +++++++++++++++++++ 4 files changed, 819 insertions(+) create mode 100644 target/hexagon/imported/allextenc.def create mode 100644 target/hexagon/imported/mmvec/encode_ext.def diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 653bfd751e..6f0f27b4ba 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -47,6 +47,7 @@ enum { /* Name Num Table */ DEF_REGMAP(R_16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23) DEF_REGMAP(R__8, 8, 0, 2, 4, 6, 16, 18, 20, 22) +DEF_REGMAP(R_8, 8, 0, 1, 2, 3, 4, 5, 6, 7) #define DECODE_MAPPED_REG(OPNUM, NAME) \ insn->regno[OPNUM] = DECODE_REGISTER_##NAME[insn->regno[OPNUM]]; @@ -158,6 +159,9 @@ static void decode_ext_init(void) for (i = EXT_IDX_noext; i < EXT_IDX_noext_AFTER; i++) { ext_trees[i] = &dectree_table_DECODE_EXT_EXT_noext; } + for (i = EXT_IDX_mmvec; i < EXT_IDX_mmvec_AFTER; i++) { + ext_trees[i] = &dectree_table_DECODE_EXT_EXT_mmvec; + } } typedef struct { diff --git a/target/hexagon/imported/allextenc.def b/target/hexagon/imported/allextenc.def new file mode 100644 index 0000000000..39a3e93fad --- /dev/null +++ b/target/hexagon/imported/allextenc.def @@ -0,0 +1,20 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#define EXTNAME mmvec +#include "mmvec/encode_ext.def" +#undef EXTNAME diff --git a/target/hexagon/imported/encode.def b/target/hexagon/imported/encode.def index b9368d1284..e40e7fbffb 100644 --- a/target/hexagon/imported/encode.def +++ b/target/hexagon/imported/encode.def @@ -71,6 +71,7 @@ #include "encode_pp.def" #include "encode_subinsn.def" +#include "allextenc.def" #ifdef __SELF_DEF_FIELD32 #undef __SELF_DEF_FIELD32 diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def new file mode 100644 index 0000000000..6fbbe2c422 --- /dev/null +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -0,0 +1,794 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#define CONCAT(A,B) A##B +#define EXTEXTNAME(X) CONCAT(EXT_,X) +#define DEF_ENC(TAG,STR) DEF_EXT_ENC(TAG,EXTEXTNAME(EXTNAME),STR) + + +#ifndef NO_MMVEC +DEF_ENC(V6_extractw, ICLASS_LD" 001 0 000sssss PP0uuuuu --1ddddd") /* coproc insn, returns Rd */ +#endif + + +#ifndef NO_MMVEC + + + +DEF_CLASS32(ICLASS_NCJ" 1--- -------- PP------ --------",COPROC_VMEM) +DEF_CLASS32(ICLASS_NCJ" 1000 0-0ttttt PPi--iii ---ddddd",BaseOffset_VMEM_Loads) +DEF_CLASS32(ICLASS_NCJ" 1000 1-0ttttt PPivviii ---ddddd",BaseOffset_if_Pv_VMEM_Loads) +DEF_CLASS32(ICLASS_NCJ" 1000 0-1ttttt PPi--iii --------",BaseOffset_VMEM_Stores1) +DEF_CLASS32(ICLASS_NCJ" 1000 1-0ttttt PPi--iii 00------",BaseOffset_VMEM_Stores2) +DEF_CLASS32(ICLASS_NCJ" 1000 1-1ttttt PPivviii --------",BaseOffset_if_Pv_VMEM_Stores) + +DEF_CLASS32(ICLASS_NCJ" 1001 0-0xxxxx PP---iii ---ddddd",PostImm_VMEM_Loads) +DEF_CLASS32(ICLASS_NCJ" 1001 1-0xxxxx PP-vviii ---ddddd",PostImm_if_Pv_VMEM_Loads) +DEF_CLASS32(ICLASS_NCJ" 1001 0-1xxxxx PP---iii --------",PostImm_VMEM_Stores1) +DEF_CLASS32(ICLASS_NCJ" 1001 1-0xxxxx PP---iii 00------",PostImm_VMEM_Stores2) +DEF_CLASS32(ICLASS_NCJ" 1001 1-1xxxxx PP-vviii --------",PostImm_if_Pv_VMEM_Stores) + +DEF_CLASS32(ICLASS_NCJ" 1011 0-0xxxxx PPu----- ---ddddd",PostM_VMEM_Loads) +DEF_CLASS32(ICLASS_NCJ" 1011 1-0xxxxx PPuvv--- ---ddddd",PostM_if_Pv_VMEM_Loads) +DEF_CLASS32(ICLASS_NCJ" 1011 0-1xxxxx PPu----- --------",PostM_VMEM_Stores1) +DEF_CLASS32(ICLASS_NCJ" 1011 1-0xxxxx PPu----- 00------",PostM_VMEM_Stores2) +DEF_CLASS32(ICLASS_NCJ" 1011 1-1xxxxx PPuvv--- --------",PostM_if_Pv_VMEM_Stores) + +DEF_CLASS32(ICLASS_NCJ" 110- 0------- PP------ --------",Z_Load) +DEF_CLASS32(ICLASS_NCJ" 110- 1------- PP------ --------",Z_Load_if_Pv) + +DEF_CLASS32(ICLASS_NCJ" 1111 000ttttt PPu--0-- ---vvvvv",Gather) +DEF_CLASS32(ICLASS_NCJ" 1111 000ttttt PPu--1-- -ssvvvvv",Gather_if_Qs) +DEF_CLASS32(ICLASS_NCJ" 1111 001ttttt PPuvvvvv ---wwwww",Scatter) +DEF_CLASS32(ICLASS_NCJ" 1111 001ttttt PPuvvvvv -----sss",Scatter_New) +DEF_CLASS32(ICLASS_NCJ" 1111 1--ttttt PPuvvvvv -sswwwww",Scatter_if_Qs) + + +DEF_FIELD32(ICLASS_NCJ" 1--- -!------ PP------ --------",NT,"NonTemporal") + + + +DEF_FIELDROW_DESC32( ICLASS_NCJ" 1 000 --- ----- PP i --iii ----- ---","[#0] vmem(Rt+#s4)[:nt]") + +#define LDST_ENC(TAG,MAJ3,MID3,RREG,TINY6,MIN3,VREG) DEF_ENC(TAG, ICLASS_NCJ "1" #MAJ3 #MID3 #RREG "PP" #TINY6 #MIN3 #VREG) + +#define LDST_BO(TAGPRE,MID3,PRED,MIN3,VREG) LDST_ENC(TAGPRE##_ai, 000,MID3,ttttt,i PRED iii,MIN3,VREG) +#define LDST_PI(TAGPRE,MID3,PRED,MIN3,VREG) LDST_ENC(TAGPRE##_pi, 001,MID3,xxxxx,- PRED iii,MIN3,VREG) +#define LDST_PM(TAGPRE,MID3,PRED,MIN3,VREG) LDST_ENC(TAGPRE##_ppu,011,MID3,xxxxx,u PRED ---,MIN3,VREG) + +#define LDST_BASICLD(OP,TAGPRE) \ + OP(TAGPRE, 000,00,000,ddddd) \ + OP(TAGPRE##_nt, 010,00,000,ddddd) \ + OP(TAGPRE##_cur, 000,00,001,ddddd) \ + OP(TAGPRE##_nt_cur, 010,00,001,ddddd) \ + OP(TAGPRE##_tmp, 000,00,010,ddddd) \ + OP(TAGPRE##_nt_tmp, 010,00,010,ddddd) + +#define LDST_BASICST(OP,TAGPRE) \ + OP(TAGPRE, 001,--,000,sssss) \ + OP(TAGPRE##_nt, 011,--,000,sssss) \ + OP(TAGPRE##_new, 001,--,001,-0sss) \ + OP(TAGPRE##_srls, 001,--,001,-1---) \ + OP(TAGPRE##_nt_new, 011,--,001,--sss) \ + + +#define LDST_QPREDST(OP,TAGPRE) \ + OP(TAGPRE##_qpred, 100,vv,000,sssss) \ + OP(TAGPRE##_nt_qpred, 110,vv,000,sssss) \ + OP(TAGPRE##_nqpred, 100,vv,001,sssss) \ + OP(TAGPRE##_nt_nqpred,110,vv,001,sssss) \ + +#define LDST_CONDLD(OP,TAGPRE) \ + OP(TAGPRE##_pred, 100,vv,010,ddddd) \ + OP(TAGPRE##_nt_pred, 110,vv,010,ddddd) \ + OP(TAGPRE##_npred, 100,vv,011,ddddd) \ + OP(TAGPRE##_nt_npred, 110,vv,011,ddddd) \ + OP(TAGPRE##_cur_pred, 100,vv,100,ddddd) \ + OP(TAGPRE##_nt_cur_pred, 110,vv,100,ddddd) \ + OP(TAGPRE##_cur_npred, 100,vv,101,ddddd) \ + OP(TAGPRE##_nt_cur_npred, 110,vv,101,ddddd) \ + OP(TAGPRE##_tmp_pred, 100,vv,110,ddddd) \ + OP(TAGPRE##_nt_tmp_pred, 110,vv,110,ddddd) \ + OP(TAGPRE##_tmp_npred, 100,vv,111,ddddd) \ + OP(TAGPRE##_nt_tmp_npred, 110,vv,111,ddddd) \ + +#define LDST_PREDST(OP,TAGPRE,NT,MIN2) \ + OP(TAGPRE##_pred, 1 NT 1,vv,MIN2 0,sssss) \ + OP(TAGPRE##_npred, 1 NT 1,vv,MIN2 1,sssss) + +#define LDST_PREDSTNEW(OP,TAGPRE,NT,MIN2) \ + OP(TAGPRE##_pred, 1 NT 1,vv,MIN2 0,NT 0 sss) \ + OP(TAGPRE##_npred, 1 NT 1,vv,MIN2 1,NT 1 sss) + +// 0.0,vv,0--,sssss: pred st +#define LDST_BASICPREDST(OP,TAGPRE) \ + LDST_PREDST(OP,TAGPRE, 0,00) \ + LDST_PREDST(OP,TAGPRE##_nt, 1,00) \ + LDST_PREDSTNEW(OP,TAGPRE##_new, 0,01) \ + LDST_PREDSTNEW(OP,TAGPRE##_nt_new, 1,01) + + + +LDST_BASICLD(LDST_BO,V6_vL32b) +LDST_CONDLD(LDST_BO,V6_vL32b) +LDST_BASICLD(LDST_PI,V6_vL32b) +LDST_CONDLD(LDST_PI,V6_vL32b) +LDST_BASICLD(LDST_PM,V6_vL32b) +LDST_CONDLD(LDST_PM,V6_vL32b) + +// Loads + +LDST_BO(V6_vL32Ub,000,00,111,ddddd) +//Stores +LDST_BASICST(LDST_BO,V6_vS32b) + + +LDST_BO(V6_vS32Ub,001,--,111,sssss) + + + + +// Byte Enabled Stores +LDST_QPREDST(LDST_BO,V6_vS32b) + +// Scalar Predicated Stores +LDST_BASICPREDST(LDST_BO,V6_vS32b) + + +LDST_PREDST(LDST_BO,V6_vS32Ub,0,11) + + + + +DEF_FIELDROW_DESC32( ICLASS_NCJ" 1 001 --- ----- PP - ----- ddddd ---","[#1] vmem(Rx++#s3)[:nt]") + +// Loads +LDST_PI(V6_vL32Ub,000,00,111,ddddd) + +//Stores +LDST_BASICST(LDST_PI,V6_vS32b) + + + +LDST_PI(V6_vS32Ub,001,--,111,sssss) + + +// Byte Enabled Stores +LDST_QPREDST(LDST_PI,V6_vS32b) + + +// Scalar Predicated Stores +LDST_BASICPREDST(LDST_PI,V6_vS32b) + + +LDST_PREDST(LDST_PI,V6_vS32Ub,0,11) + + + +DEF_FIELDROW_DESC32( ICLASS_NCJ" 1 011 --- ----- PP - ----- ----- ---","[#3] vmem(Rx++#M)[:nt]") + +// Loads +LDST_PM(V6_vL32Ub,000,00,111,ddddd) + +//Stores +LDST_BASICST(LDST_PM,V6_vS32b) + + + +LDST_PM(V6_vS32Ub,001,--,111,sssss) + +// Byte Enabled Stores +LDST_QPREDST(LDST_PM,V6_vS32b) + +// Scalar Predicated Stores +LDST_BASICPREDST(LDST_PM,V6_vS32b) + + +LDST_PREDST(LDST_PM,V6_vS32Ub,0,11) + + + +DEF_ENC(V6_vaddcarrysat, ICLASS_CJ" 1 101 100 vvvvv PP 1 uuuuu 0ss ddddd") // +DEF_ENC(V6_vaddcarryo, ICLASS_CJ" 1 101 101 vvvvv PP 1 uuuuu 0ee ddddd") // +DEF_ENC(V6_vsubcarryo, ICLASS_CJ" 1 101 101 vvvvv PP 1 uuuuu 1ee ddddd") // +DEF_ENC(V6_vsatdw, ICLASS_CJ" 1 101 100 vvvvv PP 1 uuuuu 111 ddddd") // + +DEF_FIELDROW_DESC32( ICLASS_NCJ" 1 111 --- ----- PP - ----- ----- ---","[#6] vgather,vscatter") +DEF_ENC(V6_vgathermw, ICLASS_NCJ" 1 111 000 ttttt PP u --000 --- vvvvv") // vtmp.w=vmem(Rt32,Mu2,Vv32.w).w +DEF_ENC(V6_vgathermh, ICLASS_NCJ" 1 111 000 ttttt PP u --001 --- vvvvv") // vtmp.h=vmem(Rt32,Mu2,Vv32.h).h +DEF_ENC(V6_vgathermhw, ICLASS_NCJ" 1 111 000 ttttt PP u --010 --- vvvvv") // vtmp.h=vmem(Rt32,Mu2,Vvv32.w).h + + +DEF_ENC(V6_vgathermwq, ICLASS_NCJ" 1 111 000 ttttt PP u --100 -ss vvvvv") // if (Qs4) vtmp.w=vmem(Rt32,Mu2,Vv32.w).w +DEF_ENC(V6_vgathermhq, ICLASS_NCJ" 1 111 000 ttttt PP u --101 -ss vvvvv") // if (Qs4) vtmp.h=vmem(Rt32,Mu2,Vv32.h).h +DEF_ENC(V6_vgathermhwq, ICLASS_NCJ" 1 111 000 ttttt PP u --110 -ss vvvvv") // if (Qs4) vtmp.h=vmem(Rt32,Mu2,Vvv32.w).h + + + +DEF_ENC(V6_vscattermw, ICLASS_NCJ" 1 111 001 ttttt PP u vvvvv 000 wwwww") // vmem(Rt32,Mu2,Vv32.w)=Vw32.w +DEF_ENC(V6_vscattermh, ICLASS_NCJ" 1 111 001 ttttt PP u vvvvv 001 wwwww") // vmem(Rt32,Mu2,Vv32.h)=Vw32.h +DEF_ENC(V6_vscattermhw, ICLASS_NCJ" 1 111 001 ttttt PP u vvvvv 010 wwwww") // vmem(Rt32,Mu2,Vv32.h)=Vw32.h + +DEF_ENC(V6_vscattermw_add, ICLASS_NCJ" 1 111 001 ttttt PP u vvvvv 100 wwwww") // vmem(Rt32,Mu2,Vv32.w) += Vw32.w +DEF_ENC(V6_vscattermh_add, ICLASS_NCJ" 1 111 001 ttttt PP u vvvvv 101 wwwww") // vmem(Rt32,Mu2,Vv32.h) += Vw32.h +DEF_ENC(V6_vscattermhw_add, ICLASS_NCJ" 1 111 001 ttttt PP u vvvvv 110 wwwww") // vmem(Rt32,Mu2,Vv32.h) += Vw32.h + + +DEF_ENC(V6_vscattermwq, ICLASS_NCJ" 1 111 100 ttttt PP u vvvvv 0ss wwwww") // if (Qs4) vmem(Rt32,Mu2,Vv32.w)=Vw32.w +DEF_ENC(V6_vscattermhq, ICLASS_NCJ" 1 111 100 ttttt PP u vvvvv 1ss wwwww") // if (Qs4) vmem(Rt32,Mu2,Vv32.h)=Vw32.h +DEF_ENC(V6_vscattermhwq, ICLASS_NCJ" 1 111 101 ttttt PP u vvvvv 0ss wwwww") // if (Qs4) vmem(Rt32,Mu2,Vv32.h)=Vw32.h + + + + + +DEF_CLASS32(ICLASS_CJ" 1--- -------- PP------ --------",COPROC_VX) + + + +/*************************************************************** +* +* Group #0, Uses Q6 Rt8: new in v61 +* +****************************************************************/ + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 000 --- ----- PP - ----- ----- ---","[#1] Vd32=(Vu32, Vv32, Rt8)") +DEF_ENC(V6_vasrhbsat, ICLASS_CJ" 1 000 vvv vvttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vasruwuhrndsat, ICLASS_CJ" 1 000 vvv vvttt PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vasrwuhrndsat, ICLASS_CJ" 1 000 vvv vvttt PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vlutvvb_nm, ICLASS_CJ" 1 000 vvv vvttt PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vlutvwh_nm, ICLASS_CJ" 1 000 vvv vvttt PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vasruhubrndsat, ICLASS_CJ" 1 000 vvv vvttt PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vasruwuhsat, ICLASS_CJ" 1 000 vvv vvttt PP 1 uuuuu 100 ddddd") // +DEF_ENC(V6_vasruhubsat, ICLASS_CJ" 1 000 vvv vvttt PP 1 uuuuu 101 ddddd") // + +/*************************************************************** +* +* Group #1, Uses Q6 Rt32 +* +****************************************************************/ + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 001 --- ----- PP - ----- ----- ---","[#1] Vd32=(Vu32, Rt32)") +DEF_ENC(V6_vtmpyb, ICLASS_CJ" 1 001 000 ttttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vtmpybus, ICLASS_CJ" 1 001 000 ttttt PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vdmpyhb, ICLASS_CJ" 1 001 000 ttttt PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vrmpyub, ICLASS_CJ" 1 001 000 ttttt PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vrmpybus, ICLASS_CJ" 1 001 000 ttttt PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vdsaduh, ICLASS_CJ" 1 001 000 ttttt PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vdmpybus, ICLASS_CJ" 1 001 000 ttttt PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vdmpybus_dv, ICLASS_CJ" 1 001 000 ttttt PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vdmpyhsusat, ICLASS_CJ" 1 001 001 ttttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vdmpyhsuisat, ICLASS_CJ" 1 001 001 ttttt PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vdmpyhsat, ICLASS_CJ" 1 001 001 ttttt PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vdmpyhisat, ICLASS_CJ" 1 001 001 ttttt PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vdmpyhb_dv, ICLASS_CJ" 1 001 001 ttttt PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vmpybus, ICLASS_CJ" 1 001 001 ttttt PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vmpabus, ICLASS_CJ" 1 001 001 ttttt PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vmpahb, ICLASS_CJ" 1 001 001 ttttt PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vmpyh, ICLASS_CJ" 1 001 010 ttttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vmpyhss, ICLASS_CJ" 1 001 010 ttttt PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vmpyhsrs, ICLASS_CJ" 1 001 010 ttttt PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vmpyuh, ICLASS_CJ" 1 001 010 ttttt PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vrmpybusi, ICLASS_CJ" 1 001 010 ttttt PP 0 uuuuu 10i ddddd") // +DEF_ENC(V6_vrsadubi, ICLASS_CJ" 1 001 010 ttttt PP 0 uuuuu 11i ddddd") // + +DEF_ENC(V6_vmpyihb, ICLASS_CJ" 1 001 011 ttttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vror, ICLASS_CJ" 1 001 011 ttttt PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vmpyuhe, ICLASS_CJ" 1 001 011 ttttt PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vmpabuu, ICLASS_CJ" 1 001 011 ttttt PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vlut4, ICLASS_CJ" 1 001 011 ttttt PP 0 uuuuu 100 ddddd") // + + +DEF_ENC(V6_vasrw, ICLASS_CJ" 1 001 011 ttttt PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vasrh, ICLASS_CJ" 1 001 011 ttttt PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vaslw, ICLASS_CJ" 1 001 011 ttttt PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vaslh, ICLASS_CJ" 1 001 100 ttttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vlsrw, ICLASS_CJ" 1 001 100 ttttt PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vlsrh, ICLASS_CJ" 1 001 100 ttttt PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vlsrb, ICLASS_CJ" 1 001 100 ttttt PP 0 uuuuu 011 ddddd") // + +DEF_ENC(V6_vmpauhb, ICLASS_CJ" 1 001 100 ttttt PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vmpyiwub, ICLASS_CJ" 1 001 100 ttttt PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vmpyiwh, ICLASS_CJ" 1 001 100 ttttt PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vmpyiwb, ICLASS_CJ" 1 001 101 ttttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_lvsplatw, ICLASS_CJ" 1 001 101 ttttt PP 0 ----0 001 ddddd") // + + + +DEF_ENC(V6_pred_scalar2, ICLASS_CJ" 1 001 101 ttttt PP 0 ----- 010 -01dd") // +DEF_ENC(V6_vandvrt, ICLASS_CJ" 1 001 101 ttttt PP 0 uuuuu 010 -10dd") // +DEF_ENC(V6_pred_scalar2v2, ICLASS_CJ" 1 001 101 ttttt PP 0 ----- 010 -11dd") // + +DEF_ENC(V6_vtmpyhb, ICLASS_CJ" 1 001 101 ttttt PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vandqrt, ICLASS_CJ" 1 001 101 ttttt PP 0 --0uu 101 ddddd") // +DEF_ENC(V6_vandnqrt, ICLASS_CJ" 1 001 101 ttttt PP 0 --1uu 101 ddddd") // + +DEF_ENC(V6_vrmpyubi, ICLASS_CJ" 1 001 101 ttttt PP 0 uuuuu 11i ddddd") // + +DEF_ENC(V6_vmpyub, ICLASS_CJ" 1 001 110 ttttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_lvsplath, ICLASS_CJ" 1 001 110 ttttt PP 0 ----- 001 ddddd") // +DEF_ENC(V6_lvsplatb, ICLASS_CJ" 1 001 110 ttttt PP 0 ----- 010 ddddd") // + + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 001 --- ----- PP - ----- ----- ---","[#1] Vx32=(Vu32, Rt32)") +DEF_ENC(V6_vtmpyb_acc, ICLASS_CJ" 1 001 000 ttttt PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vtmpybus_acc, ICLASS_CJ" 1 001 000 ttttt PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vtmpyhb_acc, ICLASS_CJ" 1 001 000 ttttt PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vdmpyhb_acc, ICLASS_CJ" 1 001 000 ttttt PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vrmpyub_acc, ICLASS_CJ" 1 001 000 ttttt PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vrmpybus_acc, ICLASS_CJ" 1 001 000 ttttt PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vdmpybus_acc, ICLASS_CJ" 1 001 000 ttttt PP 1 uuuuu 110 xxxxx") // +DEF_ENC(V6_vdmpybus_dv_acc, ICLASS_CJ" 1 001 000 ttttt PP 1 uuuuu 111 xxxxx") // + +DEF_ENC(V6_vdmpyhsusat_acc, ICLASS_CJ" 1 001 001 ttttt PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vdmpyhsuisat_acc,ICLASS_CJ" 1 001 001 ttttt PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vdmpyhisat_acc, ICLASS_CJ" 1 001 001 ttttt PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vdmpyhsat_acc, ICLASS_CJ" 1 001 001 ttttt PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vdmpyhb_dv_acc, ICLASS_CJ" 1 001 001 ttttt PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vmpybus_acc, ICLASS_CJ" 1 001 001 ttttt PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vmpabus_acc, ICLASS_CJ" 1 001 001 ttttt PP 1 uuuuu 110 xxxxx") // +DEF_ENC(V6_vmpahb_acc, ICLASS_CJ" 1 001 001 ttttt PP 1 uuuuu 111 xxxxx") // + +DEF_ENC(V6_vmpyhsat_acc, ICLASS_CJ" 1 001 010 ttttt PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vmpyuh_acc, ICLASS_CJ" 1 001 010 ttttt PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vmpyiwb_acc, ICLASS_CJ" 1 001 010 ttttt PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vmpyiwh_acc, ICLASS_CJ" 1 001 010 ttttt PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vrmpybusi_acc, ICLASS_CJ" 1 001 010 ttttt PP 1 uuuuu 10i xxxxx") // +DEF_ENC(V6_vrsadubi_acc, ICLASS_CJ" 1 001 010 ttttt PP 1 uuuuu 11i xxxxx") // + +DEF_ENC(V6_vdsaduh_acc, ICLASS_CJ" 1 001 011 ttttt PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vmpyihb_acc, ICLASS_CJ" 1 001 011 ttttt PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vaslw_acc, ICLASS_CJ" 1 001 011 ttttt PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vandqrt_acc, ICLASS_CJ" 1 001 011 ttttt PP 1 --0uu 011 xxxxx") // +DEF_ENC(V6_vandnqrt_acc, ICLASS_CJ" 1 001 011 ttttt PP 1 --1uu 011 xxxxx") // +DEF_ENC(V6_vandvrt_acc, ICLASS_CJ" 1 001 011 ttttt PP 1 uuuuu 100 ---xx") // +DEF_ENC(V6_vasrw_acc, ICLASS_CJ" 1 001 011 ttttt PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vrmpyubi_acc, ICLASS_CJ" 1 001 011 ttttt PP 1 uuuuu 11i xxxxx") // + +DEF_ENC(V6_vmpyub_acc, ICLASS_CJ" 1 001 100 ttttt PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vmpyiwub_acc, ICLASS_CJ" 1 001 100 ttttt PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vmpauhb_acc, ICLASS_CJ" 1 001 100 ttttt PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vmpyuhe_acc, ICLASS_CJ" 1 001 100 ttttt PP 1 uuuuu 011 xxxxx") +DEF_ENC(V6_vmpahhsat, ICLASS_CJ" 1 001 100 ttttt PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vmpauhuhsat, ICLASS_CJ" 1 001 100 ttttt PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vmpsuhuhsat, ICLASS_CJ" 1 001 100 ttttt PP 1 uuuuu 110 xxxxx") // +DEF_ENC(V6_vasrh_acc, ICLASS_CJ" 1 001 100 ttttt PP 1 uuuuu 111 xxxxx") // + + + + +DEF_ENC(V6_vinsertwr, ICLASS_CJ" 1 001 101 ttttt PP 1 ----- 001 xxxxx") + +DEF_ENC(V6_vmpabuu_acc, ICLASS_CJ" 1 001 101 ttttt PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vaslh_acc, ICLASS_CJ" 1 001 101 ttttt PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vmpyh_acc, ICLASS_CJ" 1 001 101 ttttt PP 1 uuuuu 110 xxxxx") // + + + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 001 --- ----- PP - ----- ----- ---","[#1] (Vx32, Vy32, Rt32)") +DEF_ENC(V6_vshuff, ICLASS_CJ" 1 001 111 ttttt PP 1 yyyyy 001 xxxxx") // +DEF_ENC(V6_vdeal, ICLASS_CJ" 1 001 111 ttttt PP 1 yyyyy 010 xxxxx") // + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 010 --- ----- PP - ----- ----- ---","[#2] if (Ps) Vd=Vu") +DEF_ENC(V6_vcmov, ICLASS_CJ" 1 010 000 ----- PP - uuuuu -ss ddddd") +DEF_ENC(V6_vncmov, ICLASS_CJ" 1 010 001 ----- PP - uuuuu -ss ddddd") +DEF_ENC(V6_vnccombine, ICLASS_CJ" 1 010 010 vvvvv PP - uuuuu -ss ddddd") +DEF_ENC(V6_vccombine, ICLASS_CJ" 1 010 011 vvvvv PP - uuuuu -ss ddddd") + +DEF_ENC(V6_vrotr, ICLASS_CJ" 1 010 100 vvvvv PP 1 uuuuu 111 ddddd") +DEF_ENC(V6_vasr_into, ICLASS_CJ" 1 010 101 vvvvv PP 1 uuuuu 111 xxxxx") + +/*************************************************************** +* +* Group #3, Uses Q6 Rt8 +* +****************************************************************/ + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 011 --- ----- PP - ----- ----- ---","[#3] Vd32=(Vu32, Vv32, Rt8)") +DEF_ENC(V6_valignb, ICLASS_CJ" 1 011 vvv vvttt PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vlalignb, ICLASS_CJ" 1 011 vvv vvttt PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vasrwh, ICLASS_CJ" 1 011 vvv vvttt PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vasrwhsat, ICLASS_CJ" 1 011 vvv vvttt PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vasrwhrndsat, ICLASS_CJ" 1 011 vvv vvttt PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vasrwuhsat, ICLASS_CJ" 1 011 vvv vvttt PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vasrhubsat, ICLASS_CJ" 1 011 vvv vvttt PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vasrhubrndsat, ICLASS_CJ" 1 011 vvv vvttt PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vasrhbrndsat, ICLASS_CJ" 1 011 vvv vvttt PP 1 uuuuu 000 ddddd") // +DEF_ENC(V6_vlutvvb, ICLASS_CJ" 1 011 vvv vvttt PP 1 uuuuu 001 ddddd") +DEF_ENC(V6_vshuffvdd, ICLASS_CJ" 1 011 vvv vvttt PP 1 uuuuu 011 ddddd") // +DEF_ENC(V6_vdealvdd, ICLASS_CJ" 1 011 vvv vvttt PP 1 uuuuu 100 ddddd") // +DEF_ENC(V6_vlutvvb_oracc, ICLASS_CJ" 1 011 vvv vvttt PP 1 uuuuu 101 xxxxx") +DEF_ENC(V6_vlutvwh, ICLASS_CJ" 1 011 vvv vvttt PP 1 uuuuu 110 ddddd") +DEF_ENC(V6_vlutvwh_oracc, ICLASS_CJ" 1 011 vvv vvttt PP 1 uuuuu 111 xxxxx") + + + +/*************************************************************** +* +* Group #4, No Q6 regs +* +****************************************************************/ + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 100 --- ----- PP 0 ----- ----- ---","[#4] Vd32=(Vu32, Vv32)") +DEF_ENC(V6_vrmpyubv, ICLASS_CJ" 1 100 000 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vrmpybv, ICLASS_CJ" 1 100 000 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vrmpybusv, ICLASS_CJ" 1 100 000 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vdmpyhvsat, ICLASS_CJ" 1 100 000 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vmpybv, ICLASS_CJ" 1 100 000 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vmpyubv, ICLASS_CJ" 1 100 000 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vmpybusv, ICLASS_CJ" 1 100 000 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vmpyhv, ICLASS_CJ" 1 100 000 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vmpyuhv, ICLASS_CJ" 1 100 001 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vmpyhvsrs, ICLASS_CJ" 1 100 001 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vmpyhus, ICLASS_CJ" 1 100 001 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vmpabusv, ICLASS_CJ" 1 100 001 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vmpyih, ICLASS_CJ" 1 100 001 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vand, ICLASS_CJ" 1 100 001 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vor, ICLASS_CJ" 1 100 001 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vxor, ICLASS_CJ" 1 100 001 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vaddw, ICLASS_CJ" 1 100 010 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vaddubsat, ICLASS_CJ" 1 100 010 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vadduhsat, ICLASS_CJ" 1 100 010 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vaddhsat, ICLASS_CJ" 1 100 010 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vaddwsat, ICLASS_CJ" 1 100 010 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vsubb, ICLASS_CJ" 1 100 010 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vsubh, ICLASS_CJ" 1 100 010 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vsubw, ICLASS_CJ" 1 100 010 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vsububsat, ICLASS_CJ" 1 100 011 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vsubuhsat, ICLASS_CJ" 1 100 011 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vsubhsat, ICLASS_CJ" 1 100 011 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vsubwsat, ICLASS_CJ" 1 100 011 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vaddb_dv, ICLASS_CJ" 1 100 011 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vaddh_dv, ICLASS_CJ" 1 100 011 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vaddw_dv, ICLASS_CJ" 1 100 011 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vaddubsat_dv,ICLASS_CJ" 1 100 011 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vadduhsat_dv,ICLASS_CJ" 1 100 100 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vaddhsat_dv, ICLASS_CJ" 1 100 100 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vaddwsat_dv, ICLASS_CJ" 1 100 100 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vsubb_dv, ICLASS_CJ" 1 100 100 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vsubh_dv, ICLASS_CJ" 1 100 100 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vsubw_dv, ICLASS_CJ" 1 100 100 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vsububsat_dv,ICLASS_CJ" 1 100 100 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vsubuhsat_dv,ICLASS_CJ" 1 100 100 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vsubhsat_dv, ICLASS_CJ" 1 100 101 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vsubwsat_dv, ICLASS_CJ" 1 100 101 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vaddubh, ICLASS_CJ" 1 100 101 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vadduhw, ICLASS_CJ" 1 100 101 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vaddhw, ICLASS_CJ" 1 100 101 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vsububh, ICLASS_CJ" 1 100 101 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vsubuhw, ICLASS_CJ" 1 100 101 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vsubhw, ICLASS_CJ" 1 100 101 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vabsdiffub, ICLASS_CJ" 1 100 110 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vabsdiffh, ICLASS_CJ" 1 100 110 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vabsdiffuh, ICLASS_CJ" 1 100 110 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vabsdiffw, ICLASS_CJ" 1 100 110 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vavgub, ICLASS_CJ" 1 100 110 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vavguh, ICLASS_CJ" 1 100 110 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vavgh, ICLASS_CJ" 1 100 110 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vavgw, ICLASS_CJ" 1 100 110 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vnavgub, ICLASS_CJ" 1 100 111 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vnavgh, ICLASS_CJ" 1 100 111 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vnavgw, ICLASS_CJ" 1 100 111 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vavgubrnd, ICLASS_CJ" 1 100 111 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vavguhrnd, ICLASS_CJ" 1 100 111 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vavghrnd, ICLASS_CJ" 1 100 111 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vavgwrnd, ICLASS_CJ" 1 100 111 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vmpabuuv, ICLASS_CJ" 1 100 111 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 100 --- ----- PP 1 ----- ----- ---","[#4] Vx32=(Vu32, Vv32)") +DEF_ENC(V6_vrmpyubv_acc, ICLASS_CJ" 1 100 000 vvvvv PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vrmpybv_acc, ICLASS_CJ" 1 100 000 vvvvv PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vrmpybusv_acc, ICLASS_CJ" 1 100 000 vvvvv PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vdmpyhvsat_acc, ICLASS_CJ" 1 100 000 vvvvv PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vmpybv_acc, ICLASS_CJ" 1 100 000 vvvvv PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vmpyubv_acc, ICLASS_CJ" 1 100 000 vvvvv PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vmpybusv_acc, ICLASS_CJ" 1 100 000 vvvvv PP 1 uuuuu 110 xxxxx") // +DEF_ENC(V6_vmpyhv_acc, ICLASS_CJ" 1 100 000 vvvvv PP 1 uuuuu 111 xxxxx") // + +DEF_ENC(V6_vmpyuhv_acc, ICLASS_CJ" 1 100 001 vvvvv PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vmpyhus_acc, ICLASS_CJ" 1 100 001 vvvvv PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vaddhw_acc, ICLASS_CJ" 1 100 001 vvvvv PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vmpyowh_64_acc, ICLASS_CJ" 1 100 001 vvvvv PP 1 uuuuu 011 xxxxx") +DEF_ENC(V6_vmpyih_acc, ICLASS_CJ" 1 100 001 vvvvv PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vmpyiewuh_acc, ICLASS_CJ" 1 100 001 vvvvv PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vmpyowh_sacc, ICLASS_CJ" 1 100 001 vvvvv PP 1 uuuuu 110 xxxxx") // +DEF_ENC(V6_vmpyowh_rnd_sacc,ICLASS_CJ" 1 100 001 vvvvv PP 1 uuuuu 111 xxxxx") // + +DEF_ENC(V6_vmpyiewh_acc, ICLASS_CJ" 1 100 010 vvvvv PP 1 uuuuu 000 xxxxx") // + +DEF_ENC(V6_vadduhw_acc, ICLASS_CJ" 1 100 010 vvvvv PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vaddubh_acc, ICLASS_CJ" 1 100 010 vvvvv PP 1 uuuuu 101 xxxxx") // + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 100 100 ----- PP 1 ----- ----- ---","[#4] Qx4=(Vu32, Vv32)") +// Grouped by element size (lsbs), operation (next-lsbs) and operation (next-lsbs) +DEF_ENC(V6_veqb_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 000 000xx") // +DEF_ENC(V6_veqh_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 000 001xx") // +DEF_ENC(V6_veqw_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 000 010xx") // + +DEF_ENC(V6_vgtb_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 000 100xx") // +DEF_ENC(V6_vgth_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 000 101xx") // +DEF_ENC(V6_vgtw_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 000 110xx") // + +DEF_ENC(V6_vgtub_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 001 000xx") // +DEF_ENC(V6_vgtuh_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 001 001xx") // +DEF_ENC(V6_vgtuw_and, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 001 010xx") // + +DEF_ENC(V6_veqb_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 010 000xx") // +DEF_ENC(V6_veqh_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 010 001xx") // +DEF_ENC(V6_veqw_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 010 010xx") // + +DEF_ENC(V6_vgtb_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 010 100xx") // +DEF_ENC(V6_vgth_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 010 101xx") // +DEF_ENC(V6_vgtw_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 010 110xx") // + +DEF_ENC(V6_vgtub_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 011 000xx") // +DEF_ENC(V6_vgtuh_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 011 001xx") // +DEF_ENC(V6_vgtuw_or, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 011 010xx") // + +DEF_ENC(V6_veqb_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 100 000xx") // +DEF_ENC(V6_veqh_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 100 001xx") // +DEF_ENC(V6_veqw_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 100 010xx") // + +DEF_ENC(V6_vgtb_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 100 100xx") // +DEF_ENC(V6_vgth_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 100 101xx") // +DEF_ENC(V6_vgtw_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 100 110xx") // + +DEF_ENC(V6_vgtub_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 101 000xx") // +DEF_ENC(V6_vgtuh_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 101 001xx") // +DEF_ENC(V6_vgtuw_xor, ICLASS_CJ" 1 100 100 vvvvv PP 1 uuuuu 101 010xx") // + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 100 101 ----- PP 1 ----- ----- ---","[#4] Qx4,Vd32=(Vu32, Vv32)") +DEF_ENC(V6_vaddcarry, ICLASS_CJ" 1 100 101 vvvvv PP 1 uuuuu 0xx ddddd") // +DEF_ENC(V6_vsubcarry, ICLASS_CJ" 1 100 101 vvvvv PP 1 uuuuu 1xx ddddd") // + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 100 11- ----- PP 1 ----- ----- ---","[#4] Vx32|=(Vu32, Vv32,#)") +DEF_ENC(V6_vlutvvb_oracci, ICLASS_CJ" 1 100 110 vvvvv PP 1 uuuuu iii xxxxx") // +DEF_ENC(V6_vlutvwh_oracci, ICLASS_CJ" 1 100 111 vvvvv PP 1 uuuuu iii xxxxx") // + + + +/*************************************************************** +* +* Group #5, Reserved/Deprecated. Uses Q6 Rx. Stupid FFT. +* +****************************************************************/ + + + + +/*************************************************************** +* +* Group #6, No Q6 regs +* +****************************************************************/ + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --0 ----- PP 0 ----- ----- ---","[#6] Vd32=Vu32") +DEF_ENC(V6_vabsh, ICLASS_CJ" 1 110 --0 ---00 PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vabsh_sat, ICLASS_CJ" 1 110 --0 ---00 PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vabsw, ICLASS_CJ" 1 110 --0 ---00 PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vabsw_sat, ICLASS_CJ" 1 110 --0 ---00 PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vnot, ICLASS_CJ" 1 110 --0 ---00 PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vdealh, ICLASS_CJ" 1 110 --0 ---00 PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vdealb, ICLASS_CJ" 1 110 --0 ---00 PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vunpackub, ICLASS_CJ" 1 110 --0 ---01 PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vunpackuh, ICLASS_CJ" 1 110 --0 ---01 PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vunpackb, ICLASS_CJ" 1 110 --0 ---01 PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vunpackh, ICLASS_CJ" 1 110 --0 ---01 PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vabsb, ICLASS_CJ" 1 110 --0 ---01 PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vabsb_sat, ICLASS_CJ" 1 110 --0 ---01 PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vshuffh, ICLASS_CJ" 1 110 --0 ---01 PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vshuffb, ICLASS_CJ" 1 110 --0 ---10 PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vzb, ICLASS_CJ" 1 110 --0 ---10 PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vzh, ICLASS_CJ" 1 110 --0 ---10 PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vsb, ICLASS_CJ" 1 110 --0 ---10 PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vsh, ICLASS_CJ" 1 110 --0 ---10 PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vcl0w, ICLASS_CJ" 1 110 --0 ---10 PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vpopcounth, ICLASS_CJ" 1 110 --0 ---10 PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vcl0h, ICLASS_CJ" 1 110 --0 ---10 PP 0 uuuuu 111 ddddd") // + + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --0 ---11 PP 0 ----- ----- ---","[#6] Qd4=Qt4, Qs4") +DEF_ENC(V6_pred_and, ICLASS_CJ" 1 110 tt0 ---11 PP 0 ---ss 000 000dd") // +DEF_ENC(V6_pred_or, ICLASS_CJ" 1 110 tt0 ---11 PP 0 ---ss 000 001dd") // +DEF_ENC(V6_pred_not, ICLASS_CJ" 1 110 --0 ---11 PP 0 ---ss 000 010dd") // +DEF_ENC(V6_pred_xor, ICLASS_CJ" 1 110 tt0 ---11 PP 0 ---ss 000 011dd") // +DEF_ENC(V6_pred_or_n, ICLASS_CJ" 1 110 tt0 ---11 PP 0 ---ss 000 100dd") // +DEF_ENC(V6_pred_and_n, ICLASS_CJ" 1 110 tt0 ---11 PP 0 ---ss 000 101dd") // +DEF_ENC(V6_shuffeqh, ICLASS_CJ" 1 110 tt0 ---11 PP 0 ---ss 000 110dd") // +DEF_ENC(V6_shuffeqw, ICLASS_CJ" 1 110 tt0 ---11 PP 0 ---ss 000 111dd") // + +DEF_ENC(V6_vnormamtw, ICLASS_CJ" 1 110 --0 ---11 PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vnormamth, ICLASS_CJ" 1 110 --0 ---11 PP 0 uuuuu 101 ddddd") // + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --1 ----- PP 0 ----- ----- ---","[#6] Vd32=Vu32,Vv32") +DEF_ENC(V6_vlutvvbi, ICLASS_CJ" 1 110 001 vvvvv PP 0 uuuuu iii ddddd") +DEF_ENC(V6_vlutvwhi, ICLASS_CJ" 1 110 011 vvvvv PP 0 uuuuu iii ddddd") + +DEF_ENC(V6_vaddbsat_dv, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 000 ddddd") +DEF_ENC(V6_vsubbsat_dv, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 001 ddddd") +DEF_ENC(V6_vadduwsat_dv, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 010 ddddd") +DEF_ENC(V6_vsubuwsat_dv, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 011 ddddd") +DEF_ENC(V6_vaddububb_sat, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 100 ddddd") +DEF_ENC(V6_vsubububb_sat, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 101 ddddd") +DEF_ENC(V6_vmpyewuh_64, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 110 ddddd") + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --0 ----- PP 1 ----- ----- ---","Vx32=Vu32") +DEF_ENC(V6_vunpackob, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vunpackoh, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 001 xxxxx") // +//DEF_ENC(V6_vunpackow, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 010 xxxxx") // + +DEF_ENC(V6_vhist, ICLASS_CJ" 1 110 --0 ---00 PP 1 -000- 100 -----") +DEF_ENC(V6_vwhist256, ICLASS_CJ" 1 110 --0 ---00 PP 1 -0010 100 -----") +DEF_ENC(V6_vwhist256_sat, ICLASS_CJ" 1 110 --0 ---00 PP 1 -0011 100 -----") +DEF_ENC(V6_vwhist128, ICLASS_CJ" 1 110 --0 ---00 PP 1 -010- 100 -----") +DEF_ENC(V6_vwhist128m, ICLASS_CJ" 1 110 --0 ---00 PP 1 -011i 100 -----") + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --0 ----- PP 1 ----- ----- ---","if (Qv4) Vx32=Vu32") +DEF_ENC(V6_vaddbq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vaddhq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vaddwq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vaddbnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vaddhnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vaddwnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vsubbq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 110 xxxxx") // +DEF_ENC(V6_vsubhq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 111 xxxxx") // + +DEF_ENC(V6_vsubwq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vsubbnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vsubhnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vsubwnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 011 xxxxx") // + +DEF_ENC(V6_vhistq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --00- 100 -----") +DEF_ENC(V6_vwhist256q, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --010 100 -----") +DEF_ENC(V6_vwhist256q_sat, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --011 100 -----") +DEF_ENC(V6_vwhist128q, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --10- 100 -----") +DEF_ENC(V6_vwhist128qm, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --11i 100 -----") + + +DEF_ENC(V6_vandvqv, ICLASS_CJ" 1 110 vv0 ---11 PP 1 uuuuu 000 ddddd") +DEF_ENC(V6_vandvnqv, ICLASS_CJ" 1 110 vv0 ---11 PP 1 uuuuu 001 ddddd") + + +DEF_ENC(V6_vprefixqb, ICLASS_CJ" 1 110 vv0 ---11 PP 1 --000 010 ddddd") // +DEF_ENC(V6_vprefixqh, ICLASS_CJ" 1 110 vv0 ---11 PP 1 --001 010 ddddd") // +DEF_ENC(V6_vprefixqw, ICLASS_CJ" 1 110 vv0 ---11 PP 1 --010 010 ddddd") // + + + + +DEF_ENC(V6_vassign, ICLASS_CJ" 1 110 --0 ---11 PP 1 uuuuu 111 ddddd") + +DEF_ENC(V6_valignbi, ICLASS_CJ" 1 110 001 vvvvv PP 1 uuuuu iii ddddd") +DEF_ENC(V6_vlalignbi, ICLASS_CJ" 1 110 011 vvvvv PP 1 uuuuu iii ddddd") +DEF_ENC(V6_vswap, ICLASS_CJ" 1 110 101 vvvvv PP 1 uuuuu -tt ddddd") // +DEF_ENC(V6_vmux, ICLASS_CJ" 1 110 111 vvvvv PP 1 uuuuu -tt ddddd") // + + + +/*************************************************************** +* +* Group #7, No Q6 regs +* +****************************************************************/ + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 111 --- ----- PP 0 ----- ----- ---","[#7] Vd32=(Vu32, Vv32)") +DEF_ENC(V6_vaddbsat, ICLASS_CJ" 1 111 000 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vminub, ICLASS_CJ" 1 111 000 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vminuh, ICLASS_CJ" 1 111 000 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vminh, ICLASS_CJ" 1 111 000 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vminw, ICLASS_CJ" 1 111 000 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vmaxub, ICLASS_CJ" 1 111 000 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vmaxuh, ICLASS_CJ" 1 111 000 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vmaxh, ICLASS_CJ" 1 111 000 vvvvv PP 0 uuuuu 111 ddddd") // + + +DEF_ENC(V6_vaddclbh, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 000 ddddd") // +DEF_ENC(V6_vaddclbw, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 001 ddddd") // + +DEF_ENC(V6_vavguw, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 010 ddddd") // +DEF_ENC(V6_vavguwrnd, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 011 ddddd") // +DEF_ENC(V6_vavgb, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 100 ddddd") // +DEF_ENC(V6_vavgbrnd, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 101 ddddd") // +DEF_ENC(V6_vnavgb, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 110 ddddd") // + + +DEF_ENC(V6_vmaxw, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vdelta, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vsubbsat, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vrdelta, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vminb, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vmaxb, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vsatuwuh, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vdealb4w, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 111 ddddd") // + + +DEF_ENC(V6_vmpyowh_rnd, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vshuffeb, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vshuffob, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vshufeh, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vshufoh, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vshufoeh, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vshufoeb, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vcombine, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vmpyieoh, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vadduwsat, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vsathub, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vsatwh, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vroundwh, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 100 ddddd") +DEF_ENC(V6_vroundwuh, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 101 ddddd") +DEF_ENC(V6_vroundhb, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 110 ddddd") +DEF_ENC(V6_vroundhub, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 111 ddddd") + +DEF_FIELDROW_DESC32( ICLASS_CJ" 1 111 100 ----- PP - ----- ----- ---","[#7] Qd4=(Vu32, Vv32)") +DEF_ENC(V6_veqb, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 000 000dd") // +DEF_ENC(V6_veqh, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 000 001dd") // +DEF_ENC(V6_veqw, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 000 010dd") // + +DEF_ENC(V6_vgtb, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 000 100dd") // +DEF_ENC(V6_vgth, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 000 101dd") // +DEF_ENC(V6_vgtw, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 000 110dd") // + +DEF_ENC(V6_vgtub, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 001 000dd") // +DEF_ENC(V6_vgtuh, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 001 001dd") // +DEF_ENC(V6_vgtuw, ICLASS_CJ" 1 111 100 vvvvv PP 0 uuuuu 001 010dd") // + + +DEF_ENC(V6_vasrwv, ICLASS_CJ" 1 111 101 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vlsrwv, ICLASS_CJ" 1 111 101 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vlsrhv, ICLASS_CJ" 1 111 101 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vasrhv, ICLASS_CJ" 1 111 101 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vaslwv, ICLASS_CJ" 1 111 101 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vaslhv, ICLASS_CJ" 1 111 101 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vaddb, ICLASS_CJ" 1 111 101 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vaddh, ICLASS_CJ" 1 111 101 vvvvv PP 0 uuuuu 111 ddddd") // + + +DEF_ENC(V6_vmpyiewuh, ICLASS_CJ" 1 111 110 vvvvv PP 0 uuuuu 000 ddddd") +DEF_ENC(V6_vmpyiowh, ICLASS_CJ" 1 111 110 vvvvv PP 0 uuuuu 001 ddddd") +DEF_ENC(V6_vpackeb, ICLASS_CJ" 1 111 110 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vpackeh, ICLASS_CJ" 1 111 110 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vsubuwsat, ICLASS_CJ" 1 111 110 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vpackhub_sat,ICLASS_CJ" 1 111 110 vvvvv PP 0 uuuuu 101 ddddd") // +DEF_ENC(V6_vpackhb_sat, ICLASS_CJ" 1 111 110 vvvvv PP 0 uuuuu 110 ddddd") // +DEF_ENC(V6_vpackwuh_sat,ICLASS_CJ" 1 111 110 vvvvv PP 0 uuuuu 111 ddddd") // + +DEF_ENC(V6_vpackwh_sat, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 000 ddddd") // +DEF_ENC(V6_vpackob, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 001 ddddd") // +DEF_ENC(V6_vpackoh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 010 ddddd") // +DEF_ENC(V6_vrounduhub, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 011 ddddd") // +DEF_ENC(V6_vrounduwuh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 100 ddddd") // +DEF_ENC(V6_vmpyewuh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 101 ddddd") +DEF_ENC(V6_vmpyowh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 111 ddddd") + + +#endif /* NO MMVEC */ From 59203274496402eba8a5048f07e8d854edc3eda6 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Mon, 8 Mar 2021 08:04:00 -0800 Subject: [PATCH 1186/1334] Hexagon HVX (tests/tcg/hexagon) vector_add_int test Reviewed-by: Richard Henderson Signed-off-by: Taylor Simpson --- tests/tcg/hexagon/vector_add_int.c | 61 ++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/tcg/hexagon/vector_add_int.c diff --git a/tests/tcg/hexagon/vector_add_int.c b/tests/tcg/hexagon/vector_add_int.c new file mode 100644 index 0000000000..d6010ea14b --- /dev/null +++ b/tests/tcg/hexagon/vector_add_int.c @@ -0,0 +1,61 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include + +int gA[401]; +int gB[401]; +int gC[401]; + +void vector_add_int() +{ + int i; + for (i = 0; i < 400; i++) { + gA[i] = gB[i] + gC[i]; + } +} + +int main() +{ + int error = 0; + int i; + for (i = 0; i < 400; i++) { + gB[i] = i * 2; + gC[i] = i * 3; + } + gA[400] = 17; + vector_add_int(); + for (i = 0; i < 400; i++) { + if (gA[i] != i * 5) { + error++; + printf("ERROR: gB[%d] = %d\t", i, gB[i]); + printf("gC[%d] = %d\t", i, gC[i]); + printf("gA[%d] = %d\n", i, gA[i]); + } + } + if (gA[400] != 17) { + error++; + printf("ERROR: Overran the buffer\n"); + } + if (!error) { + printf("PASS\n"); + return 0; + } else { + printf("FAIL\n"); + return 1; + } +} From afb9539ebe01239f585c08c36c3e1b2a71d7767b Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 3 Nov 2021 15:39:09 -0500 Subject: [PATCH 1187/1334] Hexagon HVX (tests/tcg/hexagon) hvx_misc test Tests for packet semantics vector loads (aligned and unaligned) vector stores (aligned and unaligned) vector masked stores vector new value store maximum HVX temps in a packet vector operations Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- tests/tcg/hexagon/hvx_misc.c | 469 +++++++++++++++++++++++++++++++++++ 1 file changed, 469 insertions(+) create mode 100644 tests/tcg/hexagon/hvx_misc.c diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c new file mode 100644 index 0000000000..312bb98b41 --- /dev/null +++ b/tests/tcg/hexagon/hvx_misc.c @@ -0,0 +1,469 @@ +/* + * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include + +int err; + +static void __check(int line, int i, int j, uint64_t result, uint64_t expect) +{ + if (result != expect) { + printf("ERROR at line %d: [%d][%d] 0x%016llx != 0x%016llx\n", + line, i, j, result, expect); + err++; + } +} + +#define check(RES, EXP) __check(__LINE__, RES, EXP) + +#define MAX_VEC_SIZE_BYTES 128 + +typedef union { + uint64_t ud[MAX_VEC_SIZE_BYTES / 8]; + int64_t d[MAX_VEC_SIZE_BYTES / 8]; + uint32_t uw[MAX_VEC_SIZE_BYTES / 4]; + int32_t w[MAX_VEC_SIZE_BYTES / 4]; + uint16_t uh[MAX_VEC_SIZE_BYTES / 2]; + int16_t h[MAX_VEC_SIZE_BYTES / 2]; + uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; + int8_t b[MAX_VEC_SIZE_BYTES / 1]; +} MMVector; + +#define BUFSIZE 16 +#define OUTSIZE 16 +#define MASKMOD 3 + +MMVector buffer0[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector buffer1[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector mask[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector output[OUTSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector expect[OUTSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); + +#define CHECK_OUTPUT_FUNC(FIELD, FIELDSZ) \ +static void check_output_##FIELD(int line, size_t num_vectors) \ +{ \ + for (int i = 0; i < num_vectors; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ + __check(line, i, j, output[i].FIELD[j], expect[i].FIELD[j]); \ + } \ + } \ +} + +CHECK_OUTPUT_FUNC(d, 8) +CHECK_OUTPUT_FUNC(w, 4) +CHECK_OUTPUT_FUNC(h, 2) +CHECK_OUTPUT_FUNC(b, 1) + +static void init_buffers(void) +{ + int counter0 = 0; + int counter1 = 17; + for (int i = 0; i < BUFSIZE; i++) { + for (int j = 0; j < MAX_VEC_SIZE_BYTES; j++) { + buffer0[i].b[j] = counter0++; + buffer1[i].b[j] = counter1++; + } + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + mask[i].w[j] = (i + j % MASKMOD == 0) ? 0 : 1; + } + } +} + +static void test_load_tmp(void) +{ + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + + for (int i = 0; i < BUFSIZE; i++) { + /* + * Load into v12 as .tmp, then use it in the next packet + * Should get the new value within the same packet and + * the old value in the next packet + */ + asm("v3 = vmem(%0 + #0)\n\t" + "r1 = #1\n\t" + "v12 = vsplat(r1)\n\t" + "{\n\t" + " v12.tmp = vmem(%1 + #0)\n\t" + " v4.w = vadd(v12.w, v3.w)\n\t" + "}\n\t" + "v4.w = vadd(v4.w, v12.w)\n\t" + "vmem(%2 + #0) = v4\n\t" + : : "r"(p0), "r"(p1), "r"(pout) + : "r1", "v12", "v3", "v4", "v6", "memory"); + p0 += sizeof(MMVector); + p1 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[i].w[j] = buffer0[i].w[j] + buffer1[i].w[j] + 1; + } + } + + check_output_w(__LINE__, BUFSIZE); +} + +static void test_load_cur(void) +{ + void *p0 = buffer0; + void *pout = output; + + for (int i = 0; i < BUFSIZE; i++) { + asm("{\n\t" + " v2.cur = vmem(%0 + #0)\n\t" + " vmem(%1 + #0) = v2\n\t" + "}\n\t" + : : "r"(p0), "r"(pout) : "v2", "memory"); + p0 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[i].uw[j] = buffer0[i].uw[j]; + } + } + + check_output_w(__LINE__, BUFSIZE); +} + +static void test_load_aligned(void) +{ + /* Aligned loads ignore the low bits of the address */ + void *p0 = buffer0; + void *pout = output; + const size_t offset = 13; + + p0 += offset; /* Create an unaligned address */ + asm("v2 = vmem(%0 + #0)\n\t" + "vmem(%1 + #0) = v2\n\t" + : : "r"(p0), "r"(pout) : "v2", "memory"); + + expect[0] = buffer0[0]; + + check_output_w(__LINE__, 1); +} + +static void test_load_unaligned(void) +{ + void *p0 = buffer0; + void *pout = output; + const size_t offset = 12; + + p0 += offset; /* Create an unaligned address */ + asm("v2 = vmemu(%0 + #0)\n\t" + "vmem(%1 + #0) = v2\n\t" + : : "r"(p0), "r"(pout) : "v2", "memory"); + + memcpy(expect, &buffer0[0].ub[offset], sizeof(MMVector)); + + check_output_w(__LINE__, 1); +} + +static void test_store_aligned(void) +{ + /* Aligned stores ignore the low bits of the address */ + void *p0 = buffer0; + void *pout = output; + const size_t offset = 13; + + pout += offset; /* Create an unaligned address */ + asm("v2 = vmem(%0 + #0)\n\t" + "vmem(%1 + #0) = v2\n\t" + : : "r"(p0), "r"(pout) : "v2", "memory"); + + expect[0] = buffer0[0]; + + check_output_w(__LINE__, 1); +} + +static void test_store_unaligned(void) +{ + void *p0 = buffer0; + void *pout = output; + const size_t offset = 12; + + pout += offset; /* Create an unaligned address */ + asm("v2 = vmem(%0 + #0)\n\t" + "vmemu(%1 + #0) = v2\n\t" + : : "r"(p0), "r"(pout) : "v2", "memory"); + + memcpy(expect, buffer0, 2 * sizeof(MMVector)); + memcpy(&expect[0].ub[offset], buffer0, sizeof(MMVector)); + + check_output_w(__LINE__, 2); +} + +static void test_masked_store(bool invert) +{ + void *p0 = buffer0; + void *pmask = mask; + void *pout = output; + + memset(expect, 0xff, sizeof(expect)); + memset(output, 0xff, sizeof(expect)); + + for (int i = 0; i < BUFSIZE; i++) { + if (invert) { + asm("r4 = #0\n\t" + "v4 = vsplat(r4)\n\t" + "v5 = vmem(%0 + #0)\n\t" + "q0 = vcmp.eq(v4.w, v5.w)\n\t" + "v5 = vmem(%1)\n\t" + "if (!q0) vmem(%2) = v5\n\t" /* Inverted test */ + : : "r"(pmask), "r"(p0), "r"(pout) + : "r4", "v4", "v5", "q0", "memory"); + } else { + asm("r4 = #0\n\t" + "v4 = vsplat(r4)\n\t" + "v5 = vmem(%0 + #0)\n\t" + "q0 = vcmp.eq(v4.w, v5.w)\n\t" + "v5 = vmem(%1)\n\t" + "if (q0) vmem(%2) = v5\n\t" /* Non-inverted test */ + : : "r"(pmask), "r"(p0), "r"(pout) + : "r4", "v4", "v5", "q0", "memory"); + } + p0 += sizeof(MMVector); + pmask += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + if (invert) { + if (i + j % MASKMOD != 0) { + expect[i].w[j] = buffer0[i].w[j]; + } + } else { + if (i + j % MASKMOD == 0) { + expect[i].w[j] = buffer0[i].w[j]; + } + } + } + } + + check_output_w(__LINE__, BUFSIZE); +} + +static void test_new_value_store(void) +{ + void *p0 = buffer0; + void *pout = output; + + asm("{\n\t" + " v2 = vmem(%0 + #0)\n\t" + " vmem(%1 + #0) = v2.new\n\t" + "}\n\t" + : : "r"(p0), "r"(pout) : "v2", "memory"); + + expect[0] = buffer0[0]; + + check_output_w(__LINE__, 1); +} + +static void test_max_temps() +{ + void *p0 = buffer0; + void *pout = output; + + asm("v0 = vmem(%0 + #0)\n\t" + "v1 = vmem(%0 + #1)\n\t" + "v2 = vmem(%0 + #2)\n\t" + "v3 = vmem(%0 + #3)\n\t" + "v4 = vmem(%0 + #4)\n\t" + "{\n\t" + " v1:0.w = vadd(v3:2.w, v1:0.w)\n\t" + " v2.b = vshuffe(v3.b, v2.b)\n\t" + " v3.w = vadd(v1.w, v4.w)\n\t" + " v4.tmp = vmem(%0 + #5)\n\t" + "}\n\t" + "vmem(%1 + #0) = v0\n\t" + "vmem(%1 + #1) = v1\n\t" + "vmem(%1 + #2) = v2\n\t" + "vmem(%1 + #3) = v3\n\t" + "vmem(%1 + #4) = v4\n\t" + : : "r"(p0), "r"(pout) : "memory"); + + /* The first two vectors come from the vadd-pair instruction */ + for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) { + expect[0].w[i] = buffer0[0].w[i] + buffer0[2].w[i]; + expect[1].w[i] = buffer0[1].w[i] + buffer0[3].w[i]; + } + /* The third vector comes from the vshuffe instruction */ + for (int i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) { + expect[2].uh[i] = (buffer0[2].uh[i] & 0xff) | + (buffer0[3].uh[i] & 0xff) << 8; + } + /* The fourth vector comes from the vadd-single instruction */ + for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) { + expect[3].w[i] = buffer0[1].w[i] + buffer0[5].w[i]; + } + /* + * The fifth vector comes from the load to v4 + * make sure the .tmp is dropped + */ + expect[4] = buffer0[4]; + + check_output_b(__LINE__, 5); +} + +#define VEC_OP1(ASM, EL, IN, OUT) \ + asm("v2 = vmem(%0 + #0)\n\t" \ + "v2" #EL " = " #ASM "(v2" #EL ")\n\t" \ + "vmem(%1 + #0) = v2\n\t" \ + : : "r"(IN), "r"(OUT) : "v2", "memory") + +#define VEC_OP2(ASM, EL, IN0, IN1, OUT) \ + asm("v2 = vmem(%0 + #0)\n\t" \ + "v3 = vmem(%1 + #0)\n\t" \ + "v2" #EL " = " #ASM "(v2" #EL ", v3" #EL ")\n\t" \ + "vmem(%2 + #0) = v2\n\t" \ + : : "r"(IN0), "r"(IN1), "r"(OUT) : "v2", "v3", "memory") + +#define TEST_VEC_OP1(NAME, ASM, EL, FIELD, FIELDSZ, OP) \ +static void test_##NAME(void) \ +{ \ + void *pin = buffer0; \ + void *pout = output; \ + for (int i = 0; i < BUFSIZE; i++) { \ + VEC_OP1(ASM, EL, pin, pout); \ + pin += sizeof(MMVector); \ + pout += sizeof(MMVector); \ + } \ + for (int i = 0; i < BUFSIZE; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ + expect[i].FIELD[j] = OP buffer0[i].FIELD[j]; \ + } \ + } \ + check_output_##FIELD(__LINE__, BUFSIZE); \ +} + +#define TEST_VEC_OP2(NAME, ASM, EL, FIELD, FIELDSZ, OP) \ +static void test_##NAME(void) \ +{ \ + void *p0 = buffer0; \ + void *p1 = buffer1; \ + void *pout = output; \ + for (int i = 0; i < BUFSIZE; i++) { \ + VEC_OP2(ASM, EL, p0, p1, pout); \ + p0 += sizeof(MMVector); \ + p1 += sizeof(MMVector); \ + pout += sizeof(MMVector); \ + } \ + for (int i = 0; i < BUFSIZE; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ + expect[i].FIELD[j] = buffer0[i].FIELD[j] OP buffer1[i].FIELD[j]; \ + } \ + } \ + check_output_##FIELD(__LINE__, BUFSIZE); \ +} + +#define THRESHOLD 31 + +#define PRED_OP2(ASM, IN0, IN1, OUT, INV) \ + asm("r4 = #%3\n\t" \ + "v1.b = vsplat(r4)\n\t" \ + "v2 = vmem(%0 + #0)\n\t" \ + "q0 = vcmp.gt(v2.b, v1.b)\n\t" \ + "v3 = vmem(%1 + #0)\n\t" \ + "q1 = vcmp.gt(v3.b, v1.b)\n\t" \ + "q2 = " #ASM "(q0, " INV "q1)\n\t" \ + "r4 = #0xff\n\t" \ + "v1.b = vsplat(r4)\n\t" \ + "if (q2) vmem(%2 + #0) = v1\n\t" \ + : : "r"(IN0), "r"(IN1), "r"(OUT), "i"(THRESHOLD) \ + : "r4", "v1", "v2", "v3", "q0", "q1", "q2", "memory") + +#define TEST_PRED_OP2(NAME, ASM, OP, INV) \ +static void test_##NAME(bool invert) \ +{ \ + void *p0 = buffer0; \ + void *p1 = buffer1; \ + void *pout = output; \ + memset(output, 0, sizeof(expect)); \ + for (int i = 0; i < BUFSIZE; i++) { \ + PRED_OP2(ASM, p0, p1, pout, INV); \ + p0 += sizeof(MMVector); \ + p1 += sizeof(MMVector); \ + pout += sizeof(MMVector); \ + } \ + for (int i = 0; i < BUFSIZE; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES; j++) { \ + bool p0 = (buffer0[i].b[j] > THRESHOLD); \ + bool p1 = (buffer1[i].b[j] > THRESHOLD); \ + if (invert) { \ + expect[i].b[j] = (p0 OP !p1) ? 0xff : 0x00; \ + } else { \ + expect[i].b[j] = (p0 OP p1) ? 0xff : 0x00; \ + } \ + } \ + } \ + check_output_b(__LINE__, BUFSIZE); \ +} + +TEST_VEC_OP2(vadd_w, vadd, .w, w, 4, +) +TEST_VEC_OP2(vadd_h, vadd, .h, h, 2, +) +TEST_VEC_OP2(vadd_b, vadd, .b, b, 1, +) +TEST_VEC_OP2(vsub_w, vsub, .w, w, 4, -) +TEST_VEC_OP2(vsub_h, vsub, .h, h, 2, -) +TEST_VEC_OP2(vsub_b, vsub, .b, b, 1, -) +TEST_VEC_OP2(vxor, vxor, , d, 8, ^) +TEST_VEC_OP2(vand, vand, , d, 8, &) +TEST_VEC_OP2(vor, vor, , d, 8, |) +TEST_VEC_OP1(vnot, vnot, , d, 8, ~) + +TEST_PRED_OP2(pred_or, or, |, "") +TEST_PRED_OP2(pred_or_n, or, |, "!") +TEST_PRED_OP2(pred_and, and, &, "") +TEST_PRED_OP2(pred_and_n, and, &, "!") +TEST_PRED_OP2(pred_xor, xor, ^, "") + +int main() +{ + init_buffers(); + + test_load_tmp(); + test_load_cur(); + test_load_aligned(); + test_load_unaligned(); + test_store_aligned(); + test_store_unaligned(); + test_masked_store(false); + test_masked_store(true); + test_new_value_store(); + test_max_temps(); + + test_vadd_w(); + test_vadd_h(); + test_vadd_b(); + test_vsub_w(); + test_vsub_h(); + test_vsub_b(); + test_vxor(); + test_vand(); + test_vor(); + test_vnot(); + + test_pred_or(false); + test_pred_or_n(true); + test_pred_and(false); + test_pred_and_n(true); + test_pred_xor(false); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} From 62e93b084c57d80770d0e3f31f627377edc99237 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 3 Nov 2021 15:41:37 -0500 Subject: [PATCH 1188/1334] Hexagon HVX (tests/tcg/hexagon) scatter_gather test Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- tests/tcg/hexagon/scatter_gather.c | 1011 ++++++++++++++++++++++++++++ 1 file changed, 1011 insertions(+) create mode 100644 tests/tcg/hexagon/scatter_gather.c diff --git a/tests/tcg/hexagon/scatter_gather.c b/tests/tcg/hexagon/scatter_gather.c new file mode 100644 index 0000000000..b93eb18133 --- /dev/null +++ b/tests/tcg/hexagon/scatter_gather.c @@ -0,0 +1,1011 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * This example tests the HVX scatter/gather instructions + * + * See section 5.13 of the V68 HVX Programmer's Reference + * + * There are 3 main classes operations + * _16 16-bit elements and 16-bit offsets + * _32 32-bit elements and 32-bit offsets + * _16_32 16-bit elements and 32-bit offsets + * + * There are also masked and accumulate versions + */ + +#include +#include +#include +#include + +typedef long HVX_Vector __attribute__((__vector_size__(128))) + __attribute__((aligned(128))); +typedef long HVX_VectorPair __attribute__((__vector_size__(256))) + __attribute__((aligned(128))); +typedef long HVX_VectorPred __attribute__((__vector_size__(128))) + __attribute__((aligned(128))); + +#define VSCATTER_16(BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermh_128B((int)BASE, RGN, OFF, VALS) +#define VSCATTER_16_MASKED(MASK, BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermhq_128B(MASK, (int)BASE, RGN, OFF, VALS) +#define VSCATTER_32(BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermw_128B((int)BASE, RGN, OFF, VALS) +#define VSCATTER_32_MASKED(MASK, BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermwq_128B(MASK, (int)BASE, RGN, OFF, VALS) +#define VSCATTER_16_32(BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermhw_128B((int)BASE, RGN, OFF, VALS) +#define VSCATTER_16_32_MASKED(MASK, BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermhwq_128B(MASK, (int)BASE, RGN, OFF, VALS) +#define VSCATTER_16_ACC(BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermh_add_128B((int)BASE, RGN, OFF, VALS) +#define VSCATTER_32_ACC(BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermw_add_128B((int)BASE, RGN, OFF, VALS) +#define VSCATTER_16_32_ACC(BASE, RGN, OFF, VALS) \ + __builtin_HEXAGON_V6_vscattermhw_add_128B((int)BASE, RGN, OFF, VALS) + +#define VGATHER_16(DSTADDR, BASE, RGN, OFF) \ + __builtin_HEXAGON_V6_vgathermh_128B(DSTADDR, (int)BASE, RGN, OFF) +#define VGATHER_16_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ + __builtin_HEXAGON_V6_vgathermhq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) +#define VGATHER_32(DSTADDR, BASE, RGN, OFF) \ + __builtin_HEXAGON_V6_vgathermw_128B(DSTADDR, (int)BASE, RGN, OFF) +#define VGATHER_32_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ + __builtin_HEXAGON_V6_vgathermwq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) +#define VGATHER_16_32(DSTADDR, BASE, RGN, OFF) \ + __builtin_HEXAGON_V6_vgathermhw_128B(DSTADDR, (int)BASE, RGN, OFF) +#define VGATHER_16_32_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ + __builtin_HEXAGON_V6_vgathermhwq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) + +#define VSHUFF_H(V) \ + __builtin_HEXAGON_V6_vshuffh_128B(V) +#define VSPLAT_H(X) \ + __builtin_HEXAGON_V6_lvsplath_128B(X) +#define VAND_VAL(PRED, VAL) \ + __builtin_HEXAGON_V6_vandvrt_128B(PRED, VAL) +#define VDEAL_H(V) \ + __builtin_HEXAGON_V6_vdealh_128B(V) + +int err; + +/* define the number of rows/cols in a square matrix */ +#define MATRIX_SIZE 64 + +/* define the size of the scatter buffer */ +#define SCATTER_BUFFER_SIZE (MATRIX_SIZE * MATRIX_SIZE) + +/* fake vtcm - put buffers together and force alignment */ +static struct { + unsigned short vscatter16[SCATTER_BUFFER_SIZE]; + unsigned short vgather16[MATRIX_SIZE]; + unsigned int vscatter32[SCATTER_BUFFER_SIZE]; + unsigned int vgather32[MATRIX_SIZE]; + unsigned short vscatter16_32[SCATTER_BUFFER_SIZE]; + unsigned short vgather16_32[MATRIX_SIZE]; +} vtcm __attribute__((aligned(0x10000))); + +/* declare the arrays of reference values */ +unsigned short vscatter16_ref[SCATTER_BUFFER_SIZE]; +unsigned short vgather16_ref[MATRIX_SIZE]; +unsigned int vscatter32_ref[SCATTER_BUFFER_SIZE]; +unsigned int vgather32_ref[MATRIX_SIZE]; +unsigned short vscatter16_32_ref[SCATTER_BUFFER_SIZE]; +unsigned short vgather16_32_ref[MATRIX_SIZE]; + +/* declare the arrays of offsets */ +unsigned short half_offsets[MATRIX_SIZE]; +unsigned int word_offsets[MATRIX_SIZE]; + +/* declare the arrays of values */ +unsigned short half_values[MATRIX_SIZE]; +unsigned short half_values_acc[MATRIX_SIZE]; +unsigned short half_values_masked[MATRIX_SIZE]; +unsigned int word_values[MATRIX_SIZE]; +unsigned int word_values_acc[MATRIX_SIZE]; +unsigned int word_values_masked[MATRIX_SIZE]; + +/* declare the arrays of predicates */ +unsigned short half_predicates[MATRIX_SIZE]; +unsigned int word_predicates[MATRIX_SIZE]; + +/* make this big enough for all the intrinsics */ +const size_t region_len = sizeof(vtcm); + +/* optionally add sync instructions */ +#define SYNC_VECTOR 1 + +static void sync_scatter(void *addr) +{ +#if SYNC_VECTOR + /* + * Do the scatter release followed by a dummy load to complete the + * synchronization. Normally the dummy load would be deferred as + * long as possible to minimize stalls. + */ + asm volatile("vmem(%0 + #0):scatter_release\n" : : "r"(addr)); + /* use volatile to force the load */ + volatile HVX_Vector vDummy = *(HVX_Vector *)addr; vDummy = vDummy; +#endif +} + +static void sync_gather(void *addr) +{ +#if SYNC_VECTOR + /* use volatile to force the load */ + volatile HVX_Vector vDummy = *(HVX_Vector *)addr; vDummy = vDummy; +#endif +} + +/* optionally print the results */ +#define PRINT_DATA 0 + +#define FILL_CHAR '.' + +/* fill vtcm scratch with ee */ +void prefill_vtcm_scratch(void) +{ + memset(&vtcm, FILL_CHAR, sizeof(vtcm)); +} + +/* create byte offsets to be a diagonal of the matrix with 16 bit elements */ +void create_offsets_values_preds_16(void) +{ + unsigned short half_element = 0; + unsigned short half_element_masked = 0; + char letter = 'A'; + char letter_masked = '@'; + + for (int i = 0; i < MATRIX_SIZE; i++) { + half_offsets[i] = i * (2 * MATRIX_SIZE + 2); + + half_element = 0; + half_element_masked = 0; + for (int j = 0; j < 2; j++) { + half_element |= letter << j * 8; + half_element_masked |= letter_masked << j * 8; + } + + half_values[i] = half_element; + half_values_acc[i] = ((i % 10) << 8) + (i % 10); + half_values_masked[i] = half_element_masked; + + letter++; + /* reset to 'A' */ + if (letter == 'M') { + letter = 'A'; + } + + half_predicates[i] = (i % 3 == 0 || i % 5 == 0) ? ~0 : 0; + } +} + +/* create byte offsets to be a diagonal of the matrix with 32 bit elements */ +void create_offsets_values_preds_32(void) +{ + unsigned int word_element = 0; + unsigned int word_element_masked = 0; + char letter = 'A'; + char letter_masked = '&'; + + for (int i = 0; i < MATRIX_SIZE; i++) { + word_offsets[i] = i * (4 * MATRIX_SIZE + 4); + + word_element = 0; + word_element_masked = 0; + for (int j = 0; j < 4; j++) { + word_element |= letter << j * 8; + word_element_masked |= letter_masked << j * 8; + } + + word_values[i] = word_element; + word_values_acc[i] = ((i % 10) << 8) + (i % 10); + word_values_masked[i] = word_element_masked; + + letter++; + /* reset to 'A' */ + if (letter == 'M') { + letter = 'A'; + } + + word_predicates[i] = (i % 4 == 0 || i % 7 == 0) ? ~0 : 0; + } +} + +/* + * create byte offsets to be a diagonal of the matrix with 16 bit elements + * and 32 bit offsets + */ +void create_offsets_values_preds_16_32(void) +{ + unsigned short half_element = 0; + unsigned short half_element_masked = 0; + char letter = 'D'; + char letter_masked = '$'; + + for (int i = 0; i < MATRIX_SIZE; i++) { + word_offsets[i] = i * (2 * MATRIX_SIZE + 2); + + half_element = 0; + half_element_masked = 0; + for (int j = 0; j < 2; j++) { + half_element |= letter << j * 8; + half_element_masked |= letter_masked << j * 8; + } + + half_values[i] = half_element; + half_values_acc[i] = ((i % 10) << 8) + (i % 10); + half_values_masked[i] = half_element_masked; + + letter++; + /* reset to 'A' */ + if (letter == 'P') { + letter = 'D'; + } + + half_predicates[i] = (i % 2 == 0 || i % 13 == 0) ? ~0 : 0; + } +} + +/* scatter the 16 bit elements using intrinsics */ +void vector_scatter_16(void) +{ + /* copy the offsets and values to vectors */ + HVX_Vector offsets = *(HVX_Vector *)half_offsets; + HVX_Vector values = *(HVX_Vector *)half_values; + + VSCATTER_16(&vtcm.vscatter16, region_len, offsets, values); + + sync_scatter(vtcm.vscatter16); +} + +/* scatter-accumulate the 16 bit elements using intrinsics */ +void vector_scatter_16_acc(void) +{ + /* copy the offsets and values to vectors */ + HVX_Vector offsets = *(HVX_Vector *)half_offsets; + HVX_Vector values = *(HVX_Vector *)half_values_acc; + + VSCATTER_16_ACC(&vtcm.vscatter16, region_len, offsets, values); + + sync_scatter(vtcm.vscatter16); +} + +/* scatter the 16 bit elements using intrinsics */ +void vector_scatter_16_masked(void) +{ + /* copy the offsets and values to vectors */ + HVX_Vector offsets = *(HVX_Vector *)half_offsets; + HVX_Vector values = *(HVX_Vector *)half_values_masked; + HVX_Vector pred_reg = *(HVX_Vector *)half_predicates; + HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); + + VSCATTER_16_MASKED(preds, &vtcm.vscatter16, region_len, offsets, values); + + sync_scatter(vtcm.vscatter16); +} + +/* scatter the 32 bit elements using intrinsics */ +void vector_scatter_32(void) +{ + /* copy the offsets and values to vectors */ + HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; + HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector valueslo = *(HVX_Vector *)word_values; + HVX_Vector valueshi = *(HVX_Vector *)&word_values[MATRIX_SIZE / 2]; + + VSCATTER_32(&vtcm.vscatter32, region_len, offsetslo, valueslo); + VSCATTER_32(&vtcm.vscatter32, region_len, offsetshi, valueshi); + + sync_scatter(vtcm.vscatter32); +} + +/* scatter-acc the 32 bit elements using intrinsics */ +void vector_scatter_32_acc(void) +{ + /* copy the offsets and values to vectors */ + HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; + HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector valueslo = *(HVX_Vector *)word_values_acc; + HVX_Vector valueshi = *(HVX_Vector *)&word_values_acc[MATRIX_SIZE / 2]; + + VSCATTER_32_ACC(&vtcm.vscatter32, region_len, offsetslo, valueslo); + VSCATTER_32_ACC(&vtcm.vscatter32, region_len, offsetshi, valueshi); + + sync_scatter(vtcm.vscatter32); +} + +/* scatter the 32 bit elements using intrinsics */ +void vector_scatter_32_masked(void) +{ + /* copy the offsets and values to vectors */ + HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; + HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector valueslo = *(HVX_Vector *)word_values_masked; + HVX_Vector valueshi = *(HVX_Vector *)&word_values_masked[MATRIX_SIZE / 2]; + HVX_Vector pred_reglo = *(HVX_Vector *)word_predicates; + HVX_Vector pred_reghi = *(HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; + HVX_VectorPred predslo = VAND_VAL(pred_reglo, ~0); + HVX_VectorPred predshi = VAND_VAL(pred_reghi, ~0); + + VSCATTER_32_MASKED(predslo, &vtcm.vscatter32, region_len, offsetslo, + valueslo); + VSCATTER_32_MASKED(predshi, &vtcm.vscatter32, region_len, offsetshi, + valueshi); + + sync_scatter(vtcm.vscatter16); +} + +/* scatter the 16 bit elements with 32 bit offsets using intrinsics */ +void vector_scatter_16_32(void) +{ + HVX_VectorPair offsets; + HVX_Vector values; + + /* get the word offsets in a vector pair */ + offsets = *(HVX_VectorPair *)word_offsets; + + /* these values need to be shuffled for the scatter */ + values = *(HVX_Vector *)half_values; + values = VSHUFF_H(values); + + VSCATTER_16_32(&vtcm.vscatter16_32, region_len, offsets, values); + + sync_scatter(vtcm.vscatter16_32); +} + +/* scatter-acc the 16 bit elements with 32 bit offsets using intrinsics */ +void vector_scatter_16_32_acc(void) +{ + HVX_VectorPair offsets; + HVX_Vector values; + + /* get the word offsets in a vector pair */ + offsets = *(HVX_VectorPair *)word_offsets; + + /* these values need to be shuffled for the scatter */ + values = *(HVX_Vector *)half_values_acc; + values = VSHUFF_H(values); + + VSCATTER_16_32_ACC(&vtcm.vscatter16_32, region_len, offsets, values); + + sync_scatter(vtcm.vscatter16_32); +} + +/* masked scatter the 16 bit elements with 32 bit offsets using intrinsics */ +void vector_scatter_16_32_masked(void) +{ + HVX_VectorPair offsets; + HVX_Vector values; + HVX_Vector pred_reg; + + /* get the word offsets in a vector pair */ + offsets = *(HVX_VectorPair *)word_offsets; + + /* these values need to be shuffled for the scatter */ + values = *(HVX_Vector *)half_values_masked; + values = VSHUFF_H(values); + + pred_reg = *(HVX_Vector *)half_predicates; + pred_reg = VSHUFF_H(pred_reg); + HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); + + VSCATTER_16_32_MASKED(preds, &vtcm.vscatter16_32, region_len, offsets, + values); + + sync_scatter(vtcm.vscatter16_32); +} + +/* gather the elements from the scatter16 buffer */ +void vector_gather_16(void) +{ + HVX_Vector *vgather = (HVX_Vector *)&vtcm.vgather16; + HVX_Vector offsets = *(HVX_Vector *)half_offsets; + + VGATHER_16(vgather, &vtcm.vscatter16, region_len, offsets); + + sync_gather(vgather); +} + +static unsigned short gather_16_masked_init(void) +{ + char letter = '?'; + return letter | (letter << 8); +} + +void vector_gather_16_masked(void) +{ + HVX_Vector *vgather = (HVX_Vector *)&vtcm.vgather16; + HVX_Vector offsets = *(HVX_Vector *)half_offsets; + HVX_Vector pred_reg = *(HVX_Vector *)half_predicates; + HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); + + *vgather = VSPLAT_H(gather_16_masked_init()); + VGATHER_16_MASKED(vgather, preds, &vtcm.vscatter16, region_len, offsets); + + sync_gather(vgather); +} + +/* gather the elements from the scatter32 buffer */ +void vector_gather_32(void) +{ + HVX_Vector *vgatherlo = (HVX_Vector *)&vtcm.vgather32; + HVX_Vector *vgatherhi = + (HVX_Vector *)((int)&vtcm.vgather32 + (MATRIX_SIZE * 2)); + HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; + HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + + VGATHER_32(vgatherlo, &vtcm.vscatter32, region_len, offsetslo); + VGATHER_32(vgatherhi, &vtcm.vscatter32, region_len, offsetshi); + + sync_gather(vgatherhi); +} + +static unsigned int gather_32_masked_init(void) +{ + char letter = '?'; + return letter | (letter << 8) | (letter << 16) | (letter << 24); +} + +void vector_gather_32_masked(void) +{ + HVX_Vector *vgatherlo = (HVX_Vector *)&vtcm.vgather32; + HVX_Vector *vgatherhi = + (HVX_Vector *)((int)&vtcm.vgather32 + (MATRIX_SIZE * 2)); + HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; + HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector pred_reglo = *(HVX_Vector *)word_predicates; + HVX_VectorPred predslo = VAND_VAL(pred_reglo, ~0); + HVX_Vector pred_reghi = *(HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; + HVX_VectorPred predshi = VAND_VAL(pred_reghi, ~0); + + *vgatherlo = VSPLAT_H(gather_32_masked_init()); + *vgatherhi = VSPLAT_H(gather_32_masked_init()); + VGATHER_32_MASKED(vgatherlo, predslo, &vtcm.vscatter32, region_len, + offsetslo); + VGATHER_32_MASKED(vgatherhi, predshi, &vtcm.vscatter32, region_len, + offsetshi); + + sync_gather(vgatherlo); + sync_gather(vgatherhi); +} + +/* gather the elements from the scatter16_32 buffer */ +void vector_gather_16_32(void) +{ + HVX_Vector *vgather; + HVX_VectorPair offsets; + HVX_Vector values; + + /* get the vtcm address to gather from */ + vgather = (HVX_Vector *)&vtcm.vgather16_32; + + /* get the word offsets in a vector pair */ + offsets = *(HVX_VectorPair *)word_offsets; + + VGATHER_16_32(vgather, &vtcm.vscatter16_32, region_len, offsets); + + /* deal the elements to get the order back */ + values = *(HVX_Vector *)vgather; + values = VDEAL_H(values); + + /* write it back to vtcm address */ + *(HVX_Vector *)vgather = values; +} + +void vector_gather_16_32_masked(void) +{ + HVX_Vector *vgather; + HVX_VectorPair offsets; + HVX_Vector pred_reg; + HVX_VectorPred preds; + HVX_Vector values; + + /* get the vtcm address to gather from */ + vgather = (HVX_Vector *)&vtcm.vgather16_32; + + /* get the word offsets in a vector pair */ + offsets = *(HVX_VectorPair *)word_offsets; + pred_reg = *(HVX_Vector *)half_predicates; + pred_reg = VSHUFF_H(pred_reg); + preds = VAND_VAL(pred_reg, ~0); + + *vgather = VSPLAT_H(gather_16_masked_init()); + VGATHER_16_32_MASKED(vgather, preds, &vtcm.vscatter16_32, region_len, + offsets); + + /* deal the elements to get the order back */ + values = *(HVX_Vector *)vgather; + values = VDEAL_H(values); + + /* write it back to vtcm address */ + *(HVX_Vector *)vgather = values; +} + +static void check_buffer(const char *name, void *c, void *r, size_t size) +{ + char *check = (char *)c; + char *ref = (char *)r; + for (int i = 0; i < size; i++) { + if (check[i] != ref[i]) { + printf("ERROR %s [%d]: 0x%x (%c) != 0x%x (%c)\n", name, i, + check[i], check[i], ref[i], ref[i]); + err++; + } + } +} + +/* + * These scalar functions are the C equivalents of the vector functions that + * use HVX + */ + +/* scatter the 16 bit elements using C */ +void scalar_scatter_16(unsigned short *vscatter16) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vscatter16[half_offsets[i] / 2] = half_values[i]; + } +} + +void check_scatter_16() +{ + memset(vscatter16_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); + scalar_scatter_16(vscatter16_ref); + check_buffer(__func__, vtcm.vscatter16, vscatter16_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); +} + +/* scatter the 16 bit elements using C */ +void scalar_scatter_16_acc(unsigned short *vscatter16) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vscatter16[half_offsets[i] / 2] += half_values_acc[i]; + } +} + +void check_scatter_16_acc() +{ + memset(vscatter16_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); + scalar_scatter_16(vscatter16_ref); + scalar_scatter_16_acc(vscatter16_ref); + check_buffer(__func__, vtcm.vscatter16, vscatter16_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); +} + +/* scatter the 16 bit elements using C */ +void scalar_scatter_16_masked(unsigned short *vscatter16) +{ + for (int i = 0; i < MATRIX_SIZE; i++) { + if (half_predicates[i]) { + vscatter16[half_offsets[i] / 2] = half_values_masked[i]; + } + } + +} + +void check_scatter_16_masked() +{ + memset(vscatter16_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); + scalar_scatter_16(vscatter16_ref); + scalar_scatter_16_acc(vscatter16_ref); + scalar_scatter_16_masked(vscatter16_ref); + check_buffer(__func__, vtcm.vscatter16, vscatter16_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); +} + +/* scatter the 32 bit elements using C */ +void scalar_scatter_32(unsigned int *vscatter32) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vscatter32[word_offsets[i] / 4] = word_values[i]; + } +} + +void check_scatter_32() +{ + memset(vscatter32_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned int)); + scalar_scatter_32(vscatter32_ref); + check_buffer(__func__, vtcm.vscatter32, vscatter32_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned int)); +} + +/* scatter the 32 bit elements using C */ +void scalar_scatter_32_acc(unsigned int *vscatter32) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vscatter32[word_offsets[i] / 4] += word_values_acc[i]; + } +} + +void check_scatter_32_acc() +{ + memset(vscatter32_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned int)); + scalar_scatter_32(vscatter32_ref); + scalar_scatter_32_acc(vscatter32_ref); + check_buffer(__func__, vtcm.vscatter32, vscatter32_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned int)); +} + +/* scatter the 32 bit elements using C */ +void scalar_scatter_32_masked(unsigned int *vscatter32) +{ + for (int i = 0; i < MATRIX_SIZE; i++) { + if (word_predicates[i]) { + vscatter32[word_offsets[i] / 4] = word_values_masked[i]; + } + } +} + +void check_scatter_32_masked() +{ + memset(vscatter32_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned int)); + scalar_scatter_32(vscatter32_ref); + scalar_scatter_32_acc(vscatter32_ref); + scalar_scatter_32_masked(vscatter32_ref); + check_buffer(__func__, vtcm.vscatter32, vscatter32_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned int)); +} + +/* scatter the 32 bit elements using C */ +void scalar_scatter_16_32(unsigned short *vscatter16_32) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vscatter16_32[word_offsets[i] / 2] = half_values[i]; + } +} + +void check_scatter_16_32() +{ + memset(vscatter16_32_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); + scalar_scatter_16_32(vscatter16_32_ref); + check_buffer(__func__, vtcm.vscatter16_32, vscatter16_32_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); +} + +/* scatter the 32 bit elements using C */ +void scalar_scatter_16_32_acc(unsigned short *vscatter16_32) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vscatter16_32[word_offsets[i] / 2] += half_values_acc[i]; + } +} + +void check_scatter_16_32_acc() +{ + memset(vscatter16_32_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); + scalar_scatter_16_32(vscatter16_32_ref); + scalar_scatter_16_32_acc(vscatter16_32_ref); + check_buffer(__func__, vtcm.vscatter16_32, vscatter16_32_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); +} + +void scalar_scatter_16_32_masked(unsigned short *vscatter16_32) +{ + for (int i = 0; i < MATRIX_SIZE; i++) { + if (half_predicates[i]) { + vscatter16_32[word_offsets[i] / 2] = half_values_masked[i]; + } + } +} + +void check_scatter_16_32_masked() +{ + memset(vscatter16_32_ref, FILL_CHAR, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); + scalar_scatter_16_32(vscatter16_32_ref); + scalar_scatter_16_32_acc(vscatter16_32_ref); + scalar_scatter_16_32_masked(vscatter16_32_ref); + check_buffer(__func__, vtcm.vscatter16_32, vscatter16_32_ref, + SCATTER_BUFFER_SIZE * sizeof(unsigned short)); +} + +/* gather the elements from the scatter buffer using C */ +void scalar_gather_16(unsigned short *vgather16) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vgather16[i] = vtcm.vscatter16[half_offsets[i] / 2]; + } +} + +void check_gather_16() +{ + memset(vgather16_ref, 0, MATRIX_SIZE * sizeof(unsigned short)); + scalar_gather_16(vgather16_ref); + check_buffer(__func__, vtcm.vgather16, vgather16_ref, + MATRIX_SIZE * sizeof(unsigned short)); +} + +void scalar_gather_16_masked(unsigned short *vgather16) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + if (half_predicates[i]) { + vgather16[i] = vtcm.vscatter16[half_offsets[i] / 2]; + } + } +} + +void check_gather_16_masked() +{ + memset(vgather16_ref, gather_16_masked_init(), + MATRIX_SIZE * sizeof(unsigned short)); + scalar_gather_16_masked(vgather16_ref); + check_buffer(__func__, vtcm.vgather16, vgather16_ref, + MATRIX_SIZE * sizeof(unsigned short)); +} + +/* gather the elements from the scatter buffer using C */ +void scalar_gather_32(unsigned int *vgather32) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vgather32[i] = vtcm.vscatter32[word_offsets[i] / 4]; + } +} + +void check_gather_32(void) +{ + memset(vgather32_ref, 0, MATRIX_SIZE * sizeof(unsigned int)); + scalar_gather_32(vgather32_ref); + check_buffer(__func__, vtcm.vgather32, vgather32_ref, + MATRIX_SIZE * sizeof(unsigned int)); +} + +void scalar_gather_32_masked(unsigned int *vgather32) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + if (word_predicates[i]) { + vgather32[i] = vtcm.vscatter32[word_offsets[i] / 4]; + } + } +} + + +void check_gather_32_masked(void) +{ + memset(vgather32_ref, gather_32_masked_init(), + MATRIX_SIZE * sizeof(unsigned int)); + scalar_gather_32_masked(vgather32_ref); + check_buffer(__func__, vtcm.vgather32, + vgather32_ref, MATRIX_SIZE * sizeof(unsigned int)); +} + +/* gather the elements from the scatter buffer using C */ +void scalar_gather_16_32(unsigned short *vgather16_32) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + vgather16_32[i] = vtcm.vscatter16_32[word_offsets[i] / 2]; + } +} + +void check_gather_16_32(void) +{ + memset(vgather16_32_ref, 0, MATRIX_SIZE * sizeof(unsigned short)); + scalar_gather_16_32(vgather16_32_ref); + check_buffer(__func__, vtcm.vgather16_32, vgather16_32_ref, + MATRIX_SIZE * sizeof(unsigned short)); +} + +void scalar_gather_16_32_masked(unsigned short *vgather16_32) +{ + for (int i = 0; i < MATRIX_SIZE; ++i) { + if (half_predicates[i]) { + vgather16_32[i] = vtcm.vscatter16_32[word_offsets[i] / 2]; + } + } + +} + +void check_gather_16_32_masked(void) +{ + memset(vgather16_32_ref, gather_16_masked_init(), + MATRIX_SIZE * sizeof(unsigned short)); + scalar_gather_16_32_masked(vgather16_32_ref); + check_buffer(__func__, vtcm.vgather16_32, vgather16_32_ref, + MATRIX_SIZE * sizeof(unsigned short)); +} + +/* print scatter16 buffer */ +void print_scatter16_buffer(void) +{ + if (PRINT_DATA) { + printf("\n\nPrinting the 16 bit scatter buffer"); + + for (int i = 0; i < SCATTER_BUFFER_SIZE; i++) { + if ((i % MATRIX_SIZE) == 0) { + printf("\n"); + } + for (int j = 0; j < 2; j++) { + printf("%c", (char)((vtcm.vscatter16[i] >> j * 8) & 0xff)); + } + printf(" "); + } + printf("\n"); + } +} + +/* print the gather 16 buffer */ +void print_gather_result_16(void) +{ + if (PRINT_DATA) { + printf("\n\nPrinting the 16 bit gather result\n"); + + for (int i = 0; i < MATRIX_SIZE; i++) { + for (int j = 0; j < 2; j++) { + printf("%c", (char)((vtcm.vgather16[i] >> j * 8) & 0xff)); + } + printf(" "); + } + printf("\n"); + } +} + +/* print the scatter32 buffer */ +void print_scatter32_buffer(void) +{ + if (PRINT_DATA) { + printf("\n\nPrinting the 32 bit scatter buffer"); + + for (int i = 0; i < SCATTER_BUFFER_SIZE; i++) { + if ((i % MATRIX_SIZE) == 0) { + printf("\n"); + } + for (int j = 0; j < 4; j++) { + printf("%c", (char)((vtcm.vscatter32[i] >> j * 8) & 0xff)); + } + printf(" "); + } + printf("\n"); + } +} + +/* print the gather 32 buffer */ +void print_gather_result_32(void) +{ + if (PRINT_DATA) { + printf("\n\nPrinting the 32 bit gather result\n"); + + for (int i = 0; i < MATRIX_SIZE; i++) { + for (int j = 0; j < 4; j++) { + printf("%c", (char)((vtcm.vgather32[i] >> j * 8) & 0xff)); + } + printf(" "); + } + printf("\n"); + } +} + +/* print the scatter16_32 buffer */ +void print_scatter16_32_buffer(void) +{ + if (PRINT_DATA) { + printf("\n\nPrinting the 16_32 bit scatter buffer"); + + for (int i = 0; i < SCATTER_BUFFER_SIZE; i++) { + if ((i % MATRIX_SIZE) == 0) { + printf("\n"); + } + for (int j = 0; j < 2; j++) { + printf("%c", + (unsigned char)((vtcm.vscatter16_32[i] >> j * 8) & 0xff)); + } + printf(" "); + } + printf("\n"); + } +} + +/* print the gather 16_32 buffer */ +void print_gather_result_16_32(void) +{ + if (PRINT_DATA) { + printf("\n\nPrinting the 16_32 bit gather result\n"); + + for (int i = 0; i < MATRIX_SIZE; i++) { + for (int j = 0; j < 2; j++) { + printf("%c", + (unsigned char)((vtcm.vgather16_32[i] >> j * 8) & 0xff)); + } + printf(" "); + } + printf("\n"); + } +} + +int main() +{ + prefill_vtcm_scratch(); + + /* 16 bit elements with 16 bit offsets */ + create_offsets_values_preds_16(); + + vector_scatter_16(); + print_scatter16_buffer(); + check_scatter_16(); + + vector_gather_16(); + print_gather_result_16(); + check_gather_16(); + + vector_gather_16_masked(); + print_gather_result_16(); + check_gather_16_masked(); + + vector_scatter_16_acc(); + print_scatter16_buffer(); + check_scatter_16_acc(); + + vector_scatter_16_masked(); + print_scatter16_buffer(); + check_scatter_16_masked(); + + /* 32 bit elements with 32 bit offsets */ + create_offsets_values_preds_32(); + + vector_scatter_32(); + print_scatter32_buffer(); + check_scatter_32(); + + vector_gather_32(); + print_gather_result_32(); + check_gather_32(); + + vector_gather_32_masked(); + print_gather_result_32(); + check_gather_32_masked(); + + vector_scatter_32_acc(); + print_scatter32_buffer(); + check_scatter_32_acc(); + + vector_scatter_32_masked(); + print_scatter32_buffer(); + check_scatter_32_masked(); + + /* 16 bit elements with 32 bit offsets */ + create_offsets_values_preds_16_32(); + + vector_scatter_16_32(); + print_scatter16_32_buffer(); + check_scatter_16_32(); + + vector_gather_16_32(); + print_gather_result_16_32(); + check_gather_16_32(); + + vector_gather_16_32_masked(); + print_gather_result_16_32(); + check_gather_16_32_masked(); + + vector_scatter_16_32_acc(); + print_scatter16_32_buffer(); + check_scatter_16_32_acc(); + + vector_scatter_16_32_masked(); + print_scatter16_32_buffer(); + check_scatter_16_32_masked(); + + puts(err ? "FAIL" : "PASS"); + return err; +} From 49278c1b0d7ef5864d0d8ad9a950296deb8b05ae Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 3 Nov 2021 15:42:28 -0500 Subject: [PATCH 1189/1334] Hexagon HVX (tests/tcg/hexagon) histogram test Acked-by: Richard Henderson Signed-off-by: Taylor Simpson --- tests/tcg/hexagon/hvx_histogram.c | 88 +++ tests/tcg/hexagon/hvx_histogram_input.h | 717 ++++++++++++++++++++++++ tests/tcg/hexagon/hvx_histogram_row.S | 294 ++++++++++ tests/tcg/hexagon/hvx_histogram_row.h | 24 + 4 files changed, 1123 insertions(+) create mode 100644 tests/tcg/hexagon/hvx_histogram.c create mode 100644 tests/tcg/hexagon/hvx_histogram_input.h create mode 100644 tests/tcg/hexagon/hvx_histogram_row.S create mode 100644 tests/tcg/hexagon/hvx_histogram_row.h diff --git a/tests/tcg/hexagon/hvx_histogram.c b/tests/tcg/hexagon/hvx_histogram.c new file mode 100644 index 0000000000..43377a9abb --- /dev/null +++ b/tests/tcg/hexagon/hvx_histogram.c @@ -0,0 +1,88 @@ +/* + * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include "hvx_histogram_row.h" + +const int vector_len = 128; +const int width = 275; +const int height = 20; +const int stride = (width + vector_len - 1) & -vector_len; + +int err; + +static uint8_t input[height][stride] __attribute__((aligned(128))) = { +#include "hvx_histogram_input.h" +}; + +static int result[256] __attribute__((aligned(128))); +static int expect[256] __attribute__((aligned(128))); + +static void check(void) +{ + for (int i = 0; i < 256; i++) { + int res = result[i]; + int exp = expect[i]; + if (res != exp) { + printf("ERROR at %3d: 0x%04x != 0x%04x\n", + i, res, exp); + err++; + } + } +} + +static void ref_histogram(uint8_t *src, int stride, int width, int height, + int *hist) +{ + for (int i = 0; i < 256; i++) { + hist[i] = 0; + } + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + hist[src[i * stride + j]]++; + } + } +} + +static void hvx_histogram(uint8_t *src, int stride, int width, int height, + int *hist) +{ + int n = 8192 / width; + + for (int i = 0; i < 256; i++) { + hist[i] = 0; + } + + for (int i = 0; i < height; i += n) { + int k = height - i > n ? n : height - i; + hvx_histogram_row(src, stride, width, k, hist); + src += n * stride; + } +} + +int main() +{ + ref_histogram(&input[0][0], stride, width, height, expect); + hvx_histogram(&input[0][0], stride, width, height, result); + check(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/hvx_histogram_input.h b/tests/tcg/hexagon/hvx_histogram_input.h new file mode 100644 index 0000000000..2f9109255e --- /dev/null +++ b/tests/tcg/hexagon/hvx_histogram_input.h @@ -0,0 +1,717 @@ +/* + * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + { 0x26, 0x32, 0x2e, 0x2e, 0x2d, 0x2c, 0x2d, 0x2d, + 0x2c, 0x2e, 0x31, 0x33, 0x36, 0x39, 0x3b, 0x3f, + 0x42, 0x46, 0x4a, 0x4c, 0x51, 0x53, 0x53, 0x54, + 0x56, 0x57, 0x58, 0x57, 0x56, 0x52, 0x51, 0x4f, + 0x4c, 0x49, 0x47, 0x42, 0x3e, 0x3b, 0x38, 0x35, + 0x33, 0x30, 0x2e, 0x2c, 0x2b, 0x2a, 0x2a, 0x28, + 0x28, 0x27, 0x27, 0x28, 0x29, 0x2a, 0x2c, 0x2e, + 0x2f, 0x33, 0x36, 0x38, 0x3c, 0x3d, 0x40, 0x42, + 0x43, 0x42, 0x43, 0x44, 0x43, 0x41, 0x40, 0x3b, + 0x3b, 0x3a, 0x38, 0x35, 0x32, 0x2f, 0x2c, 0x29, + 0x27, 0x26, 0x23, 0x21, 0x1e, 0x1c, 0x1a, 0x19, + 0x17, 0x15, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x0f, 0x0e, 0x0f, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, + 0x0c, 0x0d, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0d, 0x0c, 0x0f, 0x0e, 0x0f, 0x0f, + 0x0f, 0x10, 0x11, 0x12, 0x14, 0x16, 0x17, 0x19, + 0x1c, 0x1d, 0x21, 0x25, 0x27, 0x29, 0x2b, 0x2f, + 0x31, 0x33, 0x36, 0x38, 0x39, 0x3a, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3e, 0x3e, 0x3c, 0x3b, 0x3a, 0x39, + 0x39, 0x3a, 0x3a, 0x3a, 0x3a, 0x3c, 0x3e, 0x43, + 0x47, 0x4a, 0x4d, 0x51, 0x51, 0x54, 0x56, 0x56, + 0x57, 0x56, 0x53, 0x4f, 0x4b, 0x47, 0x43, 0x41, + 0x3e, 0x3c, 0x3a, 0x37, 0x36, 0x33, 0x32, 0x34, + 0x34, 0x34, 0x34, 0x35, 0x36, 0x39, 0x3d, 0x3d, + 0x3f, 0x40, 0x40, 0x40, 0x40, 0x3e, 0x40, 0x40, + 0x42, 0x44, 0x47, 0x48, 0x4b, 0x4e, 0x56, 0x5c, + 0x62, 0x68, 0x6f, 0x73, 0x76, 0x79, 0x7a, 0x7c, + 0x7e, 0x7c, 0x78, 0x72, 0x6e, 0x69, 0x65, 0x60, + 0x5b, 0x56, 0x52, 0x4d, 0x4a, 0x48, 0x47, 0x46, + 0x44, 0x43, 0x42, 0x41, 0x41, 0x41, 0x40, 0x40, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3b, 0x38, 0x37, + 0x36, 0x35, 0x36, 0x35, 0x36, 0x37, 0x38, 0x3c, + 0x3d, 0x3f, 0x42, 0x44, 0x46, 0x48, 0x4b, 0x4c, + 0x4e, 0x4e, 0x4d, 0x4c, 0x4a, 0x48, 0x49, 0x49, + 0x4b, 0x4d, 0x4e, }, + { 0x23, 0x2d, 0x29, 0x29, 0x28, 0x28, 0x29, 0x29, + 0x28, 0x2b, 0x2d, 0x2f, 0x32, 0x34, 0x36, 0x3a, + 0x3d, 0x41, 0x44, 0x47, 0x4a, 0x4c, 0x4e, 0x4e, + 0x50, 0x51, 0x51, 0x51, 0x4f, 0x4c, 0x4b, 0x48, + 0x46, 0x44, 0x40, 0x3d, 0x39, 0x36, 0x34, 0x30, + 0x2f, 0x2d, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, + 0x25, 0x24, 0x24, 0x24, 0x26, 0x28, 0x28, 0x2a, + 0x2b, 0x2e, 0x32, 0x34, 0x37, 0x39, 0x3b, 0x3c, + 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3c, 0x3b, 0x38, + 0x37, 0x35, 0x33, 0x30, 0x2e, 0x2b, 0x27, 0x25, + 0x24, 0x21, 0x20, 0x1d, 0x1b, 0x1a, 0x18, 0x16, + 0x15, 0x14, 0x13, 0x12, 0x10, 0x11, 0x10, 0x0e, + 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, + 0x0b, 0x0b, 0x0c, 0x0b, 0x0b, 0x09, 0x0a, 0x0b, + 0x0b, 0x0a, 0x0a, 0x0c, 0x0c, 0x0c, 0x0d, 0x0e, + 0x0e, 0x0f, 0x0f, 0x11, 0x12, 0x15, 0x15, 0x17, + 0x1a, 0x1c, 0x1f, 0x22, 0x25, 0x26, 0x29, 0x2a, + 0x2d, 0x30, 0x33, 0x34, 0x35, 0x35, 0x37, 0x37, + 0x39, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x36, 0x37, + 0x35, 0x36, 0x35, 0x35, 0x36, 0x37, 0x3a, 0x3e, + 0x40, 0x43, 0x48, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, + 0x4f, 0x4f, 0x4c, 0x48, 0x45, 0x41, 0x3e, 0x3b, + 0x3a, 0x37, 0x36, 0x33, 0x32, 0x31, 0x30, 0x31, + 0x32, 0x31, 0x31, 0x31, 0x31, 0x34, 0x37, 0x38, + 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3b, 0x3d, 0x3e, + 0x3f, 0x40, 0x43, 0x44, 0x47, 0x4b, 0x4f, 0x56, + 0x5a, 0x60, 0x66, 0x69, 0x6a, 0x6e, 0x71, 0x72, + 0x73, 0x72, 0x6d, 0x69, 0x66, 0x60, 0x5c, 0x59, + 0x54, 0x50, 0x4d, 0x48, 0x46, 0x44, 0x44, 0x43, + 0x42, 0x41, 0x41, 0x40, 0x3f, 0x3f, 0x3e, 0x3d, + 0x3d, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x35, 0x35, + 0x34, 0x34, 0x35, 0x34, 0x35, 0x36, 0x39, 0x3c, + 0x3d, 0x3e, 0x41, 0x43, 0x44, 0x46, 0x48, 0x49, + 0x4a, 0x49, 0x48, 0x47, 0x45, 0x43, 0x43, 0x44, + 0x45, 0x47, 0x48, }, + { 0x23, 0x2d, 0x2a, 0x2a, 0x29, 0x29, 0x2a, 0x2a, + 0x29, 0x2c, 0x2d, 0x2f, 0x32, 0x34, 0x36, 0x3a, + 0x3d, 0x40, 0x44, 0x48, 0x4a, 0x4c, 0x4e, 0x4e, + 0x50, 0x51, 0x51, 0x51, 0x4f, 0x4c, 0x4b, 0x48, + 0x46, 0x44, 0x40, 0x3d, 0x39, 0x36, 0x34, 0x30, + 0x2f, 0x2d, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, + 0x25, 0x24, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, + 0x2b, 0x2e, 0x31, 0x34, 0x37, 0x39, 0x3b, 0x3c, + 0x3d, 0x3e, 0x3e, 0x3d, 0x3e, 0x3c, 0x3c, 0x3a, + 0x37, 0x35, 0x33, 0x30, 0x2f, 0x2b, 0x28, 0x26, + 0x24, 0x21, 0x20, 0x1e, 0x1c, 0x1b, 0x18, 0x17, + 0x16, 0x14, 0x13, 0x12, 0x10, 0x10, 0x0f, 0x0e, + 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, + 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0c, + 0x0c, 0x0b, 0x0b, 0x0c, 0x0d, 0x0c, 0x0e, 0x0e, + 0x0e, 0x0f, 0x11, 0x11, 0x13, 0x14, 0x16, 0x18, + 0x1a, 0x1d, 0x1f, 0x22, 0x25, 0x26, 0x29, 0x2b, + 0x2d, 0x31, 0x33, 0x34, 0x36, 0x37, 0x38, 0x38, + 0x39, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x37, 0x37, + 0x35, 0x36, 0x35, 0x36, 0x35, 0x38, 0x3a, 0x3e, + 0x40, 0x41, 0x45, 0x47, 0x49, 0x4a, 0x4c, 0x4d, + 0x4e, 0x4d, 0x4a, 0x47, 0x44, 0x40, 0x3d, 0x3b, + 0x39, 0x37, 0x34, 0x34, 0x32, 0x31, 0x31, 0x33, + 0x32, 0x31, 0x32, 0x33, 0x32, 0x36, 0x38, 0x39, + 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3e, 0x3e, + 0x41, 0x42, 0x43, 0x45, 0x48, 0x4c, 0x50, 0x56, + 0x5b, 0x5f, 0x62, 0x67, 0x69, 0x6c, 0x6e, 0x6e, + 0x70, 0x6f, 0x6b, 0x67, 0x63, 0x5e, 0x5b, 0x58, + 0x54, 0x51, 0x4e, 0x4a, 0x48, 0x46, 0x46, 0x46, + 0x45, 0x46, 0x44, 0x43, 0x44, 0x43, 0x42, 0x42, + 0x41, 0x40, 0x3f, 0x3e, 0x3c, 0x3b, 0x3a, 0x39, + 0x39, 0x39, 0x38, 0x37, 0x37, 0x3a, 0x3e, 0x40, + 0x42, 0x43, 0x47, 0x47, 0x48, 0x4a, 0x4b, 0x4c, + 0x4c, 0x4b, 0x4a, 0x48, 0x46, 0x44, 0x43, 0x45, + 0x45, 0x46, 0x47, }, + { 0x21, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, + 0x28, 0x2a, 0x2d, 0x30, 0x32, 0x34, 0x37, 0x3a, + 0x3c, 0x40, 0x44, 0x48, 0x4a, 0x4c, 0x4e, 0x4e, + 0x50, 0x51, 0x52, 0x51, 0x4f, 0x4b, 0x4b, 0x48, + 0x45, 0x43, 0x3f, 0x3c, 0x39, 0x36, 0x33, 0x30, + 0x2f, 0x2d, 0x2b, 0x2a, 0x28, 0x27, 0x26, 0x25, + 0x24, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2c, 0x2d, 0x31, 0x34, 0x37, 0x39, 0x3b, 0x3c, + 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3d, 0x3c, 0x3a, + 0x37, 0x35, 0x33, 0x30, 0x2f, 0x2b, 0x28, 0x26, + 0x25, 0x21, 0x20, 0x1e, 0x1c, 0x19, 0x19, 0x18, + 0x17, 0x15, 0x15, 0x12, 0x11, 0x11, 0x11, 0x0f, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, + 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x10, 0x11, 0x13, 0x13, 0x15, 0x16, 0x18, + 0x1a, 0x1c, 0x1f, 0x22, 0x25, 0x28, 0x29, 0x2d, + 0x2f, 0x32, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, + 0x39, 0x3a, 0x39, 0x39, 0x37, 0x36, 0x37, 0x36, + 0x35, 0x35, 0x37, 0x35, 0x36, 0x37, 0x3a, 0x3d, + 0x3e, 0x41, 0x43, 0x46, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x49, 0x47, 0x45, 0x42, 0x3f, 0x3d, 0x3b, + 0x3a, 0x38, 0x36, 0x34, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x31, 0x33, 0x32, 0x34, 0x37, 0x38, 0x38, + 0x3a, 0x3b, 0x3d, 0x3d, 0x3d, 0x3e, 0x3f, 0x41, + 0x42, 0x44, 0x44, 0x46, 0x49, 0x4d, 0x50, 0x54, + 0x58, 0x5c, 0x61, 0x63, 0x65, 0x69, 0x6a, 0x6c, + 0x6d, 0x6c, 0x68, 0x64, 0x61, 0x5c, 0x59, 0x57, + 0x53, 0x51, 0x4f, 0x4c, 0x4a, 0x48, 0x48, 0x49, + 0x49, 0x48, 0x48, 0x48, 0x47, 0x47, 0x46, 0x46, + 0x45, 0x44, 0x42, 0x41, 0x3f, 0x3e, 0x3c, 0x3c, + 0x3c, 0x3d, 0x3c, 0x3c, 0x3c, 0x3e, 0x41, 0x43, + 0x46, 0x48, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4e, + 0x4e, 0x4d, 0x4b, 0x49, 0x47, 0x44, 0x44, 0x45, + 0x45, 0x45, 0x46, }, + { 0x22, 0x2b, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, + 0x28, 0x2a, 0x2c, 0x2f, 0x30, 0x34, 0x37, 0x3b, + 0x3d, 0x41, 0x45, 0x48, 0x4a, 0x4c, 0x4e, 0x4e, + 0x50, 0x51, 0x52, 0x51, 0x4f, 0x4b, 0x4b, 0x47, + 0x45, 0x43, 0x3f, 0x3c, 0x39, 0x36, 0x33, 0x30, + 0x2f, 0x2d, 0x2b, 0x2a, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2c, 0x2e, 0x31, 0x34, 0x37, 0x39, 0x3a, 0x3b, + 0x3d, 0x3e, 0x3e, 0x3f, 0x3f, 0x3d, 0x3c, 0x3a, + 0x38, 0x36, 0x34, 0x31, 0x2e, 0x2c, 0x29, 0x26, + 0x25, 0x22, 0x20, 0x1e, 0x1c, 0x1a, 0x19, 0x18, + 0x16, 0x15, 0x14, 0x12, 0x10, 0x11, 0x11, 0x0f, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0c, 0x0d, 0x0c, + 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0f, 0x0f, + 0x0f, 0x10, 0x11, 0x13, 0x13, 0x15, 0x15, 0x18, + 0x19, 0x1d, 0x1f, 0x21, 0x24, 0x27, 0x2a, 0x2c, + 0x30, 0x33, 0x35, 0x36, 0x37, 0x38, 0x39, 0x39, + 0x3a, 0x3a, 0x39, 0x39, 0x37, 0x36, 0x37, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x39, 0x3a, + 0x3d, 0x3e, 0x41, 0x43, 0x43, 0x45, 0x46, 0x46, + 0x47, 0x46, 0x44, 0x42, 0x40, 0x3d, 0x3a, 0x39, + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3c, 0x3c, 0x3e, 0x3e, 0x3e, 0x41, 0x43, + 0x44, 0x45, 0x46, 0x48, 0x49, 0x4c, 0x51, 0x54, + 0x56, 0x5a, 0x5f, 0x61, 0x63, 0x65, 0x67, 0x69, + 0x6a, 0x69, 0x67, 0x61, 0x5f, 0x5b, 0x58, 0x56, + 0x54, 0x51, 0x50, 0x4e, 0x4c, 0x4a, 0x4b, 0x4c, + 0x4c, 0x4b, 0x4b, 0x4b, 0x4b, 0x49, 0x4a, 0x49, + 0x49, 0x48, 0x46, 0x44, 0x42, 0x41, 0x40, 0x3f, + 0x3f, 0x40, 0x40, 0x40, 0x40, 0x42, 0x46, 0x49, + 0x4b, 0x4c, 0x4f, 0x4f, 0x50, 0x52, 0x51, 0x51, + 0x50, 0x4f, 0x4c, 0x4a, 0x48, 0x46, 0x45, 0x44, + 0x44, 0x45, 0x46, }, + { 0x21, 0x2a, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x29, 0x2d, 0x2f, 0x31, 0x34, 0x37, 0x3b, + 0x3e, 0x41, 0x45, 0x48, 0x4a, 0x4c, 0x4e, 0x4e, + 0x50, 0x51, 0x52, 0x51, 0x4f, 0x4b, 0x4b, 0x48, + 0x45, 0x43, 0x3f, 0x3c, 0x39, 0x36, 0x33, 0x2f, + 0x2f, 0x2d, 0x2a, 0x2a, 0x27, 0x26, 0x25, 0x24, + 0x22, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2c, 0x2f, 0x31, 0x34, 0x37, 0x39, 0x3a, 0x3c, + 0x3d, 0x3e, 0x3f, 0x40, 0x3f, 0x3d, 0x3d, 0x3a, + 0x38, 0x36, 0x34, 0x31, 0x2e, 0x2c, 0x29, 0x26, + 0x25, 0x22, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x18, + 0x16, 0x14, 0x14, 0x13, 0x11, 0x11, 0x11, 0x0f, + 0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, + 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x10, 0x13, 0x13, 0x14, 0x15, 0x17, 0x19, + 0x1a, 0x1d, 0x1f, 0x22, 0x25, 0x27, 0x2a, 0x2e, + 0x31, 0x33, 0x35, 0x38, 0x39, 0x3a, 0x3b, 0x3b, + 0x3c, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x38, 0x37, + 0x36, 0x36, 0x37, 0x36, 0x37, 0x38, 0x38, 0x3a, + 0x3b, 0x3e, 0x40, 0x40, 0x41, 0x42, 0x43, 0x42, + 0x43, 0x42, 0x40, 0x40, 0x3f, 0x3c, 0x3b, 0x39, + 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x33, + 0x32, 0x32, 0x34, 0x35, 0x35, 0x36, 0x39, 0x39, + 0x3a, 0x3c, 0x3c, 0x3f, 0x40, 0x41, 0x43, 0x45, + 0x45, 0x47, 0x48, 0x4a, 0x4b, 0x4d, 0x50, 0x53, + 0x56, 0x59, 0x5c, 0x5f, 0x60, 0x65, 0x64, 0x66, + 0x68, 0x66, 0x64, 0x61, 0x5e, 0x5a, 0x59, 0x56, + 0x54, 0x52, 0x51, 0x50, 0x4e, 0x4c, 0x4d, 0x4f, + 0x4f, 0x4f, 0x50, 0x50, 0x4f, 0x4f, 0x4e, 0x4d, + 0x4c, 0x4b, 0x49, 0x47, 0x45, 0x44, 0x43, 0x43, + 0x42, 0x43, 0x44, 0x44, 0x46, 0x47, 0x49, 0x4d, + 0x4f, 0x51, 0x53, 0x54, 0x53, 0x54, 0x54, 0x53, + 0x53, 0x51, 0x4e, 0x4b, 0x4a, 0x47, 0x45, 0x44, + 0x44, 0x45, 0x46, }, + { 0x20, 0x28, 0x26, 0x26, 0x25, 0x24, 0x27, 0x27, + 0x27, 0x29, 0x2c, 0x2e, 0x31, 0x34, 0x37, 0x3b, + 0x3e, 0x41, 0x45, 0x48, 0x4a, 0x4c, 0x4e, 0x4e, + 0x50, 0x51, 0x52, 0x51, 0x4f, 0x4b, 0x4a, 0x49, + 0x45, 0x43, 0x3f, 0x3c, 0x3a, 0x36, 0x33, 0x30, + 0x2f, 0x2d, 0x2a, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2c, 0x2e, 0x31, 0x34, 0x37, 0x39, 0x3b, 0x3c, + 0x3d, 0x3e, 0x3f, 0x40, 0x3e, 0x3d, 0x3d, 0x3a, + 0x38, 0x36, 0x34, 0x31, 0x2f, 0x2c, 0x29, 0x27, + 0x25, 0x21, 0x21, 0x1f, 0x1c, 0x1d, 0x19, 0x18, + 0x16, 0x15, 0x15, 0x13, 0x12, 0x11, 0x11, 0x0f, + 0x0f, 0x0e, 0x0f, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x10, + 0x10, 0x10, 0x12, 0x13, 0x15, 0x16, 0x18, 0x1a, + 0x1c, 0x1d, 0x20, 0x22, 0x25, 0x27, 0x2a, 0x2e, + 0x30, 0x34, 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3b, + 0x3c, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, + 0x36, 0x36, 0x38, 0x37, 0x37, 0x37, 0x38, 0x3a, + 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x40, 0x40, + 0x42, 0x40, 0x3f, 0x3e, 0x3d, 0x3b, 0x3a, 0x39, + 0x37, 0x36, 0x36, 0x35, 0x34, 0x34, 0x33, 0x33, + 0x33, 0x34, 0x35, 0x35, 0x35, 0x36, 0x38, 0x39, + 0x3a, 0x3b, 0x3d, 0x3f, 0x42, 0x43, 0x45, 0x45, + 0x46, 0x48, 0x49, 0x4b, 0x4b, 0x4d, 0x50, 0x53, + 0x56, 0x57, 0x5a, 0x5c, 0x5e, 0x61, 0x63, 0x65, + 0x66, 0x64, 0x62, 0x5f, 0x5c, 0x59, 0x58, 0x56, + 0x55, 0x54, 0x52, 0x51, 0x50, 0x51, 0x51, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x51, 0x51, 0x51, 0x50, + 0x4f, 0x4e, 0x4c, 0x4a, 0x47, 0x46, 0x45, 0x45, + 0x45, 0x46, 0x46, 0x46, 0x4a, 0x4c, 0x4d, 0x52, + 0x54, 0x56, 0x58, 0x58, 0x56, 0x57, 0x57, 0x56, + 0x55, 0x53, 0x50, 0x4d, 0x49, 0x45, 0x44, 0x44, + 0x43, 0x44, 0x45, }, + { 0x1f, 0x27, 0x24, 0x23, 0x25, 0x24, 0x25, 0x26, + 0x26, 0x28, 0x2b, 0x2e, 0x31, 0x34, 0x37, 0x3a, + 0x3d, 0x41, 0x45, 0x48, 0x4b, 0x4d, 0x4f, 0x4e, + 0x50, 0x51, 0x52, 0x50, 0x4f, 0x4b, 0x4a, 0x49, + 0x45, 0x43, 0x3f, 0x3c, 0x3a, 0x36, 0x33, 0x30, + 0x2f, 0x2d, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x25, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2c, 0x2f, 0x32, 0x34, 0x37, 0x39, 0x3b, 0x3c, + 0x3e, 0x3f, 0x3f, 0x40, 0x3e, 0x3d, 0x3c, 0x3a, + 0x38, 0x36, 0x34, 0x31, 0x30, 0x2c, 0x29, 0x28, + 0x25, 0x23, 0x22, 0x1f, 0x1c, 0x1c, 0x18, 0x18, + 0x16, 0x14, 0x14, 0x13, 0x11, 0x11, 0x11, 0x0f, + 0x0f, 0x0e, 0x0f, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, + 0x0c, 0x0c, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0d, 0x0e, 0x0e, 0x0f, 0x0d, 0x0f, 0x10, 0x10, + 0x10, 0x11, 0x13, 0x14, 0x15, 0x16, 0x19, 0x1a, + 0x1c, 0x1f, 0x20, 0x23, 0x26, 0x28, 0x2a, 0x2e, + 0x31, 0x35, 0x38, 0x39, 0x3a, 0x3c, 0x3d, 0x3d, + 0x3e, 0x3e, 0x3d, 0x3c, 0x3a, 0x3a, 0x39, 0x39, + 0x38, 0x37, 0x38, 0x38, 0x37, 0x38, 0x39, 0x3a, + 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x3f, + 0x41, 0x40, 0x3e, 0x3e, 0x3d, 0x3b, 0x3b, 0x39, + 0x37, 0x37, 0x35, 0x36, 0x34, 0x34, 0x34, 0x35, + 0x35, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x39, + 0x3a, 0x3c, 0x3f, 0x3f, 0x43, 0x43, 0x45, 0x47, + 0x48, 0x48, 0x4a, 0x4b, 0x4e, 0x4d, 0x51, 0x53, + 0x56, 0x58, 0x59, 0x5b, 0x5d, 0x60, 0x62, 0x63, + 0x64, 0x63, 0x61, 0x5e, 0x5c, 0x5a, 0x57, 0x56, + 0x55, 0x54, 0x53, 0x52, 0x51, 0x51, 0x52, 0x52, + 0x54, 0x54, 0x55, 0x55, 0x55, 0x54, 0x54, 0x53, + 0x52, 0x50, 0x4e, 0x4d, 0x4b, 0x4a, 0x48, 0x48, + 0x48, 0x48, 0x4a, 0x4b, 0x4d, 0x4f, 0x52, 0x55, + 0x58, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5a, 0x59, + 0x58, 0x55, 0x51, 0x4e, 0x4a, 0x46, 0x45, 0x44, + 0x44, 0x44, 0x44, }, + { 0x1e, 0x26, 0x23, 0x23, 0x25, 0x24, 0x25, 0x26, + 0x26, 0x28, 0x2b, 0x2e, 0x31, 0x34, 0x37, 0x3a, + 0x3e, 0x42, 0x45, 0x48, 0x4b, 0x4d, 0x4f, 0x4f, + 0x50, 0x51, 0x52, 0x50, 0x4f, 0x4b, 0x4a, 0x48, + 0x46, 0x44, 0x3f, 0x3b, 0x39, 0x36, 0x33, 0x30, + 0x2f, 0x2d, 0x2a, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2c, 0x2f, 0x32, 0x34, 0x37, 0x39, 0x3b, 0x3d, + 0x3e, 0x3f, 0x41, 0x41, 0x40, 0x3e, 0x3d, 0x3b, + 0x38, 0x37, 0x34, 0x32, 0x30, 0x2c, 0x2a, 0x27, + 0x26, 0x23, 0x22, 0x20, 0x1d, 0x1b, 0x1a, 0x19, + 0x17, 0x15, 0x15, 0x13, 0x12, 0x12, 0x11, 0x0f, + 0x11, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x11, + 0x11, 0x13, 0x16, 0x15, 0x15, 0x18, 0x1a, 0x1b, + 0x1d, 0x20, 0x22, 0x24, 0x27, 0x29, 0x2c, 0x30, + 0x33, 0x37, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3e, + 0x40, 0x40, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3d, + 0x3d, 0x3f, 0x40, 0x40, 0x3f, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x40, 0x40, 0x3f, 0x3e, 0x3c, 0x3b, + 0x3a, 0x39, 0x37, 0x36, 0x36, 0x35, 0x35, 0x36, + 0x36, 0x35, 0x35, 0x36, 0x36, 0x38, 0x39, 0x39, + 0x3b, 0x3c, 0x3e, 0x40, 0x41, 0x43, 0x45, 0x47, + 0x48, 0x48, 0x4b, 0x4c, 0x4d, 0x4f, 0x51, 0x53, + 0x56, 0x56, 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x62, + 0x63, 0x63, 0x61, 0x5e, 0x5c, 0x5a, 0x59, 0x57, + 0x56, 0x54, 0x54, 0x53, 0x52, 0x53, 0x53, 0x55, + 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x56, 0x56, + 0x55, 0x53, 0x51, 0x4f, 0x4d, 0x4b, 0x49, 0x4b, + 0x4b, 0x4c, 0x4d, 0x4e, 0x51, 0x53, 0x55, 0x58, + 0x5b, 0x5c, 0x60, 0x60, 0x5f, 0x5e, 0x5d, 0x5c, + 0x5a, 0x57, 0x53, 0x4f, 0x4b, 0x46, 0x45, 0x44, + 0x44, 0x44, 0x44, }, + { 0x1d, 0x25, 0x22, 0x22, 0x23, 0x23, 0x24, 0x25, + 0x25, 0x28, 0x2b, 0x2e, 0x31, 0x34, 0x37, 0x3a, + 0x3e, 0x42, 0x45, 0x48, 0x4b, 0x4d, 0x4f, 0x4f, + 0x50, 0x51, 0x52, 0x50, 0x4f, 0x4b, 0x4a, 0x47, + 0x45, 0x43, 0x3f, 0x3c, 0x38, 0x35, 0x33, 0x30, + 0x2f, 0x2d, 0x2a, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2b, 0x2f, 0x32, 0x34, 0x37, 0x39, 0x3c, 0x3d, + 0x3e, 0x3f, 0x40, 0x41, 0x40, 0x3e, 0x3d, 0x3b, + 0x39, 0x36, 0x34, 0x32, 0x30, 0x2d, 0x2a, 0x26, + 0x26, 0x24, 0x22, 0x1f, 0x1d, 0x1c, 0x1a, 0x19, + 0x18, 0x16, 0x15, 0x14, 0x12, 0x12, 0x12, 0x10, + 0x10, 0x0f, 0x0e, 0x10, 0x0e, 0x0e, 0x0d, 0x0c, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0d, 0x0e, + 0x0f, 0x0f, 0x0f, 0x10, 0x11, 0x11, 0x11, 0x12, + 0x13, 0x14, 0x16, 0x16, 0x18, 0x1a, 0x1b, 0x1c, + 0x1e, 0x21, 0x23, 0x25, 0x28, 0x2a, 0x2e, 0x32, + 0x34, 0x38, 0x3a, 0x3c, 0x3d, 0x3f, 0x40, 0x42, + 0x43, 0x43, 0x43, 0x42, 0x40, 0x3e, 0x3e, 0x3c, + 0x3b, 0x3b, 0x3c, 0x3a, 0x3b, 0x3b, 0x3e, 0x3e, + 0x40, 0x3f, 0x41, 0x41, 0x41, 0x42, 0x42, 0x43, + 0x42, 0x41, 0x41, 0x41, 0x40, 0x3e, 0x3d, 0x3c, + 0x3b, 0x3a, 0x39, 0x37, 0x36, 0x35, 0x36, 0x37, + 0x35, 0x36, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3b, 0x3d, 0x3e, 0x40, 0x41, 0x41, 0x44, 0x46, + 0x48, 0x48, 0x4a, 0x4c, 0x4d, 0x4f, 0x51, 0x53, + 0x55, 0x57, 0x59, 0x5a, 0x5b, 0x5e, 0x5f, 0x61, + 0x62, 0x61, 0x60, 0x5e, 0x5c, 0x5a, 0x59, 0x58, + 0x56, 0x55, 0x54, 0x53, 0x53, 0x54, 0x54, 0x55, + 0x57, 0x57, 0x58, 0x59, 0x5a, 0x58, 0x59, 0x58, + 0x57, 0x55, 0x53, 0x52, 0x4f, 0x4e, 0x4d, 0x4d, + 0x4d, 0x4f, 0x51, 0x50, 0x54, 0x56, 0x59, 0x5c, + 0x5f, 0x61, 0x64, 0x64, 0x63, 0x61, 0x5e, 0x5e, + 0x5c, 0x59, 0x54, 0x50, 0x4c, 0x46, 0x45, 0x44, + 0x44, 0x44, 0x44, }, + { 0x1c, 0x24, 0x21, 0x21, 0x21, 0x22, 0x23, 0x23, + 0x25, 0x27, 0x2a, 0x2e, 0x31, 0x33, 0x37, 0x3b, + 0x3e, 0x42, 0x45, 0x48, 0x4b, 0x4c, 0x50, 0x4f, + 0x50, 0x51, 0x52, 0x50, 0x4e, 0x4b, 0x4a, 0x49, + 0x45, 0x42, 0x3f, 0x3c, 0x38, 0x35, 0x33, 0x30, + 0x2f, 0x2d, 0x2a, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2b, 0x2f, 0x32, 0x34, 0x38, 0x39, 0x3c, 0x3d, + 0x3e, 0x3e, 0x40, 0x41, 0x40, 0x3e, 0x3c, 0x3a, + 0x39, 0x37, 0x35, 0x33, 0x30, 0x2d, 0x2b, 0x28, + 0x26, 0x23, 0x23, 0x20, 0x1e, 0x1b, 0x19, 0x19, + 0x17, 0x16, 0x15, 0x14, 0x12, 0x12, 0x11, 0x10, + 0x0f, 0x0e, 0x0e, 0x10, 0x0e, 0x0d, 0x0c, 0x0c, + 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0d, 0x0e, + 0x0f, 0x0f, 0x0f, 0x10, 0x11, 0x11, 0x12, 0x14, + 0x14, 0x14, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e, + 0x20, 0x23, 0x26, 0x27, 0x29, 0x2c, 0x2f, 0x33, + 0x36, 0x38, 0x3b, 0x3e, 0x3e, 0x42, 0x43, 0x46, + 0x46, 0x46, 0x46, 0x44, 0x42, 0x41, 0x3f, 0x3e, + 0x3d, 0x3d, 0x3e, 0x3d, 0x3d, 0x3e, 0x3e, 0x40, + 0x40, 0x40, 0x43, 0x43, 0x42, 0x43, 0x45, 0x43, + 0x43, 0x43, 0x42, 0x42, 0x41, 0x40, 0x40, 0x3e, + 0x3c, 0x3a, 0x3a, 0x38, 0x36, 0x36, 0x36, 0x36, + 0x37, 0x37, 0x36, 0x38, 0x38, 0x39, 0x3b, 0x3b, + 0x3e, 0x3e, 0x3e, 0x40, 0x41, 0x43, 0x45, 0x46, + 0x46, 0x49, 0x4c, 0x4c, 0x4d, 0x4f, 0x51, 0x54, + 0x56, 0x57, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x60, + 0x61, 0x61, 0x60, 0x5f, 0x5c, 0x5a, 0x59, 0x58, + 0x57, 0x57, 0x55, 0x54, 0x53, 0x55, 0x55, 0x58, + 0x58, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, + 0x5a, 0x59, 0x56, 0x54, 0x53, 0x4e, 0x4e, 0x50, + 0x50, 0x51, 0x52, 0x52, 0x57, 0x59, 0x5d, 0x60, + 0x63, 0x63, 0x66, 0x66, 0x66, 0x64, 0x63, 0x61, + 0x60, 0x5b, 0x55, 0x51, 0x4d, 0x48, 0x45, 0x44, + 0x43, 0x43, 0x43, }, + { 0x1b, 0x23, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24, + 0x26, 0x27, 0x2a, 0x2e, 0x31, 0x33, 0x37, 0x3b, + 0x3d, 0x42, 0x46, 0x49, 0x4a, 0x4c, 0x4f, 0x4f, + 0x50, 0x50, 0x52, 0x50, 0x4e, 0x4b, 0x4b, 0x49, + 0x45, 0x42, 0x3e, 0x3c, 0x38, 0x35, 0x33, 0x30, + 0x2f, 0x2d, 0x2a, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2c, 0x2f, 0x32, 0x35, 0x38, 0x3a, 0x3c, 0x3d, + 0x3e, 0x3e, 0x40, 0x41, 0x40, 0x3f, 0x3d, 0x3b, + 0x3a, 0x38, 0x36, 0x33, 0x30, 0x2d, 0x2b, 0x29, + 0x27, 0x24, 0x24, 0x21, 0x1e, 0x1c, 0x1b, 0x1a, + 0x18, 0x17, 0x16, 0x15, 0x13, 0x12, 0x10, 0x0f, + 0x10, 0x0f, 0x0e, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0e, 0x0f, + 0x10, 0x11, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, + 0x15, 0x16, 0x17, 0x1a, 0x1b, 0x1d, 0x1e, 0x20, + 0x21, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x31, 0x35, + 0x37, 0x39, 0x3c, 0x3f, 0x40, 0x43, 0x46, 0x47, + 0x4a, 0x49, 0x48, 0x46, 0x45, 0x43, 0x42, 0x41, + 0x3f, 0x40, 0x3f, 0x3f, 0x40, 0x3f, 0x41, 0x43, + 0x43, 0x43, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, + 0x45, 0x45, 0x44, 0x43, 0x43, 0x42, 0x42, 0x40, + 0x3e, 0x3d, 0x3c, 0x39, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x36, 0x38, 0x39, 0x39, 0x3a, 0x3c, 0x3d, + 0x3e, 0x3e, 0x3f, 0x41, 0x42, 0x42, 0x43, 0x45, + 0x46, 0x49, 0x4b, 0x4d, 0x4f, 0x50, 0x53, 0x54, + 0x57, 0x58, 0x5a, 0x5c, 0x5b, 0x5e, 0x60, 0x61, + 0x60, 0x60, 0x5f, 0x5f, 0x5d, 0x5b, 0x5b, 0x59, + 0x58, 0x57, 0x56, 0x55, 0x55, 0x55, 0x57, 0x59, + 0x5b, 0x5b, 0x5d, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, + 0x5d, 0x5b, 0x59, 0x56, 0x54, 0x51, 0x51, 0x51, + 0x52, 0x55, 0x56, 0x56, 0x5a, 0x5d, 0x5f, 0x63, + 0x66, 0x68, 0x6b, 0x6b, 0x68, 0x67, 0x66, 0x64, + 0x61, 0x5d, 0x57, 0x52, 0x4f, 0x49, 0x46, 0x45, + 0x43, 0x43, 0x43, }, + { 0x1a, 0x22, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x26, 0x27, 0x2a, 0x2d, 0x31, 0x33, 0x37, 0x3b, + 0x3d, 0x41, 0x46, 0x49, 0x4a, 0x4d, 0x4f, 0x4f, + 0x50, 0x51, 0x52, 0x50, 0x4e, 0x4b, 0x4b, 0x48, + 0x44, 0x42, 0x3e, 0x3c, 0x39, 0x35, 0x33, 0x30, + 0x2f, 0x2d, 0x2a, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x27, 0x29, 0x2a, + 0x2d, 0x2f, 0x32, 0x35, 0x39, 0x3a, 0x3c, 0x3d, + 0x3e, 0x3f, 0x40, 0x41, 0x40, 0x3f, 0x3e, 0x3c, + 0x3a, 0x38, 0x36, 0x33, 0x31, 0x2d, 0x2c, 0x29, + 0x27, 0x26, 0x24, 0x21, 0x1f, 0x1d, 0x1c, 0x1a, + 0x19, 0x18, 0x16, 0x15, 0x14, 0x13, 0x12, 0x10, + 0x11, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, + 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, + 0x11, 0x12, 0x12, 0x13, 0x15, 0x15, 0x16, 0x16, + 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x21, + 0x22, 0x25, 0x27, 0x2a, 0x2c, 0x2e, 0x33, 0x36, + 0x39, 0x3a, 0x3d, 0x40, 0x41, 0x45, 0x47, 0x4a, + 0x4c, 0x4d, 0x4c, 0x4a, 0x48, 0x45, 0x44, 0x41, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x44, + 0x45, 0x47, 0x47, 0x48, 0x47, 0x48, 0x47, 0x47, + 0x48, 0x48, 0x46, 0x46, 0x46, 0x43, 0x43, 0x41, + 0x3f, 0x3e, 0x3b, 0x39, 0x38, 0x37, 0x37, 0x37, + 0x38, 0x38, 0x37, 0x39, 0x39, 0x3a, 0x3c, 0x3e, + 0x3e, 0x3f, 0x3f, 0x3f, 0x42, 0x43, 0x43, 0x45, + 0x47, 0x48, 0x4b, 0x4c, 0x4e, 0x50, 0x51, 0x54, + 0x56, 0x58, 0x5a, 0x5c, 0x5c, 0x5f, 0x5f, 0x5f, + 0x61, 0x60, 0x5f, 0x5f, 0x5e, 0x5b, 0x5c, 0x5b, + 0x59, 0x59, 0x57, 0x56, 0x55, 0x56, 0x57, 0x59, + 0x5a, 0x5b, 0x5c, 0x5c, 0x5d, 0x5e, 0x5e, 0x5d, + 0x5e, 0x5c, 0x5a, 0x57, 0x55, 0x52, 0x51, 0x52, + 0x53, 0x55, 0x57, 0x58, 0x5c, 0x5e, 0x61, 0x65, + 0x69, 0x6b, 0x6c, 0x6b, 0x6a, 0x69, 0x67, 0x64, + 0x61, 0x5d, 0x59, 0x53, 0x4d, 0x48, 0x46, 0x45, + 0x44, 0x44, 0x43, }, + { 0x1a, 0x21, 0x1e, 0x1f, 0x20, 0x21, 0x23, 0x24, + 0x25, 0x28, 0x2a, 0x2e, 0x31, 0x33, 0x37, 0x3b, + 0x3e, 0x41, 0x46, 0x49, 0x4b, 0x4d, 0x4f, 0x4e, + 0x50, 0x51, 0x51, 0x50, 0x4e, 0x4b, 0x4a, 0x48, + 0x44, 0x42, 0x3e, 0x3c, 0x39, 0x35, 0x32, 0x30, + 0x2f, 0x2d, 0x29, 0x27, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x29, 0x2a, + 0x2c, 0x2f, 0x32, 0x35, 0x38, 0x3b, 0x3c, 0x3e, + 0x3f, 0x3f, 0x40, 0x41, 0x40, 0x3f, 0x3e, 0x3c, + 0x3a, 0x39, 0x36, 0x34, 0x31, 0x2d, 0x2c, 0x29, + 0x27, 0x26, 0x24, 0x21, 0x1f, 0x1d, 0x1c, 0x1a, + 0x19, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x10, + 0x11, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, + 0x11, 0x13, 0x14, 0x14, 0x15, 0x16, 0x17, 0x19, + 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x22, 0x24, + 0x25, 0x27, 0x29, 0x2c, 0x2e, 0x31, 0x35, 0x38, + 0x3a, 0x3d, 0x41, 0x42, 0x45, 0x48, 0x4c, 0x4e, + 0x4f, 0x4f, 0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x47, + 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 0x46, 0x47, + 0x48, 0x49, 0x4b, 0x4b, 0x4a, 0x4b, 0x4b, 0x4a, + 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x46, 0x46, 0x44, + 0x42, 0x41, 0x3d, 0x3b, 0x3a, 0x38, 0x38, 0x38, + 0x37, 0x37, 0x39, 0x38, 0x3a, 0x3a, 0x3c, 0x3c, + 0x3e, 0x40, 0x40, 0x41, 0x43, 0x43, 0x45, 0x46, + 0x48, 0x49, 0x4b, 0x4e, 0x4f, 0x50, 0x53, 0x55, + 0x57, 0x59, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, + 0x60, 0x60, 0x5f, 0x5f, 0x5e, 0x5c, 0x5b, 0x5a, + 0x59, 0x58, 0x57, 0x57, 0x56, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x5b, 0x5c, 0x5c, 0x5d, 0x5e, 0x5d, + 0x5c, 0x5b, 0x58, 0x57, 0x54, 0x52, 0x52, 0x53, + 0x54, 0x57, 0x58, 0x58, 0x5b, 0x5e, 0x62, 0x65, + 0x69, 0x6b, 0x6d, 0x6c, 0x6a, 0x69, 0x67, 0x64, + 0x62, 0x5e, 0x59, 0x54, 0x4d, 0x48, 0x47, 0x46, + 0x45, 0x45, 0x44, }, + { 0x1a, 0x21, 0x1e, 0x1f, 0x20, 0x21, 0x23, 0x24, + 0x25, 0x28, 0x2a, 0x2e, 0x31, 0x34, 0x37, 0x3b, + 0x3e, 0x42, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 0x4f, + 0x50, 0x51, 0x51, 0x50, 0x50, 0x4c, 0x4a, 0x47, + 0x44, 0x42, 0x3e, 0x3c, 0x39, 0x35, 0x32, 0x31, + 0x2f, 0x2d, 0x29, 0x27, 0x26, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x25, 0x25, 0x26, 0x27, 0x29, 0x2b, + 0x2c, 0x2f, 0x33, 0x35, 0x38, 0x3a, 0x3c, 0x3e, + 0x40, 0x40, 0x41, 0x42, 0x41, 0x3f, 0x3f, 0x3d, + 0x3b, 0x39, 0x36, 0x33, 0x32, 0x2e, 0x2d, 0x2a, + 0x27, 0x26, 0x25, 0x22, 0x1f, 0x1d, 0x1c, 0x1b, + 0x19, 0x17, 0x17, 0x16, 0x15, 0x14, 0x12, 0x11, + 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x10, 0x11, 0x10, 0x11, 0x11, 0x12, + 0x11, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1b, + 0x1c, 0x1c, 0x1e, 0x20, 0x21, 0x22, 0x23, 0x25, + 0x27, 0x2a, 0x2c, 0x2f, 0x31, 0x35, 0x38, 0x3b, + 0x3d, 0x40, 0x44, 0x47, 0x49, 0x4c, 0x4f, 0x51, + 0x53, 0x53, 0x53, 0x51, 0x50, 0x4e, 0x4c, 0x4b, + 0x4a, 0x49, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4d, + 0x4e, 0x4e, 0x4f, 0x50, 0x4f, 0x50, 0x51, 0x50, + 0x50, 0x4e, 0x4d, 0x4c, 0x4b, 0x48, 0x48, 0x47, + 0x44, 0x42, 0x3f, 0x3d, 0x3b, 0x3a, 0x39, 0x39, + 0x39, 0x38, 0x39, 0x3b, 0x3a, 0x3c, 0x3e, 0x3d, + 0x40, 0x40, 0x40, 0x42, 0x42, 0x42, 0x45, 0x46, + 0x47, 0x49, 0x4c, 0x4e, 0x50, 0x50, 0x53, 0x56, + 0x58, 0x59, 0x5d, 0x5d, 0x5e, 0x60, 0x61, 0x61, + 0x62, 0x61, 0x60, 0x60, 0x5e, 0x5d, 0x5d, 0x5b, + 0x57, 0x58, 0x56, 0x55, 0x55, 0x56, 0x56, 0x59, + 0x59, 0x58, 0x5a, 0x5a, 0x5a, 0x5c, 0x5c, 0x5c, + 0x5b, 0x5b, 0x58, 0x57, 0x54, 0x53, 0x52, 0x53, + 0x54, 0x57, 0x58, 0x59, 0x5c, 0x5f, 0x63, 0x67, + 0x6b, 0x6d, 0x6e, 0x6e, 0x6b, 0x6a, 0x68, 0x64, + 0x62, 0x5e, 0x58, 0x53, 0x4f, 0x49, 0x47, 0x46, + 0x45, 0x45, 0x44, }, + { 0x19, 0x20, 0x1e, 0x1e, 0x1f, 0x20, 0x22, 0x23, + 0x25, 0x27, 0x2a, 0x2e, 0x31, 0x34, 0x37, 0x3a, + 0x3e, 0x41, 0x46, 0x49, 0x4a, 0x4d, 0x4f, 0x4e, + 0x50, 0x51, 0x51, 0x4f, 0x4f, 0x4d, 0x49, 0x47, + 0x44, 0x42, 0x3e, 0x3c, 0x39, 0x36, 0x32, 0x31, + 0x2f, 0x2d, 0x29, 0x27, 0x26, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x25, 0x25, 0x26, 0x28, 0x29, 0x2b, + 0x2c, 0x2f, 0x33, 0x35, 0x38, 0x3a, 0x3c, 0x3e, + 0x3f, 0x3f, 0x41, 0x42, 0x41, 0x3f, 0x3f, 0x3d, + 0x3c, 0x39, 0x36, 0x33, 0x32, 0x2e, 0x2d, 0x2a, + 0x27, 0x26, 0x25, 0x22, 0x1f, 0x1e, 0x1d, 0x1b, + 0x1a, 0x17, 0x17, 0x17, 0x14, 0x14, 0x12, 0x11, + 0x11, 0x12, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x13, 0x14, + 0x14, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1e, + 0x1e, 0x1f, 0x22, 0x23, 0x23, 0x24, 0x25, 0x27, + 0x2a, 0x2d, 0x2f, 0x31, 0x35, 0x38, 0x3a, 0x3e, + 0x41, 0x44, 0x48, 0x4b, 0x4d, 0x51, 0x53, 0x55, + 0x57, 0x57, 0x56, 0x55, 0x54, 0x52, 0x52, 0x50, + 0x4e, 0x50, 0x4e, 0x4d, 0x4d, 0x4d, 0x4f, 0x51, + 0x51, 0x52, 0x54, 0x55, 0x55, 0x55, 0x57, 0x55, + 0x54, 0x53, 0x52, 0x4e, 0x4d, 0x4b, 0x4a, 0x49, + 0x46, 0x44, 0x41, 0x3f, 0x3d, 0x3b, 0x3a, 0x3a, + 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3b, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x42, 0x44, 0x44, 0x45, 0x47, + 0x49, 0x49, 0x4a, 0x4d, 0x50, 0x51, 0x53, 0x57, + 0x5a, 0x5b, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x62, + 0x63, 0x62, 0x60, 0x60, 0x5e, 0x5c, 0x5c, 0x59, + 0x58, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, + 0x56, 0x56, 0x57, 0x58, 0x58, 0x59, 0x5a, 0x59, + 0x58, 0x57, 0x56, 0x55, 0x54, 0x52, 0x53, 0x53, + 0x53, 0x56, 0x57, 0x59, 0x5b, 0x5e, 0x62, 0x66, + 0x6a, 0x6c, 0x6d, 0x6e, 0x6b, 0x69, 0x67, 0x64, + 0x61, 0x5d, 0x58, 0x54, 0x50, 0x4a, 0x47, 0x46, + 0x45, 0x45, 0x44, }, + { 0x1a, 0x21, 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x23, + 0x25, 0x27, 0x2b, 0x2e, 0x31, 0x34, 0x37, 0x3b, + 0x3d, 0x42, 0x45, 0x49, 0x4a, 0x4d, 0x4e, 0x4e, + 0x51, 0x52, 0x50, 0x4f, 0x4f, 0x4c, 0x49, 0x48, + 0x45, 0x42, 0x3e, 0x3b, 0x39, 0x36, 0x32, 0x32, + 0x2f, 0x2c, 0x2a, 0x28, 0x26, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x25, 0x28, 0x29, 0x2b, + 0x2d, 0x2f, 0x33, 0x35, 0x38, 0x3a, 0x3c, 0x3e, + 0x3f, 0x3f, 0x41, 0x42, 0x41, 0x3f, 0x3e, 0x3c, + 0x3c, 0x3a, 0x37, 0x33, 0x32, 0x2f, 0x2d, 0x2b, + 0x28, 0x26, 0x25, 0x22, 0x20, 0x1e, 0x1d, 0x1b, + 0x1a, 0x17, 0x17, 0x16, 0x14, 0x14, 0x12, 0x11, + 0x12, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, + 0x10, 0x11, 0x12, 0x12, 0x12, 0x13, 0x14, 0x14, + 0x16, 0x18, 0x19, 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, + 0x21, 0x22, 0x23, 0x25, 0x26, 0x26, 0x28, 0x2a, + 0x2c, 0x2e, 0x32, 0x34, 0x39, 0x39, 0x3d, 0x41, + 0x45, 0x47, 0x4c, 0x4e, 0x51, 0x54, 0x56, 0x58, + 0x5b, 0x5c, 0x5a, 0x59, 0x58, 0x56, 0x55, 0x53, + 0x53, 0x52, 0x52, 0x51, 0x52, 0x52, 0x53, 0x55, + 0x57, 0x58, 0x5a, 0x5a, 0x59, 0x5b, 0x59, 0x59, + 0x58, 0x57, 0x55, 0x53, 0x51, 0x4e, 0x4c, 0x4a, + 0x48, 0x46, 0x43, 0x40, 0x3e, 0x3c, 0x3b, 0x3b, + 0x38, 0x39, 0x38, 0x39, 0x3a, 0x3d, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, + 0x4a, 0x4b, 0x4d, 0x4e, 0x50, 0x52, 0x54, 0x56, + 0x59, 0x5c, 0x5e, 0x5f, 0x60, 0x62, 0x62, 0x63, + 0x63, 0x63, 0x61, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, + 0x59, 0x56, 0x56, 0x55, 0x54, 0x53, 0x53, 0x54, + 0x55, 0x54, 0x55, 0x55, 0x55, 0x57, 0x58, 0x57, + 0x57, 0x56, 0x55, 0x54, 0x54, 0x52, 0x52, 0x53, + 0x54, 0x55, 0x57, 0x58, 0x5b, 0x5e, 0x62, 0x65, + 0x69, 0x6b, 0x6d, 0x6e, 0x6a, 0x69, 0x67, 0x63, + 0x61, 0x5d, 0x58, 0x54, 0x4f, 0x4b, 0x48, 0x47, + 0x46, 0x45, 0x45, }, + { 0x1a, 0x21, 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x23, + 0x25, 0x27, 0x2b, 0x2d, 0x31, 0x34, 0x37, 0x3b, + 0x3d, 0x42, 0x45, 0x48, 0x4c, 0x4e, 0x4e, 0x4f, + 0x51, 0x52, 0x50, 0x50, 0x4f, 0x4c, 0x4a, 0x48, + 0x45, 0x42, 0x3f, 0x3b, 0x39, 0x36, 0x32, 0x31, + 0x2f, 0x2c, 0x2a, 0x28, 0x26, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2b, + 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3b, 0x3d, 0x3f, + 0x3f, 0x40, 0x42, 0x43, 0x42, 0x40, 0x3e, 0x3c, + 0x3c, 0x3a, 0x37, 0x34, 0x32, 0x2f, 0x2d, 0x2c, + 0x2a, 0x27, 0x26, 0x23, 0x20, 0x1e, 0x1d, 0x1c, + 0x1a, 0x18, 0x18, 0x17, 0x15, 0x16, 0x14, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x12, + 0x12, 0x12, 0x13, 0x14, 0x14, 0x14, 0x15, 0x16, + 0x17, 0x19, 0x1b, 0x1c, 0x1e, 0x20, 0x20, 0x22, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x2a, 0x2c, 0x2c, + 0x2f, 0x32, 0x35, 0x37, 0x3b, 0x3c, 0x41, 0x45, + 0x48, 0x4c, 0x50, 0x52, 0x54, 0x57, 0x5a, 0x5c, + 0x5f, 0x5f, 0x5f, 0x5d, 0x5c, 0x5b, 0x5a, 0x58, + 0x57, 0x57, 0x57, 0x56, 0x56, 0x57, 0x57, 0x5a, + 0x5c, 0x5e, 0x5f, 0x61, 0x5f, 0x5f, 0x5f, 0x5e, + 0x5d, 0x5c, 0x5a, 0x57, 0x55, 0x52, 0x4f, 0x4e, + 0x4a, 0x47, 0x46, 0x42, 0x41, 0x3e, 0x3d, 0x3c, + 0x3b, 0x3a, 0x39, 0x39, 0x3b, 0x3c, 0x3d, 0x3f, + 0x40, 0x42, 0x42, 0x44, 0x45, 0x46, 0x49, 0x49, + 0x4b, 0x4c, 0x4e, 0x4f, 0x51, 0x54, 0x57, 0x58, + 0x5b, 0x5d, 0x61, 0x61, 0x61, 0x63, 0x65, 0x65, + 0x64, 0x64, 0x62, 0x61, 0x60, 0x5e, 0x5d, 0x5c, + 0x59, 0x58, 0x56, 0x54, 0x53, 0x53, 0x53, 0x54, + 0x54, 0x53, 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, + 0x56, 0x55, 0x54, 0x53, 0x53, 0x52, 0x52, 0x53, + 0x55, 0x56, 0x57, 0x58, 0x5b, 0x5e, 0x62, 0x66, + 0x69, 0x6b, 0x6d, 0x6d, 0x6b, 0x69, 0x67, 0x64, + 0x61, 0x5d, 0x58, 0x55, 0x50, 0x4b, 0x48, 0x47, + 0x46, 0x46, 0x46, }, + { 0x1a, 0x20, 0x1e, 0x1f, 0x1f, 0x21, 0x22, 0x23, + 0x25, 0x27, 0x2b, 0x2d, 0x31, 0x34, 0x37, 0x3b, + 0x3d, 0x42, 0x45, 0x48, 0x4c, 0x4e, 0x4f, 0x4f, + 0x51, 0x52, 0x51, 0x50, 0x4e, 0x4b, 0x4a, 0x48, + 0x45, 0x42, 0x3f, 0x3b, 0x38, 0x36, 0x32, 0x31, + 0x2f, 0x2c, 0x2a, 0x28, 0x26, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2b, + 0x2e, 0x30, 0x33, 0x36, 0x39, 0x3b, 0x3d, 0x3f, + 0x3f, 0x40, 0x41, 0x42, 0x41, 0x40, 0x3e, 0x3c, + 0x3c, 0x3a, 0x37, 0x34, 0x33, 0x30, 0x2e, 0x2b, + 0x29, 0x26, 0x24, 0x24, 0x20, 0x1f, 0x1d, 0x1d, + 0x1a, 0x19, 0x17, 0x16, 0x16, 0x16, 0x16, 0x14, + 0x13, 0x12, 0x13, 0x13, 0x13, 0x12, 0x12, 0x13, + 0x13, 0x14, 0x15, 0x15, 0x14, 0x15, 0x16, 0x18, + 0x19, 0x1b, 0x1c, 0x1e, 0x20, 0x21, 0x22, 0x24, + 0x27, 0x28, 0x29, 0x2a, 0x2c, 0x2c, 0x2d, 0x2f, + 0x32, 0x35, 0x37, 0x3a, 0x3c, 0x3e, 0x44, 0x48, + 0x4c, 0x50, 0x54, 0x56, 0x58, 0x5b, 0x5e, 0x60, + 0x61, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, + 0x60, 0x63, 0x64, 0x65, 0x63, 0x62, 0x63, 0x63, + 0x61, 0x60, 0x5e, 0x5b, 0x58, 0x55, 0x51, 0x4f, + 0x4c, 0x4a, 0x47, 0x44, 0x42, 0x41, 0x3e, 0x3c, + 0x3b, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3e, 0x3f, + 0x40, 0x42, 0x43, 0x45, 0x46, 0x47, 0x49, 0x4a, + 0x4c, 0x4c, 0x4f, 0x51, 0x52, 0x55, 0x58, 0x5b, + 0x5c, 0x5f, 0x61, 0x62, 0x63, 0x64, 0x64, 0x65, + 0x66, 0x65, 0x63, 0x62, 0x5f, 0x5e, 0x5e, 0x5c, + 0x5b, 0x58, 0x56, 0x55, 0x54, 0x53, 0x52, 0x53, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x55, 0x55, + 0x55, 0x53, 0x53, 0x53, 0x52, 0x51, 0x52, 0x52, + 0x55, 0x55, 0x58, 0x58, 0x5b, 0x5d, 0x61, 0x65, + 0x68, 0x6a, 0x6c, 0x6b, 0x69, 0x68, 0x67, 0x64, + 0x61, 0x5e, 0x58, 0x54, 0x4f, 0x4b, 0x49, 0x48, + 0x47, 0x46, 0x45, }, + { 0x19, 0x20, 0x1d, 0x1f, 0x1f, 0x20, 0x23, 0x23, + 0x25, 0x27, 0x2b, 0x2d, 0x31, 0x34, 0x37, 0x3b, + 0x3d, 0x42, 0x45, 0x48, 0x4c, 0x4e, 0x4f, 0x4f, + 0x51, 0x52, 0x51, 0x50, 0x4e, 0x4b, 0x4a, 0x48, + 0x44, 0x42, 0x3f, 0x3a, 0x38, 0x36, 0x32, 0x30, + 0x2f, 0x2c, 0x2a, 0x28, 0x26, 0x26, 0x25, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2b, + 0x2e, 0x30, 0x34, 0x36, 0x39, 0x3b, 0x3d, 0x3f, + 0x3f, 0x40, 0x41, 0x42, 0x41, 0x40, 0x3e, 0x3c, + 0x3c, 0x3a, 0x37, 0x34, 0x33, 0x30, 0x2e, 0x2b, + 0x29, 0x27, 0x25, 0x24, 0x21, 0x1f, 0x1e, 0x1c, + 0x1b, 0x19, 0x17, 0x16, 0x16, 0x16, 0x16, 0x14, + 0x13, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x14, 0x15, 0x14, 0x14, 0x14, 0x17, 0x19, + 0x1a, 0x1c, 0x1e, 0x20, 0x21, 0x23, 0x24, 0x26, + 0x29, 0x29, 0x2b, 0x2c, 0x2d, 0x2e, 0x30, 0x31, + 0x34, 0x38, 0x3b, 0x3c, 0x3f, 0x42, 0x47, 0x4c, + 0x50, 0x54, 0x57, 0x5b, 0x5c, 0x5e, 0x62, 0x63, + 0x66, 0x66, 0x66, 0x65, 0x64, 0x63, 0x61, 0x62, + 0x60, 0x60, 0x5f, 0x5e, 0x5e, 0x5f, 0x60, 0x62, + 0x65, 0x67, 0x69, 0x6a, 0x69, 0x68, 0x69, 0x67, + 0x66, 0x64, 0x62, 0x5f, 0x5c, 0x58, 0x54, 0x51, + 0x4e, 0x4b, 0x49, 0x45, 0x43, 0x41, 0x40, 0x3e, + 0x3c, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x41, 0x42, 0x44, 0x46, 0x46, 0x48, 0x49, 0x4b, + 0x4d, 0x50, 0x51, 0x53, 0x55, 0x57, 0x58, 0x5c, + 0x5f, 0x60, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66, + 0x66, 0x65, 0x65, 0x63, 0x61, 0x5f, 0x5e, 0x5c, + 0x5a, 0x58, 0x56, 0x55, 0x54, 0x53, 0x52, 0x52, + 0x53, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, + 0x54, 0x53, 0x53, 0x52, 0x53, 0x51, 0x53, 0x53, + 0x55, 0x57, 0x58, 0x59, 0x5b, 0x5d, 0x62, 0x64, + 0x68, 0x6a, 0x6c, 0x6b, 0x69, 0x68, 0x67, 0x64, + 0x61, 0x5d, 0x57, 0x54, 0x50, 0x4a, 0x48, 0x47, + 0x46, 0x45, 0x45, }, diff --git a/tests/tcg/hexagon/hvx_histogram_row.S b/tests/tcg/hexagon/hvx_histogram_row.S new file mode 100644 index 0000000000..5e42c33145 --- /dev/null +++ b/tests/tcg/hexagon/hvx_histogram_row.S @@ -0,0 +1,294 @@ +/* + * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + +/* + * void hvx_histogram_row(uint8_t *src, => r0 + * int stride, => r1 + * int width, => r2 + * int height, => r3 + * int *hist => r4) + */ + .text + .p2align 2 + .global hvx_histogram_row + .type hvx_histogram_row, @function +hvx_histogram_row: + { r2 = lsr(r2, #7) /* size / VLEN */ + r5 = and(r2, #127) /* size % VLEN */ + v1 = #0 + v0 = #0 + } + /* + * Step 1: Clean the whole vector register file + */ + { v3:2 = v1:0 + v5:4 = v1:0 + p0 = cmp.gt(r2, #0) /* P0 = (width / VLEN > 0) */ + p1 = cmp.eq(r5, #0) /* P1 = (width % VLEN == 0) */ + } + { q0 = vsetq(r5) + v7:6 = v1:0 + } + { v9:8 = v1:0 + v11:10 = v1:0 + } + { v13:12 = v1:0 + v15:14 = v1:0 + } + { v17:16 = v1:0 + v19:18 = v1:0 + } + { v21:20 = v1:0 + v23:22 = v1:0 + } + { v25:24 = v1:0 + v27:26 = v1:0 + } + { v29:28 = v1:0 + v31:30 = v1:0 + r10 = add(r0, r1) /* R10 = &src[2 * stride] */ + loop1(.outerloop, r3) + } + + /* + * Step 2: vhist + */ + .falign +.outerloop: + { if (!p0) jump .loopend + loop0(.innerloop, r2) + } + + .falign +.innerloop: + { v12.tmp = vmem(R0++#1) + vhist + }:endloop0 + + .falign +.loopend: + if (p1) jump .skip /* if (width % VLEN == 0) done with current row */ + { v13.tmp = vmem(r0 + #0) + vhist(q0) + } + + .falign +.skip: + { r0 = r10 /* R0 = &src[(i + 1) * stride] */ + r10 = add(r10, r1) /* R10 = &src[(i + 2) * stride] */ + }:endloop1 + + + /* + * Step 3: Sum up the data + */ + { v0.h = vshuff(v0.h) + r10 = ##0x00010001 + } + v1.h = vshuff(v1.h) + { V2.h = vshuff(v2.h) + v0.w = vdmpy(v0.h, r10.h):sat + } + { v3.h = vshuff(v3.h) + v1.w = vdmpy(v1.h, r10.h):sat + } + { v4.h = vshuff(V4.h) + v2.w = vdmpy(v2.h, r10.h):sat + } + { v5.h = vshuff(v5.h) + v3.w = vdmpy(v3.h, r10.h):sat + } + { v6.h = vshuff(v6.h) + v4.w = vdmpy(v4.h, r10.h):sat + } + { v7.h = vshuff(v7.h) + v5.w = vdmpy(v5.h, r10.h):sat + } + { v8.h = vshuff(V8.h) + v6.w = vdmpy(v6.h, r10.h):sat + } + { v9.h = vshuff(V9.h) + v7.w = vdmpy(v7.h, r10.h):sat + } + { v10.h = vshuff(v10.h) + v8.w = vdmpy(v8.h, r10.h):sat + } + { v11.h = vshuff(v11.h) + v9.w = vdmpy(v9.h, r10.h):sat + } + { v12.h = vshuff(v12.h) + v10.w = vdmpy(v10.h, r10.h):sat + } + { v13.h = vshuff(V13.h) + v11.w = vdmpy(v11.h, r10.h):sat + } + { v14.h = vshuff(v14.h) + v12.w = vdmpy(v12.h, r10.h):sat + } + { v15.h = vshuff(v15.h) + v13.w = vdmpy(v13.h, r10.h):sat + } + { v16.h = vshuff(v16.h) + v14.w = vdmpy(v14.h, r10.h):sat + } + { v17.h = vshuff(v17.h) + v15.w = vdmpy(v15.h, r10.h):sat + } + { v18.h = vshuff(v18.h) + v16.w = vdmpy(v16.h, r10.h):sat + } + { v19.h = vshuff(v19.h) + v17.w = vdmpy(v17.h, r10.h):sat + } + { v20.h = vshuff(v20.h) + v18.W = vdmpy(v18.h, r10.h):sat + } + { v21.h = vshuff(v21.h) + v19.w = vdmpy(v19.h, r10.h):sat + } + { v22.h = vshuff(v22.h) + v20.w = vdmpy(v20.h, r10.h):sat + } + { v23.h = vshuff(v23.h) + v21.w = vdmpy(v21.h, r10.h):sat + } + { v24.h = vshuff(v24.h) + v22.w = vdmpy(v22.h, r10.h):sat + } + { v25.h = vshuff(v25.h) + v23.w = vdmpy(v23.h, r10.h):sat + } + { v26.h = vshuff(v26.h) + v24.w = vdmpy(v24.h, r10.h):sat + } + { v27.h = vshuff(V27.h) + v25.w = vdmpy(v25.h, r10.h):sat + } + { v28.h = vshuff(v28.h) + v26.w = vdmpy(v26.h, r10.h):sat + } + { v29.h = vshuff(v29.h) + v27.w = vdmpy(v27.h, r10.h):sat + } + { v30.h = vshuff(v30.h) + v28.w = vdmpy(v28.h, r10.h):sat + } + { v31.h = vshuff(v31.h) + v29.w = vdmpy(v29.h, r10.h):sat + r28 = #32 + } + { vshuff(v1, v0, r28) + v30.w = vdmpy(v30.h, r10.h):sat + } + { vshuff(v3, v2, r28) + v31.w = vdmpy(v31.h, r10.h):sat + } + { vshuff(v5, v4, r28) + v0.w = vadd(v1.w, v0.w) + v2.w = vadd(v3.w, v2.w) + } + { vshuff(v7, v6, r28) + r7 = #64 + } + { vshuff(v9, v8, r28) + v4.w = vadd(v5.w, v4.w) + v6.w = vadd(v7.w, v6.w) + } + vshuff(v11, v10, r28) + { vshuff(v13, v12, r28) + v8.w = vadd(v9.w, v8.w) + v10.w = vadd(v11.w, v10.w) + } + vshuff(v15, v14, r28) + { vshuff(v17, v16, r28) + v12.w = vadd(v13.w, v12.w) + v14.w = vadd(v15.w, v14.w) + } + vshuff(v19, v18, r28) + { vshuff(v21, v20, r28) + v16.w = vadd(v17.w, v16.w) + v18.w = vadd(v19.w, v18.w) + } + vshuff(v23, v22, r28) + { vshuff(v25, v24, r28) + v20.w = vadd(v21.w, v20.w) + v22.w = vadd(v23.w, v22.w) + } + vshuff(v27, v26, r28) + { vshuff(v29, v28, r28) + v24.w = vadd(v25.w, v24.w) + v26.w = vadd(v27.w, v26.w) + } + vshuff(v31, v30, r28) + { v28.w = vadd(v29.w, v28.w) + vshuff(v2, v0, r7) + } + { v30.w = vadd(v31.w, v30.w) + vshuff(v6, v4, r7) + v0.w = vadd(v0.w, v2.w) + } + { vshuff(v10, v8, r7) + v1.tmp = vmem(r4 + #0) /* update hist[0-31] */ + v0.w = vadd(v0.w, v1.w) + vmem(r4++#1) = v0.new + } + { vshuff(v14, v12, r7) + v4.w = vadd(v4.w, v6.w) + v8.w = vadd(v8.w, v10.w) + } + { vshuff(v18, v16, r7) + v1.tmp = vmem(r4 + #0) /* update hist[32-63] */ + v4.w = vadd(v4.w, v1.w) + vmem(r4++#1) = v4.new + } + { vshuff(v22, v20, r7) + v12.w = vadd(v12.w, v14.w) + V16.w = vadd(v16.w, v18.w) + } + { vshuff(v26, v24, r7) + v1.tmp = vmem(r4 + #0) /* update hist[64-95] */ + v8.w = vadd(v8.w, v1.w) + vmem(r4++#1) = v8.new + } + { vshuff(v30, v28, r7) + v1.tmp = vmem(r4 + #0) /* update hist[96-127] */ + v12.w = vadd(v12.w, v1.w) + vmem(r4++#1) = v12.new + } + + { v20.w = vadd(v20.w, v22.w) + v1.tmp = vmem(r4 + #0) /* update hist[128-159] */ + v16.w = vadd(v16.w, v1.w) + vmem(r4++#1) = v16.new + } + { v24.w = vadd(v24.w, v26.w) + v1.tmp = vmem(r4 + #0) /* update hist[160-191] */ + v20.w = vadd(v20.w, v1.w) + vmem(r4++#1) = v20.new + } + { v28.w = vadd(v28.w, v30.w) + v1.tmp = vmem(r4 + #0) /* update hist[192-223] */ + v24.w = vadd(v24.w, v1.w) + vmem(r4++#1) = v24.new + } + { v1.tmp = vmem(r4 + #0) /* update hist[224-255] */ + v28.w = vadd(v28.w, v1.w) + vmem(r4++#1) = v28.new + } + jumpr r31 + .size hvx_histogram_row, .-hvx_histogram_row diff --git a/tests/tcg/hexagon/hvx_histogram_row.h b/tests/tcg/hexagon/hvx_histogram_row.h new file mode 100644 index 0000000000..6a4531a92d --- /dev/null +++ b/tests/tcg/hexagon/hvx_histogram_row.h @@ -0,0 +1,24 @@ +/* + * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HVX_HISTOGRAM_ROW_H +#define HVX_HISTOGRAM_ROW_H + +void hvx_histogram_row(uint8_t *src, int stride, int width, int height, + int *hist); + +#endif From 2eb4461ad51724b608f13def13e27ff28dd06c6b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Oct 2021 11:22:11 +0100 Subject: [PATCH 1190/1334] tests/docker: Add debian-nios2-cross image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build the entire cross tool chain from source. For this reason, default to caching. Signed-off-by: Richard Henderson [AJB: honour NOUSER in cached fetch and build, update MAINTAINERS] Signed-off-by: Alex Bennée Message-Id: <20211014224435.2539547-6-richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Willian Rampazzo Message-Id: <20211026102234.3961636-6-alex.bennee@linaro.org> --- MAINTAINERS | 1 + tests/docker/Makefile.include | 25 ++++++ .../debian-nios2-cross.d/build-toolchain.sh | 87 +++++++++++++++++++ .../dockerfiles/debian-toolchain.docker | 36 ++++++++ 4 files changed, 149 insertions(+) create mode 100755 tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh create mode 100644 tests/docker/dockerfiles/debian-toolchain.docker diff --git a/MAINTAINERS b/MAINTAINERS index 7e31135768..ea081bdbd1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -250,6 +250,7 @@ F: target/nios2/ F: hw/nios2/ F: disas/nios2.c F: configs/devices/nios2-softmmu/default.mak +F: tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh OpenRISC TCG CPUs M: Stafford Horne diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index b9d4094c2e..64d6713a51 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -171,10 +171,34 @@ docker-image-debian-hexagon-cross: $(DOCKER_FILES_DIR)/debian-hexagon-cross.dock qemu/debian-hexagon-cross --add-current-user, \ "PREPARE", "debian-hexagon-cross")) +debian-toolchain-run = \ + $(if $(NOCACHE), \ + $(call quiet-command, \ + $(DOCKER_SCRIPT) build -t qemu/$1 -f $< \ + $(if $V,,--quiet) --no-cache \ + --registry $(DOCKER_REGISTRY) --extra-files \ + $(DOCKER_FILES_DIR)/$1.d/build-toolchain.sh, \ + "BUILD", $1), \ + $(call quiet-command, \ + $(DOCKER_SCRIPT) fetch $(if $V,,--quiet) \ + qemu/$1 $(DOCKER_REGISTRY), \ + "FETCH", $1) \ + $(call quiet-command, \ + $(DOCKER_SCRIPT) update $(if $V,,--quiet) \ + qemu/$1 \ + $(if $(NOUSER),,--add-current-user) \ + "PREPARE", $1)) +debian-toolchain = $(call debian-toolchain-run,$(patsubst docker-image-%,%,$1)) + +docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ + $(DOCKER_FILES_DIR)/debian-nios2-cross.d/build-toolchain.sh + $(call debian-toolchain, $@) + # Specialist build images, sometimes very limited tools docker-image-debian-tricore-cross: docker-image-debian10 docker-image-debian-all-test-cross: docker-image-debian10 docker-image-debian-arm64-test-cross: docker-image-debian11 +docker-image-debian-nios2-cross: docker-image-debian10 docker-image-debian-powerpc-test-cross: docker-image-debian11 # These images may be good enough for building tests but not for test builds @@ -183,6 +207,7 @@ DOCKER_PARTIAL_IMAGES += debian-arm64-test-cross DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross DOCKER_PARTIAL_IMAGES += debian-hppa-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross +DOCKER_PARTIAL_IMAGES += debian-nios2-cross DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-tricore-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross diff --git a/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh new file mode 100755 index 0000000000..ba3c9d8aff --- /dev/null +++ b/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +set -e + +TARGET=nios2-linux-gnu +LINUX_ARCH=nios2 + +J=$(expr $(nproc) / 2) +TOOLCHAIN_INSTALL=/usr/local +TOOLCHAIN_BIN=${TOOLCHAIN_INSTALL}/bin +CROSS_SYSROOT=${TOOLCHAIN_INSTALL}/$TARGET/sys-root + +export PATH=${TOOLCHAIN_BIN}:$PATH + +# +# Grab all of the source for the toolchain bootstrap. +# + +wget https://ftp.gnu.org/gnu/binutils/binutils-2.37.tar.xz +wget https://ftp.gnu.org/gnu/gcc/gcc-11.2.0/gcc-11.2.0.tar.xz +wget https://ftp.gnu.org/gnu/glibc/glibc-2.34.tar.xz +wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.70.tar.xz + +tar axf binutils-2.37.tar.xz +tar axf gcc-11.2.0.tar.xz +tar axf glibc-2.34.tar.xz +tar axf linux-5.10.70.tar.xz + +mv binutils-2.37 src-binu +mv gcc-11.2.0 src-gcc +mv glibc-2.34 src-glibc +mv linux-5.10.70 src-linux + +mkdir -p bld-hdr bld-binu bld-gcc bld-glibc +mkdir -p ${CROSS_SYSROOT}/usr/include + +# +# Install kernel and glibc headers +# + +cd src-linux +make headers_install ARCH=${LINUX_ARCH} INSTALL_HDR_PATH=${CROSS_SYSROOT}/usr +cd .. + +cd bld-hdr +../src-glibc/configure --prefix=/usr --host=${TARGET} +make install-headers DESTDIR=${CROSS_SYSROOT} +touch ${CROSS_SYSROOT}/usr/include/gnu/stubs.h +cd .. + +# +# Build binutils +# + +cd bld-binu +../src-binu/configure --disable-werror \ + --prefix=${TOOLCHAIN_INSTALL} --with-sysroot --target=${TARGET} +make -j${J} +make install +cd .. + +# +# Build gcc, without shared libraries, because we do not yet +# have a shared libc against which to link. +# + +cd bld-gcc +../src-gcc/configure --disable-werror --disable-shared \ + --prefix=${TOOLCHAIN_INSTALL} --with-sysroot --target=${TARGET} \ + --enable-languages=c --disable-libssp --disable-libsanitizer \ + --disable-libatomic --disable-libgomp --disable-libquadmath +make -j${J} +make install +cd .. + +# +# Build glibc +# There are a few random things that use c++ but we didn't build that +# cross-compiler. We can get away without them. Disable CXX so that +# glibc doesn't try to use the host c++ compiler. +# + +cd bld-glibc +CXX=false ../src-glibc/configure --prefix=/usr --host=${TARGET} +make -j${j} +make install DESTDIR=${CROSS_SYSROOT} +cd .. diff --git a/tests/docker/dockerfiles/debian-toolchain.docker b/tests/docker/dockerfiles/debian-toolchain.docker new file mode 100644 index 0000000000..738d808aa6 --- /dev/null +++ b/tests/docker/dockerfiles/debian-toolchain.docker @@ -0,0 +1,36 @@ +# +# Docker toolchain cross-compiler +# +# This dockerfile is used for building a cross-compiler toolchain. +# The script for building the toolchain is supplied via extra-files. +# +FROM qemu/debian10 + +# Install build utilities for building gcc and glibc. +# ??? The build-dep isn't working, missing a number of +# minimal build dependiencies, e.g. libmpc. + +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + bison \ + flex \ + gawk \ + libmpc-dev \ + libmpfr-dev \ + rsync \ + texinfo \ + wget && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt build-dep -yy --arch-only gcc glibc + +ADD build-toolchain.sh /root/build-toolchain.sh + +RUN cd /root && ./build-toolchain.sh + +# Throw away the extra toolchain build deps, the downloaded source, +# and the build trees by restoring the original debian10 image, +# then copying the built toolchain from stage 0. +FROM qemu/debian10 +COPY --from=0 /usr/local /usr/local From b58a4e688adf23b04ddd4845f1956d21d4ce87f7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Oct 2021 11:22:12 +0100 Subject: [PATCH 1191/1334] tests/docker: Add debian-microblaze-cross image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build the entire cross tool chain from source. For this reason, default to caching. Signed-off-by: Richard Henderson [AJB: Update MAINTAINERS] Signed-off-by: Alex Bennée Message-Id: <20211014224435.2539547-7-richard.henderson@linaro.org> Reviewed-by: Willian Rampazzo Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211026102234.3961636-7-alex.bennee@linaro.org> --- MAINTAINERS | 1 + tests/docker/Makefile.include | 6 ++ .../build-toolchain.sh | 88 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100755 tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh diff --git a/MAINTAINERS b/MAINTAINERS index ea081bdbd1..75ec9c8628 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -225,6 +225,7 @@ S: Maintained F: target/microblaze/ F: hw/microblaze/ F: disas/microblaze.c +F: tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh MIPS TCG CPUs M: Philippe Mathieu-Daudé diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 64d6713a51..c72cddbde3 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -190,6 +190,10 @@ debian-toolchain-run = \ "PREPARE", $1)) debian-toolchain = $(call debian-toolchain-run,$(patsubst docker-image-%,%,$1)) +docker-image-debian-microblaze-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ + $(DOCKER_FILES_DIR)/debian-microblaze-cross.d/build-toolchain.sh + $(call debian-toolchain, $@) + docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(DOCKER_FILES_DIR)/debian-nios2-cross.d/build-toolchain.sh $(call debian-toolchain, $@) @@ -198,6 +202,7 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ docker-image-debian-tricore-cross: docker-image-debian10 docker-image-debian-all-test-cross: docker-image-debian10 docker-image-debian-arm64-test-cross: docker-image-debian11 +docker-image-debian-microblaze-cross: docker-image-debian10 docker-image-debian-nios2-cross: docker-image-debian10 docker-image-debian-powerpc-test-cross: docker-image-debian11 @@ -207,6 +212,7 @@ DOCKER_PARTIAL_IMAGES += debian-arm64-test-cross DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross DOCKER_PARTIAL_IMAGES += debian-hppa-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross +DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-tricore-cross diff --git a/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh new file mode 100755 index 0000000000..23ec0aa9a7 --- /dev/null +++ b/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +set -e + +TARGET=microblaze-linux-musl +LINUX_ARCH=microblaze + +J=$(expr $(nproc) / 2) +TOOLCHAIN_INSTALL=/usr/local +TOOLCHAIN_BIN=${TOOLCHAIN_INSTALL}/bin +CROSS_SYSROOT=${TOOLCHAIN_INSTALL}/$TARGET/sys-root + +export PATH=${TOOLCHAIN_BIN}:$PATH + +# +# Grab all of the source for the toolchain bootstrap. +# + +wget https://ftp.gnu.org/gnu/binutils/binutils-2.37.tar.xz +wget https://ftp.gnu.org/gnu/gcc/gcc-11.2.0/gcc-11.2.0.tar.xz +wget https://www.musl-libc.org/releases/musl-1.2.2.tar.gz +wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.70.tar.xz + +tar axf binutils-2.37.tar.xz +tar axf gcc-11.2.0.tar.xz +tar axf musl-1.2.2.tar.gz +tar axf linux-5.10.70.tar.xz + +mv binutils-2.37 src-binu +mv gcc-11.2.0 src-gcc +mv musl-1.2.2 src-musl +mv linux-5.10.70 src-linux + +mkdir -p bld-hdr bld-binu bld-gcc bld-musl +mkdir -p ${CROSS_SYSROOT}/usr/include + +# +# Install kernel headers +# + +cd src-linux +make headers_install ARCH=${LINUX_ARCH} INSTALL_HDR_PATH=${CROSS_SYSROOT}/usr +cd .. + +# +# Build binutils +# + +cd bld-binu +../src-binu/configure --disable-werror \ + --prefix=${TOOLCHAIN_INSTALL} --with-sysroot --target=${TARGET} +make -j${J} +make install +cd .. + +# +# Build gcc, just the compiler so far. +# + +cd bld-gcc +../src-gcc/configure --disable-werror --disable-shared \ + --prefix=${TOOLCHAIN_INSTALL} --with-sysroot --target=${TARGET} \ + --enable-languages=c --disable-libssp --disable-libsanitizer \ + --disable-libatomic --disable-libgomp --disable-libquadmath +make -j${J} all-gcc +make install-gcc +cd .. + +# +# Build musl. +# We won't go through the extra step of building shared libraries +# because we don't actually use them in QEMU docker testing. +# + +cd bld-musl +../src-musl/configure --prefix=/usr --host=${TARGET} --disable-shared +make -j${j} +make install DESTDIR=${CROSS_SYSROOT} +cd .. + +# +# Go back and build the compiler runtime +# + +cd bld-gcc +make -j${j} +make install +cd .. From d654283d0b18bce73fde862932a1fd01cf44f5c1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Oct 2021 11:22:13 +0100 Subject: [PATCH 1192/1334] tests/tcg: Enable container_cross_cc for microblaze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20211014224435.2539547-8-richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211026102234.3961636-8-alex.bennee@linaro.org> --- tests/tcg/configure.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index 1f985ccfc0..ccf84e5559 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -55,6 +55,7 @@ fi : ${cross_cc_i386="i686-linux-gnu-gcc"} : ${cross_cc_cflags_i386="-m32"} : ${cross_cc_m68k="m68k-linux-gnu-gcc"} +: ${cross_cc_microblaze="microblaze-linux-musl-gcc"} : $(cross_cc_mips64el="mips64el-linux-gnuabi64-gcc") : $(cross_cc_mips64="mips64-linux-gnuabi64-gcc") : $(cross_cc_mipsel="mipsel-linux-gnu-gcc") @@ -133,6 +134,11 @@ for target in $target_list; do container_image=debian-m68k-cross container_cross_cc=m68k-linux-gnu-gcc ;; + microblaze-*) + container_hosts=x86_64 + container_image=debian-microblaze-cross + container_cross_cc=microblaze-linux-musl-gcc + ;; mips64el-*) container_hosts=x86_64 container_image=debian-mips64el-cross From 73594998f7f6cd6cb755b474aebf62ea247bb3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Oct 2021 11:22:14 +0100 Subject: [PATCH 1193/1334] tests/tcg: Fix some targets default cross compiler path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not want a shell command substitution, but a parameter substitution (with assignment). Replace $() -> ${}, otherwise the expanded command return an empty string and the $cross_cc variable is not set. Fixes: 634ef789f8e ("tests/tcg: add more default compilers to configure.sh") Signed-off-by: Philippe Mathieu-Daudé [AJB: disable sh4 linux-test] Signed-off-by: Alex Bennée Message-Id: <20211023164329.328137-1-f4bug@amsat.org> Reviewed-by: Richard Henderson Message-Id: <20211026102234.3961636-9-alex.bennee@linaro.org> --- tests/tcg/configure.sh | 14 +++++++------- tests/tcg/sh4/Makefile.target | 6 ++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index ccf84e5559..d74ed50c18 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -46,7 +46,7 @@ fi : ${cross_cc_aarch64="aarch64-linux-gnu-gcc"} : ${cross_cc_aarch64_be="$cross_cc_aarch64"} : ${cross_cc_cflags_aarch64_be="-mbig-endian"} -: $(cross_cc_alpha="alpha-linux-gnu-gcc") +: ${cross_cc_alpha="alpha-linux-gnu-gcc"} : ${cross_cc_arm="arm-linux-gnueabihf-gcc"} : ${cross_cc_cflags_armeb="-mbig-endian"} : ${cross_cc_hexagon="hexagon-unknown-linux-musl-clang"} @@ -56,17 +56,17 @@ fi : ${cross_cc_cflags_i386="-m32"} : ${cross_cc_m68k="m68k-linux-gnu-gcc"} : ${cross_cc_microblaze="microblaze-linux-musl-gcc"} -: $(cross_cc_mips64el="mips64el-linux-gnuabi64-gcc") -: $(cross_cc_mips64="mips64-linux-gnuabi64-gcc") -: $(cross_cc_mipsel="mipsel-linux-gnu-gcc") -: $(cross_cc_mips="mips-linux-gnu-gcc") +: ${cross_cc_mips64el="mips64el-linux-gnuabi64-gcc"} +: ${cross_cc_mips64="mips64-linux-gnuabi64-gcc"} +: ${cross_cc_mipsel="mipsel-linux-gnu-gcc"} +: ${cross_cc_mips="mips-linux-gnu-gcc"} : ${cross_cc_ppc="powerpc-linux-gnu-gcc"} : ${cross_cc_cflags_ppc="-m32"} : ${cross_cc_ppc64="powerpc64-linux-gnu-gcc"} : ${cross_cc_ppc64le="powerpc64le-linux-gnu-gcc"} -: $(cross_cc_riscv64="riscv64-linux-gnu-gcc") +: ${cross_cc_riscv64="riscv64-linux-gnu-gcc"} : ${cross_cc_s390x="s390x-linux-gnu-gcc"} -: $(cross_cc_sh4="sh4-linux-gnu-gcc") +: ${cross_cc_sh4="sh4-linux-gnu-gcc"} : ${cross_cc_cflags_sparc="-m32 -mv8plus -mcpu=ultrasparc"} : ${cross_cc_sparc64="sparc64-linux-gnu-gcc"} : ${cross_cc_cflags_sparc64="-m64 -mcpu=ultrasparc"} diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 47c39a44b6..0e96aeff16 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -12,3 +12,9 @@ run-signals: signals $(call skip-test, $<, "BROKEN") run-plugin-signals-with-%: $(call skip-test, $<, "BROKEN") + +# This test is currently broken: https://gitlab.com/qemu-project/qemu/-/issues/704 +run-linux-test: linux-test + $(call skip-test, $<, "BROKEN") +run-plugin-linux-test-with-%: + $(call skip-test, $<, "BROKEN") From a7181a287614b716e3fe2f79deec002232c2a360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 26 Oct 2021 11:22:15 +0100 Subject: [PATCH 1194/1334] tests/docker: split PARTIAL into PARTIAL and VIRTUAL images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is mostly to ensure we don't include the toolchain and bootstrap builds in DOCKER_IMAGES which is useful when verifying all images still build. Signed-off-by: Alex Bennée Reviewed-by: Willian Rampazzo Reviewed-by: Richard Henderson Message-Id: <20211026102234.3961636-10-alex.bennee@linaro.org> --- tests/docker/Makefile.include | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index c72cddbde3..7a63a3b7f7 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -11,8 +11,10 @@ HOST_ARCH = $(if $(ARCH),$(ARCH),$(shell uname -m)) DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles # we don't run tests on intermediate images (used as base by another image) -DOCKER_PARTIAL_IMAGES := debian10 debian11 debian-bootstrap empty -DOCKER_IMAGES := $(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker)))) +DOCKER_PARTIAL_IMAGES := debian10 debian11 +# we don't directly build virtual images (they are used to build other images) +DOCKER_VIRTUAL_IMAGES := debian-bootstrap debian-toolchain empty +DOCKER_IMAGES := $(sort $(filter-out $(DOCKER_VIRTUAL_IMAGES), $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) # Use a global constant ccache directory to speed up repetitive builds DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache @@ -226,7 +228,7 @@ DOCKER_PARTIAL_IMAGES += fedora-cris-cross # packages. # Expand all the pre-requistes for each docker image and test combination -$(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES)), \ +$(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES) $(DOCKER_VIRTUAL_IMAGES),$(DOCKER_IMAGES)), \ $(foreach t,$(DOCKER_TESTS), \ $(eval .PHONY: docker-$t@$i) \ $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ From 67f80eb4d0813788046a204595345d1195e767fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 26 Oct 2021 11:22:17 +0100 Subject: [PATCH 1195/1334] tests/tcg: enable debian-nios2-cross for test building MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have a nios2 test image we can start using it to build tests. However signal handling in nios2 is still broken so we disable the signals and linux-test tests that trigger the bug. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211026102234.3961636-12-alex.bennee@linaro.org> --- MAINTAINERS | 1 + tests/tcg/configure.sh | 6 ++++++ tests/tcg/nios2/Makefile.target | 11 +++++++++++ 3 files changed, 18 insertions(+) create mode 100644 tests/tcg/nios2/Makefile.target diff --git a/MAINTAINERS b/MAINTAINERS index 75ec9c8628..797be5b366 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -226,6 +226,7 @@ F: target/microblaze/ F: hw/microblaze/ F: disas/microblaze.c F: tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh +F: tests/tcg/nios2/Makefile.target MIPS TCG CPUs M: Philippe Mathieu-Daudé diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index d74ed50c18..9b76f58258 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -60,6 +60,7 @@ fi : ${cross_cc_mips64="mips64-linux-gnuabi64-gcc"} : ${cross_cc_mipsel="mipsel-linux-gnu-gcc"} : ${cross_cc_mips="mips-linux-gnu-gcc"} +: ${cross_cc_nios2="nios2-linux-gnu-gcc"} : ${cross_cc_ppc="powerpc-linux-gnu-gcc"} : ${cross_cc_cflags_ppc="-m32"} : ${cross_cc_ppc64="powerpc64-linux-gnu-gcc"} @@ -159,6 +160,11 @@ for target in $target_list; do container_image=debian-mips-cross container_cross_cc=mips-linux-gnu-gcc ;; + nios2-*) + container_hosts=x86_64 + container_image=debian-nios2-cross + container_cross_cc=nios2-linux-gnu-gcc + ;; ppc-*|ppc64abi32-*) container_hosts=x86_64 container_image=debian-powerpc-test-cross diff --git a/tests/tcg/nios2/Makefile.target b/tests/tcg/nios2/Makefile.target new file mode 100644 index 0000000000..b38e2352b7 --- /dev/null +++ b/tests/tcg/nios2/Makefile.target @@ -0,0 +1,11 @@ +# nios2 specific test tweaks + +# Currently nios2 signal handling is broken +run-signals: signals + $(call skip-test, $<, "BROKEN") +run-plugin-signals-with-%: + $(call skip-test, $<, "BROKEN") +run-linux-test: linux-test + $(call skip-test, $<, "BROKEN") +run-plugin-linux-test-with-%: + $(call skip-test, $<, "BROKEN") From 482cd5f08eeaea2722717ebe65ef0dc8dd1243b5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 26 Oct 2021 11:22:18 +0100 Subject: [PATCH 1196/1334] ebpf: really include it only in system emulators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eBPF libraries are being included in user emulators, which is useless and also breaks --static compilation if a shared library for libbpf is present in the system. Reported-by: Alex Bennée Signed-off-by: Paolo Bonzini Signed-off-by: Alex Bennée Message-Id: <20211012162252.263933-1-pbonzini@redhat.com> Reviewed-by: Richard Henderson Reviewed-bt: Warner Losh Message-Id: <20211026102234.3961636-13-alex.bennee@linaro.org> --- meson.build | 2 -- 1 file changed, 2 deletions(-) diff --git a/meson.build b/meson.build index c4aec7355a..47df10afc2 100644 --- a/meson.build +++ b/meson.build @@ -2597,8 +2597,6 @@ subdir('bsd-user') subdir('linux-user') subdir('ebpf') -common_ss.add(libbpf) - specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss) linux_user_ss.add(files('thunk.c')) From ad039c506edaf11fc39fca218c751b26521da21c Mon Sep 17 00:00:00 2001 From: Mahmoud Mandour Date: Tue, 26 Oct 2021 11:22:19 +0100 Subject: [PATCH 1197/1334] plugins/cache: freed heap-allocated mutexes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mahmoud Mandour Signed-off-by: Alex Bennée Message-Id: <20210810134844.166490-2-ma.mandourr@gmail.com> Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211026102234.3961636-14-alex.bennee@linaro.org> --- contrib/plugins/cache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index a1e03ca882..a255e26e25 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -614,6 +614,9 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) caches_free(dcaches); caches_free(icaches); + g_free(dcache_locks); + g_free(icache_locks); + g_hash_table_destroy(miss_ht); } From 14f3110a99d4edf683c6b503ca02dba09a124aff Mon Sep 17 00:00:00 2001 From: Mahmoud Mandour Date: Tue, 26 Oct 2021 11:22:20 +0100 Subject: [PATCH 1198/1334] plugins/cache: implement unified L2 cache emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds an implementation of a simple L2 configuration, in which a unified L2 cache (stores both blocks of instructions and data) is maintained for each core separately, with no inter-core interaction taken in account. The L2 cache is used as a backup for L1 and is only accessed if the wanted block does not exist in L1. In terms of multi-threaded user-space emulation, the same approximation of L1 is done, a static number of caches is maintained, and each and every memory access initiated by a thread will have to go through one of the available caches. An atomic increment is used to maintain the number of L2 misses per instruction. The default cache parameters of L2 caches is: 2MB cache size 16-way associativity 64-byte blocks Signed-off-by: Mahmoud Mandour Signed-off-by: Alex Bennée Message-Id: <20210810134844.166490-3-ma.mandourr@gmail.com> Message-Id: <20211026102234.3961636-15-alex.bennee@linaro.org> --- contrib/plugins/cache.c | 256 +++++++++++++++++++++++++++------------- 1 file changed, 175 insertions(+), 81 deletions(-) diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index a255e26e25..908c967a09 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -82,8 +82,9 @@ typedef struct { char *disas_str; const char *symbol; uint64_t addr; - uint64_t dmisses; - uint64_t imisses; + uint64_t l1_dmisses; + uint64_t l1_imisses; + uint64_t l2_misses; } InsnData; void (*update_hit)(Cache *cache, int set, int blk); @@ -93,15 +94,20 @@ void (*metadata_init)(Cache *cache); void (*metadata_destroy)(Cache *cache); static int cores; -static Cache **dcaches, **icaches; +static Cache **l1_dcaches, **l1_icaches; +static Cache **l2_ucaches; -static GMutex *dcache_locks; -static GMutex *icache_locks; +static GMutex *l1_dcache_locks; +static GMutex *l1_icache_locks; +static GMutex *l2_ucache_locks; -static uint64_t all_dmem_accesses; -static uint64_t all_imem_accesses; -static uint64_t all_imisses; -static uint64_t all_dmisses; +static uint64_t l1_dmem_accesses; +static uint64_t l1_imem_accesses; +static uint64_t l1_imisses; +static uint64_t l1_dmisses; + +static uint64_t l2_mem_accesses; +static uint64_t l2_misses; static int pow_of_two(int num) { @@ -382,6 +388,7 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, struct qemu_plugin_hwaddr *hwaddr; int cache_idx; InsnData *insn; + bool hit_in_l1; hwaddr = qemu_plugin_get_hwaddr(info, vaddr); if (hwaddr && qemu_plugin_hwaddr_is_io(hwaddr)) { @@ -391,14 +398,29 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, effective_addr = hwaddr ? qemu_plugin_hwaddr_phys_addr(hwaddr) : vaddr; cache_idx = vcpu_index % cores; - g_mutex_lock(&dcache_locks[cache_idx]); - if (!access_cache(dcaches[cache_idx], effective_addr)) { + g_mutex_lock(&l1_dcache_locks[cache_idx]); + hit_in_l1 = access_cache(l1_dcaches[cache_idx], effective_addr); + if (!hit_in_l1) { insn = (InsnData *) userdata; - __atomic_fetch_add(&insn->dmisses, 1, __ATOMIC_SEQ_CST); - dcaches[cache_idx]->misses++; + __atomic_fetch_add(&insn->l1_dmisses, 1, __ATOMIC_SEQ_CST); + l1_dcaches[cache_idx]->misses++; } - dcaches[cache_idx]->accesses++; - g_mutex_unlock(&dcache_locks[cache_idx]); + l1_dcaches[cache_idx]->accesses++; + g_mutex_unlock(&l1_dcache_locks[cache_idx]); + + if (hit_in_l1) { + /* No need to access L2 */ + return; + } + + g_mutex_lock(&l2_ucache_locks[cache_idx]); + if (!access_cache(l2_ucaches[cache_idx], effective_addr)) { + insn = (InsnData *) userdata; + __atomic_fetch_add(&insn->l2_misses, 1, __ATOMIC_SEQ_CST); + l2_ucaches[cache_idx]->misses++; + } + l2_ucaches[cache_idx]->accesses++; + g_mutex_unlock(&l2_ucache_locks[cache_idx]); } static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) @@ -406,18 +428,34 @@ static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) uint64_t insn_addr; InsnData *insn; int cache_idx; + bool hit_in_l1; insn_addr = ((InsnData *) userdata)->addr; cache_idx = vcpu_index % cores; - g_mutex_lock(&icache_locks[cache_idx]); - if (!access_cache(icaches[cache_idx], insn_addr)) { + g_mutex_lock(&l1_icache_locks[cache_idx]); + hit_in_l1 = access_cache(l1_icaches[cache_idx], insn_addr); + if (!hit_in_l1) { insn = (InsnData *) userdata; - __atomic_fetch_add(&insn->imisses, 1, __ATOMIC_SEQ_CST); - icaches[cache_idx]->misses++; + __atomic_fetch_add(&insn->l1_imisses, 1, __ATOMIC_SEQ_CST); + l1_icaches[cache_idx]->misses++; } - icaches[cache_idx]->accesses++; - g_mutex_unlock(&icache_locks[cache_idx]); + l1_icaches[cache_idx]->accesses++; + g_mutex_unlock(&l1_icache_locks[cache_idx]); + + if (hit_in_l1) { + /* No need to access L2 */ + return; + } + + g_mutex_lock(&l2_ucache_locks[cache_idx]); + if (!access_cache(l2_ucaches[cache_idx], insn_addr)) { + insn = (InsnData *) userdata; + __atomic_fetch_add(&insn->l2_misses, 1, __ATOMIC_SEQ_CST); + l2_ucaches[cache_idx]->misses++; + } + l2_ucaches[cache_idx]->accesses++; + g_mutex_unlock(&l2_ucache_locks[cache_idx]); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -493,30 +531,28 @@ static void caches_free(Cache **caches) } } -static int dcmp(gconstpointer a, gconstpointer b) +static void append_stats_line(GString *line, uint64_t l1_daccess, + uint64_t l1_dmisses, uint64_t l1_iaccess, + uint64_t l1_imisses, uint64_t l2_access, + uint64_t l2_misses) { - InsnData *insn_a = (InsnData *) a; - InsnData *insn_b = (InsnData *) b; + double l1_dmiss_rate, l1_imiss_rate, l2_miss_rate; - return insn_a->dmisses < insn_b->dmisses ? 1 : -1; -} - -static void append_stats_line(GString *line, uint64_t daccess, uint64_t dmisses, - uint64_t iaccess, uint64_t imisses) -{ - double dmiss_rate, imiss_rate; - - dmiss_rate = ((double) dmisses) / (daccess) * 100.0; - imiss_rate = ((double) imisses) / (iaccess) * 100.0; + l1_dmiss_rate = ((double) l1_dmisses) / (l1_daccess) * 100.0; + l1_imiss_rate = ((double) l1_imisses) / (l1_iaccess) * 100.0; + l2_miss_rate = ((double) l2_misses) / (l2_access) * 100.0; g_string_append_printf(line, "%-14lu %-12lu %9.4lf%% %-14lu %-12lu" - " %9.4lf%%\n", - daccess, - dmisses, - daccess ? dmiss_rate : 0.0, - iaccess, - imisses, - iaccess ? imiss_rate : 0.0); + " %9.4lf%% %-12lu %-11lu %10.4lf%%\n", + l1_daccess, + l1_dmisses, + l1_daccess ? l1_dmiss_rate : 0.0, + l1_iaccess, + l1_imisses, + l1_iaccess ? l1_imiss_rate : 0.0, + l2_access, + l2_misses, + l2_access ? l2_miss_rate : 0.0); } static void sum_stats(void) @@ -525,43 +561,66 @@ static void sum_stats(void) g_assert(cores > 1); for (i = 0; i < cores; i++) { - all_imisses += icaches[i]->misses; - all_dmisses += dcaches[i]->misses; - all_imem_accesses += icaches[i]->accesses; - all_dmem_accesses += dcaches[i]->accesses; + l1_imisses += l1_icaches[i]->misses; + l1_dmisses += l1_dcaches[i]->misses; + l1_imem_accesses += l1_icaches[i]->accesses; + l1_dmem_accesses += l1_dcaches[i]->accesses; + + l2_misses += l2_ucaches[i]->misses; + l2_mem_accesses += l2_ucaches[i]->accesses; } } +static int dcmp(gconstpointer a, gconstpointer b) +{ + InsnData *insn_a = (InsnData *) a; + InsnData *insn_b = (InsnData *) b; + + return insn_a->l1_dmisses < insn_b->l1_dmisses ? 1 : -1; +} + static int icmp(gconstpointer a, gconstpointer b) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; - return insn_a->imisses < insn_b->imisses ? 1 : -1; + return insn_a->l1_imisses < insn_b->l1_imisses ? 1 : -1; +} + +static int l2_cmp(gconstpointer a, gconstpointer b) +{ + InsnData *insn_a = (InsnData *) a; + InsnData *insn_b = (InsnData *) b; + + return insn_a->l2_misses < insn_b->l2_misses ? 1 : -1; } static void log_stats(void) { int i; - Cache *icache, *dcache; + Cache *icache, *dcache, *l2_cache; g_autoptr(GString) rep = g_string_new("core #, data accesses, data misses," " dmiss rate, insn accesses," - " insn misses, imiss rate\n"); + " insn misses, imiss rate," + " l2 accesses, l2 misses," + " l2 miss rate\n"); for (i = 0; i < cores; i++) { g_string_append_printf(rep, "%-8d", i); - dcache = dcaches[i]; - icache = icaches[i]; + dcache = l1_dcaches[i]; + icache = l1_icaches[i]; + l2_cache = l2_ucaches[i]; append_stats_line(rep, dcache->accesses, dcache->misses, - icache->accesses, icache->misses); + icache->accesses, icache->misses, l2_cache->accesses, + l2_cache->misses); } if (cores > 1) { sum_stats(); g_string_append_printf(rep, "%-8s", "sum"); - append_stats_line(rep, all_dmem_accesses, all_dmisses, - all_imem_accesses, all_imisses); + append_stats_line(rep, l1_dmem_accesses, l1_dmisses, + l1_imem_accesses, l1_imisses, l2_mem_accesses, l2_misses); } g_string_append(rep, "\n"); @@ -585,7 +644,7 @@ static void log_top_insns(void) if (insn->symbol) { g_string_append_printf(rep, " (%s)", insn->symbol); } - g_string_append_printf(rep, ", %ld, %s\n", insn->dmisses, + g_string_append_printf(rep, ", %ld, %s\n", insn->l1_dmisses, insn->disas_str); } @@ -598,7 +657,20 @@ static void log_top_insns(void) if (insn->symbol) { g_string_append_printf(rep, " (%s)", insn->symbol); } - g_string_append_printf(rep, ", %ld, %s\n", insn->imisses, + g_string_append_printf(rep, ", %ld, %s\n", insn->l1_imisses, + insn->disas_str); + } + + miss_insns = g_list_sort(miss_insns, l2_cmp); + g_string_append_printf(rep, "%s", "\naddress, L2 misses, instruction\n"); + + for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { + insn = (InsnData *) curr->data; + g_string_append_printf(rep, "0x%" PRIx64, insn->addr); + if (insn->symbol) { + g_string_append_printf(rep, " (%s)", insn->symbol); + } + g_string_append_printf(rep, ", %ld, %s\n", insn->l2_misses, insn->disas_str); } @@ -611,11 +683,13 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) log_stats(); log_top_insns(); - caches_free(dcaches); - caches_free(icaches); + caches_free(l1_dcaches); + caches_free(l1_icaches); + caches_free(l2_ucaches); - g_free(dcache_locks); - g_free(icache_locks); + g_free(l1_dcache_locks); + g_free(l1_icache_locks); + g_free(l2_ucache_locks); g_hash_table_destroy(miss_ht); } @@ -647,19 +721,24 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { int i; - int iassoc, iblksize, icachesize; - int dassoc, dblksize, dcachesize; + int l1_iassoc, l1_iblksize, l1_icachesize; + int l1_dassoc, l1_dblksize, l1_dcachesize; + int l2_assoc, l2_blksize, l2_cachesize; limit = 32; sys = info->system_emulation; - dassoc = 8; - dblksize = 64; - dcachesize = dblksize * dassoc * 32; + l1_dassoc = 8; + l1_dblksize = 64; + l1_dcachesize = l1_dblksize * l1_dassoc * 32; - iassoc = 8; - iblksize = 64; - icachesize = iblksize * iassoc * 32; + l1_iassoc = 8; + l1_iblksize = 64; + l1_icachesize = l1_iblksize * l1_iassoc * 32; + + l2_assoc = 16; + l2_blksize = 64; + l2_cachesize = l2_assoc * l2_blksize * 2048; policy = LRU; @@ -668,21 +747,27 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, for (i = 0; i < argc; i++) { char *opt = argv[i]; if (g_str_has_prefix(opt, "iblksize=")) { - iblksize = g_ascii_strtoll(opt + 9, NULL, 10); + l1_iblksize = g_ascii_strtoll(opt + 9, NULL, 10); } else if (g_str_has_prefix(opt, "iassoc=")) { - iassoc = g_ascii_strtoll(opt + 7, NULL, 10); + l1_iassoc = g_ascii_strtoll(opt + 7, NULL, 10); } else if (g_str_has_prefix(opt, "icachesize=")) { - icachesize = g_ascii_strtoll(opt + 11, NULL, 10); + l1_icachesize = g_ascii_strtoll(opt + 11, NULL, 10); } else if (g_str_has_prefix(opt, "dblksize=")) { - dblksize = g_ascii_strtoll(opt + 9, NULL, 10); + l1_dblksize = g_ascii_strtoll(opt + 9, NULL, 10); } else if (g_str_has_prefix(opt, "dassoc=")) { - dassoc = g_ascii_strtoll(opt + 7, NULL, 10); + l1_dassoc = g_ascii_strtoll(opt + 7, NULL, 10); } else if (g_str_has_prefix(opt, "dcachesize=")) { - dcachesize = g_ascii_strtoll(opt + 11, NULL, 10); + l1_dcachesize = g_ascii_strtoll(opt + 11, NULL, 10); } else if (g_str_has_prefix(opt, "limit=")) { limit = g_ascii_strtoll(opt + 6, NULL, 10); } else if (g_str_has_prefix(opt, "cores=")) { cores = g_ascii_strtoll(opt + 6, NULL, 10); + } else if (g_str_has_prefix(opt, "l2cachesize=")) { + l2_cachesize = g_ascii_strtoll(opt + 6, NULL, 10); + } else if (g_str_has_prefix(opt, "l2blksize=")) { + l2_blksize = g_ascii_strtoll(opt + 6, NULL, 10); + } else if (g_str_has_prefix(opt, "l2assoc=")) { + l2_assoc = g_ascii_strtoll(opt + 6, NULL, 10); } else if (g_str_has_prefix(opt, "evict=")) { gchar *p = opt + 6; if (g_strcmp0(p, "rand") == 0) { @@ -703,24 +788,33 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, policy_init(); - dcaches = caches_init(dblksize, dassoc, dcachesize); - if (!dcaches) { - const char *err = cache_config_error(dblksize, dassoc, dcachesize); + l1_dcaches = caches_init(l1_dblksize, l1_dassoc, l1_dcachesize); + if (!l1_dcaches) { + const char *err = cache_config_error(l1_dblksize, l1_dassoc, l1_dcachesize); fprintf(stderr, "dcache cannot be constructed from given parameters\n"); fprintf(stderr, "%s\n", err); return -1; } - icaches = caches_init(iblksize, iassoc, icachesize); - if (!icaches) { - const char *err = cache_config_error(iblksize, iassoc, icachesize); + l1_icaches = caches_init(l1_iblksize, l1_iassoc, l1_icachesize); + if (!l1_icaches) { + const char *err = cache_config_error(l1_iblksize, l1_iassoc, l1_icachesize); fprintf(stderr, "icache cannot be constructed from given parameters\n"); fprintf(stderr, "%s\n", err); return -1; } - dcache_locks = g_new0(GMutex, cores); - icache_locks = g_new0(GMutex, cores); + l2_ucaches = caches_init(l2_blksize, l2_assoc, l2_cachesize); + if (!l2_ucaches) { + const char *err = cache_config_error(l2_blksize, l2_assoc, l2_cachesize); + fprintf(stderr, "L2 cache cannot be constructed from given parameters\n"); + fprintf(stderr, "%s\n", err); + return -1; + } + + l1_dcache_locks = g_new0(GMutex, cores); + l1_icache_locks = g_new0(GMutex, cores); + l2_ucache_locks = g_new0(GMutex, cores); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); From 53366adf9c0c6b00ec54ff3074037c82995c7ed0 Mon Sep 17 00:00:00 2001 From: Mahmoud Mandour Date: Tue, 26 Oct 2021 11:22:21 +0100 Subject: [PATCH 1199/1334] plugins/cache: split command line arguments into name and value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way of handling args is more lenient and sets a better framework to parse boolean command line arguments. Signed-off-by: Mahmoud Mandour Signed-off-by: Alex Bennée Message-Id: <20210810134844.166490-4-ma.mandourr@gmail.com> Message-Id: <20211026102234.3961636-16-alex.bennee@linaro.org> --- contrib/plugins/cache.c | 57 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index 908c967a09..ff325beb9f 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -11,6 +11,8 @@ #include +#define STRTOLL(x) g_ascii_strtoll(x, NULL, 10) + QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; @@ -746,35 +748,36 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, for (i = 0; i < argc; i++) { char *opt = argv[i]; - if (g_str_has_prefix(opt, "iblksize=")) { - l1_iblksize = g_ascii_strtoll(opt + 9, NULL, 10); - } else if (g_str_has_prefix(opt, "iassoc=")) { - l1_iassoc = g_ascii_strtoll(opt + 7, NULL, 10); - } else if (g_str_has_prefix(opt, "icachesize=")) { - l1_icachesize = g_ascii_strtoll(opt + 11, NULL, 10); - } else if (g_str_has_prefix(opt, "dblksize=")) { - l1_dblksize = g_ascii_strtoll(opt + 9, NULL, 10); - } else if (g_str_has_prefix(opt, "dassoc=")) { - l1_dassoc = g_ascii_strtoll(opt + 7, NULL, 10); - } else if (g_str_has_prefix(opt, "dcachesize=")) { - l1_dcachesize = g_ascii_strtoll(opt + 11, NULL, 10); - } else if (g_str_has_prefix(opt, "limit=")) { - limit = g_ascii_strtoll(opt + 6, NULL, 10); - } else if (g_str_has_prefix(opt, "cores=")) { - cores = g_ascii_strtoll(opt + 6, NULL, 10); - } else if (g_str_has_prefix(opt, "l2cachesize=")) { - l2_cachesize = g_ascii_strtoll(opt + 6, NULL, 10); - } else if (g_str_has_prefix(opt, "l2blksize=")) { - l2_blksize = g_ascii_strtoll(opt + 6, NULL, 10); - } else if (g_str_has_prefix(opt, "l2assoc=")) { - l2_assoc = g_ascii_strtoll(opt + 6, NULL, 10); - } else if (g_str_has_prefix(opt, "evict=")) { - gchar *p = opt + 6; - if (g_strcmp0(p, "rand") == 0) { + g_autofree char **tokens = g_strsplit(opt, "=", 2); + + if (g_strcmp0(tokens[0], "iblksize") == 0) { + l1_iblksize = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "iassoc") == 0) { + l1_iassoc = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "icachesize") == 0) { + l1_icachesize = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "dblksize") == 0) { + l1_dblksize = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "dassoc") == 0) { + l1_dassoc = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "dcachesize") == 0) { + l1_dcachesize = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "limit") == 0) { + limit = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "cores") == 0) { + cores = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "l2cachesize") == 0) { + l2_cachesize = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "l2blksize") == 0) { + l2_blksize = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "l2assoc") == 0) { + l2_assoc = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "evict") == 0) { + if (g_strcmp0(tokens[1], "rand") == 0) { policy = RAND; - } else if (g_strcmp0(p, "lru") == 0) { + } else if (g_strcmp0(tokens[1], "lru") == 0) { policy = LRU; - } else if (g_strcmp0(p, "fifo") == 0) { + } else if (g_strcmp0(tokens[1], "fifo") == 0) { policy = FIFO; } else { fprintf(stderr, "invalid eviction policy: %s\n", opt); From 447f935674214dd4e449cc4a27711489779cd1ff Mon Sep 17 00:00:00 2001 From: Mahmoud Mandour Date: Tue, 26 Oct 2021 11:22:22 +0100 Subject: [PATCH 1200/1334] plugins/cache: make L2 emulation optional through args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default L2 is not enabled and is enabled by either using the newly-introduced "l2" boolean argument, or by setting any of the L2 cache parameters using args. On specifying "l2=on", the default cache configuration is used. Signed-off-by: Mahmoud Mandour Signed-off-by: Alex Bennée Message-Id: <20210810134844.166490-5-ma.mandourr@gmail.com> Message-Id: <20211026102234.3961636-17-alex.bennee@linaro.org> --- contrib/plugins/cache.c | 76 +++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index ff325beb9f..b9226e7c40 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -97,6 +97,8 @@ void (*metadata_destroy)(Cache *cache); static int cores; static Cache **l1_dcaches, **l1_icaches; + +static bool use_l2; static Cache **l2_ucaches; static GMutex *l1_dcache_locks; @@ -410,7 +412,7 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, l1_dcaches[cache_idx]->accesses++; g_mutex_unlock(&l1_dcache_locks[cache_idx]); - if (hit_in_l1) { + if (hit_in_l1 || !use_l2) { /* No need to access L2 */ return; } @@ -445,7 +447,7 @@ static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) l1_icaches[cache_idx]->accesses++; g_mutex_unlock(&l1_icache_locks[cache_idx]); - if (hit_in_l1) { + if (hit_in_l1 || !use_l2) { /* No need to access L2 */ return; } @@ -542,19 +544,25 @@ static void append_stats_line(GString *line, uint64_t l1_daccess, l1_dmiss_rate = ((double) l1_dmisses) / (l1_daccess) * 100.0; l1_imiss_rate = ((double) l1_imisses) / (l1_iaccess) * 100.0; - l2_miss_rate = ((double) l2_misses) / (l2_access) * 100.0; g_string_append_printf(line, "%-14lu %-12lu %9.4lf%% %-14lu %-12lu" - " %9.4lf%% %-12lu %-11lu %10.4lf%%\n", + " %9.4lf%%", l1_daccess, l1_dmisses, l1_daccess ? l1_dmiss_rate : 0.0, l1_iaccess, l1_imisses, - l1_iaccess ? l1_imiss_rate : 0.0, - l2_access, - l2_misses, - l2_access ? l2_miss_rate : 0.0); + l1_iaccess ? l1_imiss_rate : 0.0); + + if (use_l2) { + l2_miss_rate = ((double) l2_misses) / (l2_access) * 100.0; + g_string_append_printf(line, " %-12lu %-11lu %10.4lf%%", + l2_access, + l2_misses, + l2_access ? l2_miss_rate : 0.0); + } + + g_string_append(line, "\n"); } static void sum_stats(void) @@ -568,8 +576,10 @@ static void sum_stats(void) l1_imem_accesses += l1_icaches[i]->accesses; l1_dmem_accesses += l1_dcaches[i]->accesses; - l2_misses += l2_ucaches[i]->misses; - l2_mem_accesses += l2_ucaches[i]->accesses; + if (use_l2) { + l2_misses += l2_ucaches[i]->misses; + l2_mem_accesses += l2_ucaches[i]->accesses; + } } } @@ -604,25 +614,31 @@ static void log_stats(void) g_autoptr(GString) rep = g_string_new("core #, data accesses, data misses," " dmiss rate, insn accesses," - " insn misses, imiss rate," - " l2 accesses, l2 misses," - " l2 miss rate\n"); + " insn misses, imiss rate"); + + if (use_l2) { + g_string_append(rep, ", l2 accesses, l2 misses, l2 miss rate"); + } + + g_string_append(rep, "\n"); for (i = 0; i < cores; i++) { g_string_append_printf(rep, "%-8d", i); dcache = l1_dcaches[i]; icache = l1_icaches[i]; - l2_cache = l2_ucaches[i]; + l2_cache = use_l2 ? l2_ucaches[i] : NULL; append_stats_line(rep, dcache->accesses, dcache->misses, - icache->accesses, icache->misses, l2_cache->accesses, - l2_cache->misses); + icache->accesses, icache->misses, + l2_cache ? l2_cache->accesses : 0, + l2_cache ? l2_cache->misses : 0); } if (cores > 1) { sum_stats(); g_string_append_printf(rep, "%-8s", "sum"); append_stats_line(rep, l1_dmem_accesses, l1_dmisses, - l1_imem_accesses, l1_imisses, l2_mem_accesses, l2_misses); + l1_imem_accesses, l1_imisses, + l2_cache ? l2_mem_accesses : 0, l2_cache ? l2_misses : 0); } g_string_append(rep, "\n"); @@ -663,6 +679,10 @@ static void log_top_insns(void) insn->disas_str); } + if (!use_l2) { + goto finish; + } + miss_insns = g_list_sort(miss_insns, l2_cmp); g_string_append_printf(rep, "%s", "\naddress, L2 misses, instruction\n"); @@ -676,6 +696,7 @@ static void log_top_insns(void) insn->disas_str); } +finish: qemu_plugin_outs(rep->str); g_list_free(miss_insns); } @@ -687,11 +708,14 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) caches_free(l1_dcaches); caches_free(l1_icaches); - caches_free(l2_ucaches); g_free(l1_dcache_locks); g_free(l1_icache_locks); - g_free(l2_ucache_locks); + + if (use_l2) { + caches_free(l2_ucaches); + g_free(l2_ucache_locks); + } g_hash_table_destroy(miss_ht); } @@ -767,11 +791,19 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, } else if (g_strcmp0(tokens[0], "cores") == 0) { cores = STRTOLL(tokens[1]); } else if (g_strcmp0(tokens[0], "l2cachesize") == 0) { + use_l2 = true; l2_cachesize = STRTOLL(tokens[1]); } else if (g_strcmp0(tokens[0], "l2blksize") == 0) { + use_l2 = true; l2_blksize = STRTOLL(tokens[1]); } else if (g_strcmp0(tokens[0], "l2assoc") == 0) { + use_l2 = true; l2_assoc = STRTOLL(tokens[1]); + } else if (g_strcmp0(tokens[0], "l2") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &use_l2)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } } else if (g_strcmp0(tokens[0], "evict") == 0) { if (g_strcmp0(tokens[1], "rand") == 0) { policy = RAND; @@ -807,8 +839,8 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, return -1; } - l2_ucaches = caches_init(l2_blksize, l2_assoc, l2_cachesize); - if (!l2_ucaches) { + l2_ucaches = use_l2 ? caches_init(l2_blksize, l2_assoc, l2_cachesize) : NULL; + if (!l2_ucaches && use_l2) { const char *err = cache_config_error(l2_blksize, l2_assoc, l2_cachesize); fprintf(stderr, "L2 cache cannot be constructed from given parameters\n"); fprintf(stderr, "%s\n", err); @@ -817,7 +849,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, l1_dcache_locks = g_new0(GMutex, cores); l1_icache_locks = g_new0(GMutex, cores); - l2_ucache_locks = g_new0(GMutex, cores); + l2_ucache_locks = use_l2 ? g_new0(GMutex, cores) : NULL; qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); From b8312e04c8116b7787c54eb6c3cfee712b74f2f3 Mon Sep 17 00:00:00 2001 From: Mahmoud Mandour Date: Tue, 26 Oct 2021 11:22:23 +0100 Subject: [PATCH 1201/1334] docs/tcg-plugins: add L2 arguments to cache docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cache plugin now allows optional L2 per-core cache emulation that can be configured through plugin arguments, this commit adds this functionality to the docs. While I'm at it, I editted the bullet point for cache plugin to say: contrib/plugins/cache.c instead of contrib/plugins/cache to match other plugins. Signed-off-by: Mahmoud Mandour Signed-off-by: Alex Bennée Message-Id: <20210810134844.166490-6-ma.mandourr@gmail.com> Message-Id: <20211026102234.3961636-18-alex.bennee@linaro.org> --- docs/devel/tcg-plugins.rst | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index 842ae01a4c..59a7d838be 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -361,8 +361,9 @@ which will output an execution trace following this structure:: - contrib/plugins/cache.c -Cache modelling plugin that measures the performance of a given cache -configuration when a given working set is run:: +Cache modelling plugin that measures the performance of a given L1 cache +configuration, and optionally a unified L2 per-core cache when a given working +set is run:: qemu-x86_64 -plugin ./contrib/plugins/libcache.so \ -d plugin -D cache.log ./tests/tcg/x86_64-linux-user/float_convs @@ -420,3 +421,18 @@ The plugin has a number of arguments, all of them are optional: Sets the number of cores for which we maintain separate icache and dcache. (default: for linux-user, N = 1, for full system emulation: N = cores available to guest) + + * l2=on + + Simulates a unified L2 cache (stores blocks for both instructions and data) + using the default L2 configuration (cache size = 2MB, associativity = 16-way, + block size = 64B). + + * l2cachesize=N + * l2blksize=B + * l2assoc=A + + L2 cache configuration arguments. They specify the cache size, block size, and + associativity of the L2 cache, respectively. Setting any of the L2 + configuration arguments implies ``l2=on``. + (default: N = 2097152 (2MB), B = 64, A = 16) From a00e37a4be88a043fea3e8be3ee3a85f6c4939cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 26 Oct 2021 11:22:24 +0100 Subject: [PATCH 1202/1334] chardev: don't exit() straight away on C-a x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While there are a number of uses in the code-base of the exit(0) pattern it gets in the way of clean exit which can do all of it's house-keeping. In particular it was reported that you can crash plugins this way because TCG can still be running on other threads when the atexit callback is called. Use qmp_quit() instead which takes care of some housekeeping before triggering the shutdown. Signed-off-by: Alex Bennée Reported-by: Lukas Jünger Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Acked-by: Paolo Bonzini Message-Id: <20211026102234.3961636-19-alex.bennee@linaro.org> --- chardev/char-mux.c | 3 ++- stubs/meson.build | 1 + stubs/qmp-quit.c | 8 ++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 stubs/qmp-quit.c diff --git a/chardev/char-mux.c b/chardev/char-mux.c index ada0c6866f..ee2d47b20d 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -28,6 +28,7 @@ #include "qemu/option.h" #include "chardev/char.h" #include "sysemu/block-backend.h" +#include "qapi/qapi-commands-control.h" #include "chardev-internal.h" /* MUX driver for serial I/O splitting */ @@ -157,7 +158,7 @@ static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch) { const char *term = "QEMU: Terminated\n\r"; qemu_chr_write_all(chr, (uint8_t *)term, strlen(term)); - exit(0); + qmp_quit(NULL); break; } case 's': diff --git a/stubs/meson.build b/stubs/meson.build index f6aa3aa94f..71469c1d50 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -31,6 +31,7 @@ stub_ss.add(files('pci-bus.c')) stub_ss.add(files('qemu-timer-notify-cb.c')) stub_ss.add(files('qmp_memory_device.c')) stub_ss.add(files('qmp-command-available.c')) +stub_ss.add(files('qmp-quit.c')) stub_ss.add(files('qtest.c')) stub_ss.add(files('ram-block.c')) stub_ss.add(files('ramfb.c')) diff --git a/stubs/qmp-quit.c b/stubs/qmp-quit.c new file mode 100644 index 0000000000..a3ff47f7bd --- /dev/null +++ b/stubs/qmp-quit.c @@ -0,0 +1,8 @@ +#include "qemu/osdep.h" +#include "qapi/qapi-commands-control.h" +#include "qapi/qmp/dispatch.h" + +void qmp_quit(Error **errp) +{ + g_assert_not_reached(); +} From e83f79b3faee1316a243b05bb768840d21cec6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 26 Oct 2021 11:22:25 +0100 Subject: [PATCH 1203/1334] tests/plugins: extend the insn plugin to track opcode sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is mostly a convenience feature for identifying frontends that do multiple repeat loads so I can test changes to the instruction tracking interface. Signed-off-by: Alex Bennée Message-Id: <20211026102234.3961636-20-alex.bennee@linaro.org> --- tests/plugin/insn.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index 0f6a1938c1..d229fdc001 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -18,6 +18,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; static uint64_t insn_count; static bool do_inline; +static bool do_size; +static GArray *sizes; static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) { @@ -49,13 +51,35 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, GUINT_TO_POINTER(vaddr)); } + + if (do_size) { + size_t sz = qemu_plugin_insn_size(insn); + if (sz > sizes->len) { + g_array_set_size(sizes, sz); + } + unsigned long *cnt = &g_array_index(sizes, unsigned long, sz); + (*cnt)++; + } } } static void plugin_exit(qemu_plugin_id_t id, void *p) { - g_autofree gchar *out = g_strdup_printf("insns: %" PRIu64 "\n", insn_count); - qemu_plugin_outs(out); + g_autoptr(GString) out = g_string_new(NULL); + + if (do_size) { + int i; + for (i = 0; i <= sizes->len; i++) { + unsigned long *cnt = &g_array_index(sizes, unsigned long, i); + if (*cnt) { + g_string_append_printf(out, + "len %d bytes: %ld insns\n", i, *cnt); + } + } + } else { + g_string_append_printf(out, "insns: %" PRIu64 "\n", insn_count); + } + qemu_plugin_outs(out->str); } QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, @@ -70,12 +94,21 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, fprintf(stderr, "boolean argument parsing failed: %s\n", opt); return -1; } + } else if (g_strcmp0(tokens[0], "sizes") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_size)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; } } + if (do_size) { + sizes = g_array_new(true, true, sizeof(unsigned long)); + } + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); return 0; From 357af9be5ca47ae8ac2bc439de4bb9a39e186fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 26 Oct 2021 11:22:26 +0100 Subject: [PATCH 1204/1334] plugins: try and make plugin_insn_append more ergonomic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we make the assumption that the guest frontend loads all op code bytes sequentially. This mostly holds up for regular fixed encodings but some architectures like s390x like to re-read the instruction which causes weirdness to occur. Rather than changing the frontends make the plugin API a little more ergonomic and able to handle the re-read case. Stuff will still get strange if we read ahead of the opcode but so far no front ends have done that and this patch asserts the case so we can catch it early if they do. Signed-off-by: Alex Bennée Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Message-Id: <20211026102234.3961636-21-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 3 +-- accel/tcg/translator.c | 2 +- include/exec/plugin-gen.h | 12 ++++++++++-- include/qemu/plugin.h | 7 +++++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 61be64b78c..22d95fe1c3 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -876,9 +876,8 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; struct qemu_plugin_insn *pinsn; - pinsn = qemu_plugin_tb_insn_get(ptb); + pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next); tcg_ctx->plugin_insn = pinsn; - pinsn->vaddr = db->pc_next; plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN); /* diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 390bd9db0a..f06c314266 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -169,7 +169,7 @@ static inline void translator_maybe_page_protect(DisasContextBase *dcbase, if (do_swap) { \ ret = swap_fn(ret); \ } \ - plugin_insn_append(&ret, sizeof(ret)); \ + plugin_insn_append(pc, &ret, sizeof(ret)); \ return ret; \ } diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h index b1b72b5d90..f92f169739 100644 --- a/include/exec/plugin-gen.h +++ b/include/exec/plugin-gen.h @@ -27,13 +27,21 @@ void plugin_gen_insn_end(void); void plugin_gen_disable_mem_helpers(void); void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info); -static inline void plugin_insn_append(const void *from, size_t size) +static inline void plugin_insn_append(abi_ptr pc, const void *from, size_t size) { struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; + abi_ptr off; if (insn == NULL) { return; } + off = pc - insn->vaddr; + if (off < insn->data->len) { + g_byte_array_set_size(insn->data, off); + } else if (off > insn->data->len) { + /* we have an unexpected gap */ + g_assert_not_reached(); + } insn->data = g_byte_array_append(insn->data, from, size); } @@ -62,7 +70,7 @@ static inline void plugin_gen_disable_mem_helpers(void) static inline void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info) { } -static inline void plugin_insn_append(const void *from, size_t size) +static inline void plugin_insn_append(abi_ptr pc, const void *from, size_t size) { } #endif /* CONFIG_PLUGIN */ diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index b3172b147f..145f8a221a 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -163,10 +163,12 @@ struct qemu_plugin_tb { /** * qemu_plugin_tb_insn_get(): get next plugin record for translation. - * + * @tb: the internal tb context + * @pc: address of instruction */ static inline -struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb) +struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb, + uint64_t pc) { struct qemu_plugin_insn *insn; int i, j; @@ -179,6 +181,7 @@ struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb) g_byte_array_set_size(insn->data, 0); insn->calls_helpers = false; insn->mem_helper = false; + insn->vaddr = pc; for (i = 0; i < PLUGIN_N_CB_TYPES; i++) { for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) { From 8ef3fdf952f061df6a9b30008bd8de9dfaa32435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 26 Oct 2021 11:22:33 +0100 Subject: [PATCH 1205/1334] tests/tcg: remove duplicate EXTRA_RUNS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We set it bellow outside the #if leg. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211026102234.3961636-28-alex.bennee@linaro.org> --- tests/tcg/multiarch/Makefile.target | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 6ccb592aac..c0d9e638e9 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -61,8 +61,6 @@ run-gdbstub-sha1: sha1 --bin $< --test $(MULTIARCH_SRC)/gdbstub/sha1.py, \ "basic gdbstub support") -EXTRA_RUNS += run-gdbstub-sha1 - run-gdbstub-qxfer-auxv-read: sha1 $(call run-test, $@, $(GDB_SCRIPT) \ --gdb $(HAVE_GDB_BIN) \ From 4a82be77de9dcfe37c3a8257953737f2b3c9b9d7 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Tue, 26 Oct 2021 11:22:34 +0100 Subject: [PATCH 1206/1334] gdbstub: Switch to the thread receiving a signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Respond with Txxthread:yyyy; instead of a plain Sxx to indicate which thread received the signal. Otherwise, the debugger will associate it with the main one. Also automatically select this thread, as that is what gdb expects. Signed-off-by: Pavel Labath Message-Id: <20211019174953.36560-1-pavel@labath.sk> Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20211026102234.3961636-29-alex.bennee@linaro.org> --- gdbstub.c | 8 ++- tests/tcg/multiarch/Makefile.target | 10 +++- .../gdbstub/test-thread-breakpoint.py | 60 +++++++++++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py diff --git a/gdbstub.c b/gdbstub.c index 36b85aa50e..23baaef40e 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -3138,8 +3138,12 @@ gdb_handlesig(CPUState *cpu, int sig) tb_flush(cpu); if (sig != 0) { - snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig)); - put_packet(buf); + gdb_set_stop_cpu(cpu); + g_string_printf(gdbserver_state.str_buf, + "T%02xthread:", target_signal_to_gdb(sig)); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); + put_strbuf(); } /* put_packet() might have detected that the peer terminated the connection. */ diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index c0d9e638e9..b962ed8236 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -68,11 +68,19 @@ run-gdbstub-qxfer-auxv-read: sha1 --bin $< --test $(MULTIARCH_SRC)/gdbstub/test-qxfer-auxv-read.py, \ "basic gdbstub qXfer:auxv:read support") +run-gdbstub-thread-breakpoint: testthread + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/test-thread-breakpoint.py, \ + "hitting a breakpoint on non-main thread") + else run-gdbstub-%: $(call skip-test, "gdbstub test $*", "need working gdb") endif -EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read +EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \ + run-gdbstub-thread-breakpoint # ARM Compatible Semi Hosting Tests # diff --git a/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py new file mode 100644 index 0000000000..798d508bc7 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py @@ -0,0 +1,60 @@ +from __future__ import print_function +# +# Test auxiliary vector is loaded via gdbstub +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +failcount = 0 + +def report(cond, msg): + "Report success/fail of test" + if cond: + print ("PASS: %s" % (msg)) + else: + print ("FAIL: %s" % (msg)) + global failcount + failcount += 1 + +def run_test(): + "Run through the tests one by one" + + sym, ok = gdb.lookup_symbol("thread1_func") + gdb.execute("b thread1_func") + gdb.execute("c") + + frame = gdb.selected_frame() + report(str(frame.function()) == "thread1_func", "break @ %s"%frame) + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + arch = inferior.architecture() + print("ATTACHED: %s" % arch.name()) +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +if gdb.parse_and_eval('$pc') == 0: + print("SKIP: PC not set") + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except (gdb.error): + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + pass + +print("All tests complete: %d failures" % failcount) +exit(failcount) From 41bf7395925a5b4bf3d8bc16926d1d38b26f2d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 26 Oct 2021 18:39:14 +0100 Subject: [PATCH 1207/1334] tests/tcg: remove debug polluting make output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 5343a837cd ("tests/tcg: move some multiarch files and make conditional") Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211026173914.79377-1-alex.bennee@linaro.org> --- tests/tcg/multiarch/Makefile.target | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index b962ed8236..a83efb4a9d 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -16,7 +16,6 @@ MULTIARCH_SRCS += $(notdir $(wildcard $(MULTIARCH_SRC)/linux/*.c)) endif MULTIARCH_TESTS = $(MULTIARCH_SRCS:.c=) -$(info SRCS=${MULTIARCH_SRCS} and ${MULTIARCH_TESTS}) # # The following are any additional rules needed to build things # From b31b3fd0c0e17b95b9b0e05e0d67d0cd3ca081da Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 18 Oct 2021 13:53:13 -0700 Subject: [PATCH 1208/1334] tests/vm/openbsd: Update to release 7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two minor changes required in the script for the network configuration of the newer release. Signed-off-by: Richard Henderson Tested-by: Thomas Huth Message-Id: <20211018205313.3526915-1-richard.henderson@linaro.org> Signed-off-by: Alex Bennée --- tests/vm/openbsd | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/vm/openbsd b/tests/vm/openbsd index c4c78a80f1..337fe7c303 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -22,8 +22,8 @@ class OpenBSDVM(basevm.BaseVM): name = "openbsd" arch = "x86_64" - link = "https://cdn.openbsd.org/pub/OpenBSD/6.9/amd64/install69.iso" - csum = "140d26548aec680e34bb5f82295414228e7f61e4f5e7951af066014fda2d6e43" + link = "https://cdn.openbsd.org/pub/OpenBSD/7.0/amd64/install70.iso" + csum = "1882f9a23c9800e5dba3dbd2cf0126f552605c915433ef4c5bb672610a4ca3a4" size = "20G" pkgs = [ # tools @@ -95,10 +95,9 @@ class OpenBSDVM(basevm.BaseVM): self.console_wait_send("Terminal type", "xterm\n") self.console_wait_send("System hostname", "openbsd\n") self.console_wait_send("Which network interface", "vio0\n") - self.console_wait_send("IPv4 address", "dhcp\n") + self.console_wait_send("IPv4 address", "autoconf\n") self.console_wait_send("IPv6 address", "none\n") self.console_wait_send("Which network interface", "done\n") - self.console_wait_send("DNS domain name", "localnet\n") self.console_wait("Password for root account") self.console_send("%s\n" % self._config["root_pass"]) self.console_wait("Password for root account") From 25ddb946e6301f42cff3094ea1c25fb78813e7e9 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Thu, 21 Oct 2021 12:10:47 -0400 Subject: [PATCH 1209/1334] e1000: fix tx re-entrancy problem The fact that the MMIO handler is not re-entrant causes an infinite loop under certain conditions: Guest write to TDT -> Loopback -> RX (DMA to TDT) -> TX We now eliminate the effect of this problem locally in e1000, by adding a boolean in struct E1000State indicating when the TX side is busy. This will cause any entering new call to return early instead of interfering with the ongoing work, and eliminates any risk of looping. This is intended to address CVE-2021-20257. Signed-off-by: Jon Maloy Signed-off-by: Jason Wang --- hw/net/e1000.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/net/e1000.c b/hw/net/e1000.c index a30546c5d5..f5bc81296d 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -107,6 +107,7 @@ struct E1000State_st { e1000x_txd_props props; e1000x_txd_props tso_props; uint16_t tso_frames; + bool busy; } tx; struct { @@ -763,6 +764,11 @@ start_xmit(E1000State *s) return; } + if (s->tx.busy) { + return; + } + s->tx.busy = true; + while (s->mac_reg[TDH] != s->mac_reg[TDT]) { base = tx_desc_base(s) + sizeof(struct e1000_tx_desc) * s->mac_reg[TDH]; @@ -789,6 +795,7 @@ start_xmit(E1000State *s) break; } } + s->tx.busy = false; set_ics(s, 0, cause); } From 3fd641ac5ec713e67129c1a57e8b6281182bd843 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Wed, 3 Nov 2021 12:38:31 +0100 Subject: [PATCH 1210/1334] Fix virtio-net-pci* "vectors" compat hw_compat_5_2 has an issue: it affects only "virtio-net-pci" but not "virtio-net-pci-transitional" and "virtio-net-pci-non-transitional". The solution is to use the "virtio-net-pci-base" type in compat_props. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1999141 Signed-off-by: Eduardo Habkost Signed-off-by: Jean-Louis Dupond Acked-by: Jason Wang Acked-by: Jean-Louis Dupond Reviewed-by: Cornelia Huck Signed-off-by: Jason Wang --- hw/core/machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 948b3d9524..26ec54e726 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -56,7 +56,7 @@ GlobalProperty hw_compat_5_2[] = { { "ICH9-LPC", "smm-compat", "on"}, { "PIIX4_PM", "smm-compat", "on"}, { "virtio-blk-device", "report-discard-granularity", "off" }, - { "virtio-net-pci", "vectors", "3"}, + { "virtio-net-pci-base", "vectors", "3"}, }; const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2); From e86a93f55463c088aa0b5260e915ffbf9f86c62b Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 3 Nov 2021 23:51:52 -0700 Subject: [PATCH 1211/1334] virtio-gpu: splitting one extended mode guest fb into n-scanouts When guest is running Linux/X11 with extended multiple displays mode enabled, the guest shares one scanout resource each time containing whole surface rather than sharing individual display output separately. This extended frame is properly splited and rendered on the corresponding scanout surfaces but not in case of blob-resource (zero copy). This code change lets the qemu split this one large surface data into multiple in case of blob-resource as well so that each sub frame then can be blitted properly to each scanout. v2: resizing qemu console in virtio_gpu_update_dmabuf to scanout's width and height v3: updating stub function of virtio_gpu_update_dmabuf to match the type Cc: Gerd Hoffmann Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20211104065153.28897-5-dongwon.kim@intel.com> Signed-off-by: Gerd Hoffmann --- hw/display/virtio-gpu-udmabuf-stubs.c | 3 ++- hw/display/virtio-gpu-udmabuf.c | 22 ++++++++++++++-------- hw/display/virtio-gpu.c | 4 ++-- include/hw/virtio/virtio-gpu.h | 5 +++-- include/ui/console.h | 4 ++++ 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/hw/display/virtio-gpu-udmabuf-stubs.c b/hw/display/virtio-gpu-udmabuf-stubs.c index 81f661441a..f692e13510 100644 --- a/hw/display/virtio-gpu-udmabuf-stubs.c +++ b/hw/display/virtio-gpu-udmabuf-stubs.c @@ -20,7 +20,8 @@ void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res) int virtio_gpu_update_dmabuf(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_simple_resource *res, - struct virtio_gpu_framebuffer *fb) + struct virtio_gpu_framebuffer *fb, + struct virtio_gpu_rect *r) { /* nothing (stub) */ return 0; diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index 60ea7f8f49..1597921c51 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -171,7 +171,8 @@ static VGPUDMABuf *virtio_gpu_create_dmabuf(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_simple_resource *res, - struct virtio_gpu_framebuffer *fb) + struct virtio_gpu_framebuffer *fb, + struct virtio_gpu_rect *r) { VGPUDMABuf *dmabuf; @@ -183,6 +184,10 @@ static VGPUDMABuf dmabuf->buf.width = fb->width; dmabuf->buf.height = fb->height; dmabuf->buf.stride = fb->stride; + dmabuf->buf.x = r->x; + dmabuf->buf.y = r->y; + dmabuf->buf.scanout_width = r->width; + dmabuf->buf.scanout_height = r->height; dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format); dmabuf->buf.fd = res->dmabuf_fd; dmabuf->buf.allow_fences = true; @@ -196,24 +201,25 @@ static VGPUDMABuf int virtio_gpu_update_dmabuf(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_simple_resource *res, - struct virtio_gpu_framebuffer *fb) + struct virtio_gpu_framebuffer *fb, + struct virtio_gpu_rect *r) { struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id]; VGPUDMABuf *new_primary, *old_primary = NULL; - new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb); + new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb, r); if (!new_primary) { return -EINVAL; } - if (g->dmabuf.primary) { - old_primary = g->dmabuf.primary; + if (g->dmabuf.primary[scanout_id]) { + old_primary = g->dmabuf.primary[scanout_id]; } - g->dmabuf.primary = new_primary; + g->dmabuf.primary[scanout_id] = new_primary; qemu_console_resize(scanout->con, - new_primary->buf.width, - new_primary->buf.height); + new_primary->buf.scanout_width, + new_primary->buf.scanout_height); dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf); if (old_primary) { diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 182e0868b0..d78b9700c7 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -517,9 +517,9 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, console_has_gl(scanout->con)) { dpy_gl_update(scanout->con, 0, 0, scanout->width, scanout->height); - return; } } + return; } if (!res->blob && @@ -627,7 +627,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, if (res->blob) { if (console_has_gl(scanout->con)) { - if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb)) { + if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) { virtio_gpu_update_scanout(g, scanout_id, res, r); return; } diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 24c6628944..acfba7c76c 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -187,7 +187,7 @@ struct VirtIOGPU { struct { QTAILQ_HEAD(, VGPUDMABuf) bufs; - VGPUDMABuf *primary; + VGPUDMABuf *primary[VIRTIO_GPU_MAX_SCANOUTS]; } dmabuf; }; @@ -273,7 +273,8 @@ void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res); int virtio_gpu_update_dmabuf(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_simple_resource *res, - struct virtio_gpu_framebuffer *fb); + struct virtio_gpu_framebuffer *fb, + struct virtio_gpu_rect *r); /* virtio-gpu-3d.c */ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, diff --git a/include/ui/console.h b/include/ui/console.h index b6bedc5f41..6d678924f6 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -167,6 +167,10 @@ typedef struct QemuDmaBuf { uint32_t fourcc; uint64_t modifier; uint32_t texture; + uint32_t x; + uint32_t y; + uint32_t scanout_width; + uint32_t scanout_height; bool y0_top; void *sync; int fence_fd; From 1ab2628fc6d7f3343df7007baa57caa26dc83b6b Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 3 Nov 2021 23:51:48 -0700 Subject: [PATCH 1212/1334] ui/gtk-egl: un-tab and re-tab should destroy egl surface and context An old esurface should be destroyed and set to be NULL when doing un-tab and re-tab so that a new esurface an context can be created for the window widget that those will be bound to. v2: enabling opengl specific routines only when CONFIG_OPENGL is set Cc: Gerd Hoffmann Signed-off-by: Dongwon Kim Signed-off-by: Khairul Anuar Romli Message-Id: <20211104065153.28897-1-dongwon.kim@intel.com> Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index b0564d80c1..8da673c18c 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1242,6 +1242,16 @@ static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, vc->tab_item, vc->label); gtk_widget_destroy(vc->window); vc->window = NULL; +#if defined(CONFIG_OPENGL) + if (vc->gfx.esurface) { + eglDestroySurface(qemu_egl_display, vc->gfx.esurface); + vc->gfx.esurface = NULL; + } + if (vc->gfx.ectx) { + eglDestroyContext(qemu_egl_display, vc->gfx.ectx); + vc->gfx.ectx = NULL; + } +#endif return TRUE; } @@ -1271,6 +1281,16 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) if (!vc->window) { gtk_widget_set_sensitive(vc->menu_item, false); vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); +#if defined(CONFIG_OPENGL) + if (vc->gfx.esurface) { + eglDestroySurface(qemu_egl_display, vc->gfx.esurface); + vc->gfx.esurface = NULL; + } + if (vc->gfx.esurface) { + eglDestroyContext(qemu_egl_display, vc->gfx.ectx); + vc->gfx.ectx = NULL; + } +#endif gd_widget_reparent(s->notebook, vc->window, vc->tab_item); g_signal_connect(vc->window, "delete-event", From 01eb4749f03fe6881388287ede65f6662de11d0a Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 3 Nov 2021 23:51:49 -0700 Subject: [PATCH 1213/1334] ui/gtk-egl: make sure the right context is set as the current Making the vc->gfx.ectx current before handling texture associated with it Cc: Gerd Hoffmann Signed-off-by: Dongwon Kim Message-Id: <20211104065153.28897-2-dongwon.kim@intel.com> Signed-off-by: Gerd Hoffmann --- ui/gtk-egl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index e912b20075..2164995098 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -152,6 +152,7 @@ void gd_egl_refresh(DisplayChangeListener *dcl) } vc->gfx.gls = qemu_gl_init_shader(); if (vc->gfx.ds) { + surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); } } @@ -178,6 +179,8 @@ void gd_egl_switch(DisplayChangeListener *dcl, surface_height(vc->gfx.ds) == surface_height(surface)) { resized = false; } + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); vc->gfx.ds = surface; @@ -237,6 +240,9 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, #ifdef CONFIG_GBM VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); + egl_dmabuf_import_texture(dmabuf); if (!dmabuf->texture) { return; From 4872a023a593e6519b272a57fea03abe13a7bb00 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 3 Nov 2021 23:51:50 -0700 Subject: [PATCH 1214/1334] ui/gtk-egl: guest fb texture needs to be regenerated when reinitializing egl If guest fb is backed by dmabuf (blob-resource), the texture bound to the old context needs to be recreated in case the egl is re-initialized (e.g. new window for vc is created in case of detaching/reattaching of the tab) v2: call egl_dmabuf_release_texutre instead of putting 0 to dmabuf->texture (Vivek Kasireddy) Cc: Gerd Hoffmann Signed-off-by: Dongwon Kim Message-Id: <20211104065153.28897-3-dongwon.kim@intel.com> Signed-off-by: Gerd Hoffmann --- ui/gtk-egl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 2164995098..f2026e4b5c 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -155,6 +155,10 @@ void gd_egl_refresh(DisplayChangeListener *dcl) surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); } + if (vc->gfx.guest_fb.dmabuf) { + egl_dmabuf_release_texture(vc->gfx.guest_fb.dmabuf); + gd_egl_scanout_dmabuf(dcl, vc->gfx.guest_fb.dmabuf); + } } graphic_hw_update(dcl->con); From 7cf87257f751b5312ff5f151992016a722e273d8 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 3 Nov 2021 23:51:51 -0700 Subject: [PATCH 1215/1334] ui/gtk: gd_draw_event returns FALSE when no cairo surface is bound gd_draw_event shouldn't try to repaint if surface does not exist for the VC. Cc: Gerd Hoffmann Signed-off-by: Dongwon Kim Message-Id: <20211104065153.28897-4-dongwon.kim@intel.com> Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index 8da673c18c..d2892ea6b4 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -778,6 +778,9 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) if (!vc->gfx.ds) { return FALSE; } + if (!vc->gfx.surface) { + return FALSE; + } vc->gfx.dcl.update_interval = gd_monitor_update_interval(vc->window ? vc->window : s->window); From 1350ff156b25be65c599ecca9957ce6726c6d383 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 3 Nov 2021 23:51:53 -0700 Subject: [PATCH 1216/1334] ui/gtk-egl: blitting partial guest fb to the proper scanout surface eb_fb_blit should be able to blit partial image of guest display (blob res) in case multiple displays are configured for the guest and they are set as extended- desktop mode. v2: egl_fb includes dmabuf info then make egl_fb_blit position and size parameters programmed in dmabuf structure (previously position/size parameters were given to egl_fb_blit separately) (Vivek Kasireddy) changed the commit message as there is no interface change to egl_fb_blit Cc: Gerd Hoffmann Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20211104065153.28897-6-dongwon.kim@intel.com> Signed-off-by: Gerd Hoffmann --- ui/egl-helpers.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 385a3fa752..3a88245b67 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -90,14 +90,31 @@ void egl_fb_setup_new_tex(egl_fb *fb, int width, int height) void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) { - GLuint y1, y2; + GLuint x1 = 0; + GLuint y1 = 0; + GLuint x2, y2; + GLuint w = src->width; + GLuint h = src->height; glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer); glViewport(0, 0, dst->width, dst->height); - y1 = flip ? src->height : 0; - y2 = flip ? 0 : src->height; - glBlitFramebuffer(0, y1, src->width, y2, + + if (src->dmabuf) { + x1 = src->dmabuf->x; + y1 = src->dmabuf->y; + w = src->dmabuf->scanout_width; + h = src->dmabuf->scanout_height; + } + + w = (x1 + w) > src->width ? src->width - x1 : w; + h = (y1 + h) > src->height ? src->height - y1 : h; + + y2 = flip ? y1 : h + y1; + y1 = flip ? h + y1 : y1; + x2 = x1 + w; + + glBlitFramebuffer(x1, y1, x2, y2, 0, 0, dst->width, dst->height, GL_COLOR_BUFFER_BIT, GL_LINEAR); } From 565599807fee415a2e071d2c4f532f4e3bb18dcb Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 3 Nov 2021 20:24:27 +0100 Subject: [PATCH 1217/1334] migration: Check that postcopy fd's are not NULL If postcopy has finished, it frees the array. But vhost-user unregister it at cleanup time. fixes: c4f7538 Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/postcopy-ram.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index e721f69d0f..d18b5d05b2 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -1457,6 +1457,10 @@ void postcopy_unregister_shared_ufd(struct PostCopyFD *pcfd) MigrationIncomingState *mis = migration_incoming_get_current(); GArray *pcrfds = mis->postcopy_remote_fds; + if (!pcrfds) { + /* migration has already finished and freed the array */ + return; + } for (i = 0; i < pcrfds->len; i++) { struct PostCopyFD *cur = &g_array_index(pcrfds, struct PostCopyFD, i); if (cur->fd == pcfd->fd) { From f78d4ed701454f10079461b981ba2a61a95762ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=E9=BB=84=E5=8B=87=29?= Date: Fri, 5 Nov 2021 21:01:16 +0800 Subject: [PATCH 1218/1334] docs: fix qemu incorrect tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The patchset merged in 71864eadd9a ("migration/dirtyrate: introduce struct and adjust DirtyRateStat") was targeting QEMU 6.1 but got merged later, so correct the tag for 6.2. Signed-off-by: Hyman Huang(黄勇) Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- qapi/migration.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qapi/migration.json b/qapi/migration.json index 87146ceea2..f0aefdab64 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1740,7 +1740,7 @@ # # @dirty-rate: dirty rate. # -# Since: 6.1 +# Since: 6.2 # ## { 'struct': 'DirtyRateVcpu', @@ -1774,7 +1774,7 @@ # # @dirty-bitmap: calculate dirtyrate by dirty bitmap. # -# Since: 6.1 +# Since: 6.2 # ## { 'enum': 'DirtyRateMeasureMode', @@ -1796,13 +1796,13 @@ # @calc-time: time in units of second for sample dirty pages # # @sample-pages: page count per GB for sample dirty pages -# the default value is 512 (since 6.1) +# the default value is 512 (since 6.2) # # @mode: mode containing method of calculate dirtyrate includes -# 'page-sampling' and 'dirty-ring' (Since 6.1) +# 'page-sampling' and 'dirty-ring' (Since 6.2) # # @vcpu-dirty-rate: dirtyrate for each vcpu if dirty-ring -# mode specified (Since 6.1) +# mode specified (Since 6.2) # # Since: 5.2 # From 93eae3583256896dd91a4c2ca38dcbb8d4051cff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 4 Nov 2021 14:45:20 +0100 Subject: [PATCH 1219/1334] target-i386: mmu: use pg_mode instead of HF_LMA_MASK Correctly look up the paging mode of the hypervisor when it is using 64-bit mode but the guest is not. Fixes: 68746930ae ("target/i386: use mmu_translate for NPT walk", 2021-05-11) Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/excp_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c index 7af887be4d..492b777de9 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/sysemu/excp_helper.c @@ -90,7 +90,7 @@ static int mmu_translate(CPUState *cs, hwaddr addr, MMUTranslateFunc get_hphys_f target_ulong pdpe_addr; #ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { + if (pg_mode & PG_MODE_LMA) { bool la57 = pg_mode & PG_MODE_LA57; uint64_t pml5e_addr, pml5e; uint64_t pml4e_addr, pml4e; @@ -287,7 +287,7 @@ do_check_protect_pse36: *prot |= PAGE_EXEC; } - if (!(env->hflags & HF_LMA_MASK)) { + if (!(pg_mode & PG_MODE_LMA)) { pkr = 0; } else if (ptep & PG_USER_MASK) { pkr = pg_mode & PG_MODE_PKE ? env->pkru : 0; From b04dc92e013d55c9ac8082caefff45dcfb1310e7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 4 Nov 2021 14:47:46 +0100 Subject: [PATCH 1220/1334] target-i386: mmu: fix handling of noncanonical virtual addresses mmu_translate is supposed to return an error code for page faults; it is not able to handle other exceptions. The #GP case for noncanonical virtual addresses is not handled correctly, and incorrectly raised as a page fault with error code 1. Since it cannot happen for nested page tables, move it directly to handle_mmu_fault, even before the invocation of mmu_translate. Fixes: #676 Fixes: 661ff4879e ("target/i386: extract mmu_translate", 2021-05-11) Cc: qemu-stable@nongnu.org Tested-by: Mark Cave-Ayland Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/excp_helper.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c index 492b777de9..5ba739fbed 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/sysemu/excp_helper.c @@ -94,15 +94,6 @@ static int mmu_translate(CPUState *cs, hwaddr addr, MMUTranslateFunc get_hphys_f bool la57 = pg_mode & PG_MODE_LA57; uint64_t pml5e_addr, pml5e; uint64_t pml4e_addr, pml4e; - int32_t sext; - - /* test virtual address sign extension */ - sext = la57 ? (int64_t)addr >> 56 : (int64_t)addr >> 47; - if (get_hphys_func && sext != 0 && sext != -1) { - env->error_code = 0; - cs->exception_index = EXCP0D_GPF; - return 1; - } if (la57) { pml5e_addr = ((cr3 & ~0xfff) + @@ -423,6 +414,18 @@ static int handle_mmu_fault(CPUState *cs, vaddr addr, int size, page_size = 4096; } else { pg_mode = get_pg_mode(env); + if (pg_mode & PG_MODE_LMA) { + int32_t sext; + + /* test virtual address sign extension */ + sext = (int64_t)addr >> (pg_mode & PG_MODE_LA57 ? 56 : 47); + if (sext != 0 && sext != -1) { + env->error_code = 0; + cs->exception_index = EXCP0D_GPF; + return 1; + } + } + error_code = mmu_translate(cs, addr, get_hphys, env->cr[3], is_write1, mmu_idx, pg_mode, &paddr, &page_size, &prot); From b3af7fdf9cc537f8f0dd3e2423d83f5c99a457e8 Mon Sep 17 00:00:00 2001 From: Mauro Matteo Cascella Date: Thu, 4 Nov 2021 17:31:38 +0100 Subject: [PATCH 1221/1334] hw/scsi/scsi-disk: MODE_PAGE_ALLS not allowed in MODE SELECT commands This avoids an off-by-one read of 'mode_sense_valid' buffer in hw/scsi/scsi-disk.c:mode_sense_page(). Fixes: CVE-2021-3930 Cc: qemu-stable@nongnu.org Reported-by: Alexander Bulekov Fixes: a8f4bbe2900 ("scsi-disk: store valid mode pages in a table") Fixes: #546 Reported-by: Qiuhao Li Signed-off-by: Mauro Matteo Cascella Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e8a547dbb7..d4914178ea 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1087,6 +1087,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, uint8_t *p = *p_outbuf + 2; int length; + assert(page < ARRAY_SIZE(mode_sense_valid)); if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) { return -1; } @@ -1428,6 +1429,11 @@ static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, return -1; } + /* MODE_PAGE_ALLS is only valid for MODE SENSE commands */ + if (page == MODE_PAGE_ALLS) { + return -1; + } + p = mode_current; memset(mode_current, 0, inlen + 2); len = mode_sense_page(s, page, &p, 0); From cd0a9e983c984e3a6a08397dc2ca4500886ec48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 8 Oct 2021 22:31:06 +0400 Subject: [PATCH 1222/1334] docs/sphinx: add loaded modules to generated depfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: John Snow Reviewed-by: Paolo Bonzini --- docs/sphinx/depfile.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/depfile.py b/docs/sphinx/depfile.py index 277fdf0f56..b6fb926df1 100644 --- a/docs/sphinx/depfile.py +++ b/docs/sphinx/depfile.py @@ -12,6 +12,7 @@ import os import sphinx +import sys __version__ = '1.0' @@ -20,8 +21,17 @@ def get_infiles(env): yield env.doc2path(x) yield from ((os.path.join(env.srcdir, dep) for dep in env.dependencies[x])) + for mod in sys.modules.values(): + if hasattr(mod, '__file__'): + if mod.__file__: + yield mod.__file__ -def write_depfile(app, env): + +def write_depfile(app, exception): + if exception: + return + + env = app.env if not env.config.depfile: return @@ -42,7 +52,7 @@ def write_depfile(app, env): def setup(app): app.add_config_value('depfile', None, 'env') app.add_config_value('depfile_stamp', None, 'env') - app.connect('env-updated', write_depfile) + app.connect('build-finished', write_depfile) return dict( version = __version__, From 905655ea6ab6f25371415f2483e02ab34e3bb98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 9 Oct 2021 01:46:10 +0400 Subject: [PATCH 1223/1334] docs/sphinx: add static files to generated depfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: John Snow Reviewed-by: Paolo Bonzini --- docs/sphinx/depfile.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/sphinx/depfile.py b/docs/sphinx/depfile.py index b6fb926df1..99539adb48 100644 --- a/docs/sphinx/depfile.py +++ b/docs/sphinx/depfile.py @@ -13,6 +13,7 @@ import os import sphinx import sys +from pathlib import Path __version__ = '1.0' @@ -25,6 +26,10 @@ def get_infiles(env): if hasattr(mod, '__file__'): if mod.__file__: yield mod.__file__ + # this is perhaps going to include unused files: + for static_path in env.config.html_static_path: + for path in Path(static_path).rglob('*'): + yield str(path) def write_depfile(app, exception): From 0dd35c16297a365bc99115284ab3e77da9986368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 9 Oct 2021 01:57:51 +0400 Subject: [PATCH 1224/1334] docs/sphinx: add templates files to generated depfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: John Snow Reviewed-by: Paolo Bonzini --- docs/conf.py | 2 +- docs/sphinx/depfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index ff6e92c6e2..edc2bf8fcb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -74,7 +74,7 @@ needs_sphinx = '1.6' extensions = ['kerneldoc', 'qmp_lexer', 'hxtool', 'depfile', 'qapidoc'] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = [os.path.join(qemu_docdir, '_templates')] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: diff --git a/docs/sphinx/depfile.py b/docs/sphinx/depfile.py index 99539adb48..afdcbcec6e 100644 --- a/docs/sphinx/depfile.py +++ b/docs/sphinx/depfile.py @@ -27,7 +27,7 @@ def get_infiles(env): if mod.__file__: yield mod.__file__ # this is perhaps going to include unused files: - for static_path in env.config.html_static_path: + for static_path in env.config.html_static_path + env.config.templates_path: for path in Path(static_path).rglob('*'): yield str(path) From 706bbad2bacf21bed3e61d99203afe85b73f97f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 15 Oct 2021 14:35:10 +0400 Subject: [PATCH 1225/1334] tests/qapi-schema/meson: add depfile to sphinx doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: John Snow Reviewed-by: Paolo Bonzini --- tests/qapi-schema/meson.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index df5acfd08b..d91d972af2 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -242,6 +242,7 @@ if build_docs input: files('doc-good.json', 'doc-good.rst'), build_by_default: true, depend_files: sphinx_extn_depends, + depfile: 'docs.d', # We use -E to suppress Sphinx's caching, because # we want it to always really run the QAPI doc # generation code. It also means we don't @@ -250,6 +251,8 @@ if build_docs '-b', 'text', '-E', '-c', meson.project_source_root() / 'docs', '-D', 'master_doc=doc-good', + '-Ddepfile=@DEPFILE@', + '-Ddepfile_stamp=doc-good.stamp', meson.current_source_dir(), meson.current_build_dir()]) From 89bcfe780a8e1a900d94014dfdef756cc3eb8221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 15 Oct 2021 14:36:16 +0400 Subject: [PATCH 1226/1334] meson: drop sphinx_extn_depends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Module dependencies is now handled by depfile.py. Signed-off-by: Marc-André Lureau Reviewed-by: John Snow Reviewed-by: Paolo Bonzini --- docs/meson.build | 9 +-------- tests/qapi-schema/meson.build | 1 - 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/meson.build b/docs/meson.build index 19cce670a2..34fda6853d 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -37,13 +37,6 @@ endif if build_docs SPHINX_ARGS += ['-Dversion=' + meson.project_version(), '-Drelease=' + config_host['PKGVERSION']] - sphinx_extn_depends = [ meson.current_source_dir() / 'sphinx/depfile.py', - meson.current_source_dir() / 'sphinx/hxtool.py', - meson.current_source_dir() / 'sphinx/kerneldoc.py', - meson.current_source_dir() / 'sphinx/kernellog.py', - meson.current_source_dir() / 'sphinx/qapidoc.py', - meson.current_source_dir() / 'sphinx/qmp_lexer.py', - qapi_gen_depends ] sphinx_template_files = [ meson.project_source_root() / 'docs/_templates/footer.html' ] have_ga = have_tools and config_host.has_key('CONFIG_GUEST_AGENT') @@ -77,7 +70,7 @@ if build_docs output: 'docs.stamp', input: files('conf.py'), depfile: 'docs.d', - depend_files: [ sphinx_extn_depends, sphinx_template_files ], + depend_files: [ sphinx_template_files ], command: [SPHINX_ARGS, '-Ddepfile=@DEPFILE@', '-Ddepfile_stamp=@OUTPUT0@', '-b', 'html', '-d', private_dir, diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index d91d972af2..caf0791ba8 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -241,7 +241,6 @@ if build_docs output: ['doc-good.txt'], input: files('doc-good.json', 'doc-good.rst'), build_by_default: true, - depend_files: sphinx_extn_depends, depfile: 'docs.d', # We use -E to suppress Sphinx's caching, because # we want it to always really run the QAPI doc From ed9e6d65edaeeefd33eaa873ab983d170bd8b3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 15 Oct 2021 14:37:52 +0400 Subject: [PATCH 1227/1334] meson: drop sphinx_template_files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Static files dependencies is now handled by depfile.py. Signed-off-by: Marc-André Lureau Reviewed-by: John Snow Reviewed-by: Paolo Bonzini --- docs/meson.build | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/meson.build b/docs/meson.build index 34fda6853d..27c6e156ff 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -37,8 +37,6 @@ endif if build_docs SPHINX_ARGS += ['-Dversion=' + meson.project_version(), '-Drelease=' + config_host['PKGVERSION']] - sphinx_template_files = [ meson.project_source_root() / 'docs/_templates/footer.html' ] - have_ga = have_tools and config_host.has_key('CONFIG_GUEST_AGENT') man_pages = { @@ -70,7 +68,6 @@ if build_docs output: 'docs.stamp', input: files('conf.py'), depfile: 'docs.d', - depend_files: [ sphinx_template_files ], command: [SPHINX_ARGS, '-Ddepfile=@DEPFILE@', '-Ddepfile_stamp=@OUTPUT0@', '-b', 'html', '-d', private_dir, From 96871b38547211d8d91e7d059a3322e67b53657c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 9 Oct 2021 01:06:38 +0400 Subject: [PATCH 1228/1334] docs/sphinx: set navigation_with_keys=True MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow navigating to the previous/next page using the keyboard's left and right arrows. I wish this would be the default, and that the themes would provide more key navigation, but that doesn't seem on the roadmap. Signed-off-by: Marc-André Lureau Reviewed-by: John Snow Reviewed-by: Paolo Bonzini --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index edc2bf8fcb..f536483bc3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -166,6 +166,7 @@ html_theme = 'sphinx_rtd_theme' if LooseVersion(sphinx_rtd_theme.__version__) >= LooseVersion("0.4.3"): html_theme_options = { "style_nav_header_background": "#802400", + "navigation_with_keys": True, } html_logo = os.path.join(qemu_docdir, "../ui/icons/qemu_128x128.png") From 9423751645d27ae1146ca645431c368f09cb3b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 9 Oct 2021 01:47:56 +0400 Subject: [PATCH 1229/1334] docs/sphinx: add 's' keyboard binding to focus search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is pretty ubiquitous. ('/' is already taken by some browsers for quick search) Signed-off-by: Marc-André Lureau Reviewed-by: John Snow Reviewed-by: Paolo Bonzini --- docs/conf.py | 4 ++++ docs/sphinx-static/custom.js | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 docs/sphinx-static/custom.js diff --git a/docs/conf.py b/docs/conf.py index f536483bc3..3161b8b127 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -182,6 +182,10 @@ html_css_files = [ 'theme_overrides.css', ] +html_js_files = [ + 'custom.js', +] + html_context = { "display_gitlab": True, "gitlab_user": "qemu-project", diff --git a/docs/sphinx-static/custom.js b/docs/sphinx-static/custom.js new file mode 100644 index 0000000000..71a8605305 --- /dev/null +++ b/docs/sphinx-static/custom.js @@ -0,0 +1,9 @@ +document.addEventListener('keydown', (event) => { + // find a better way to look it up? + let search_input = document.getElementsByName('q')[0]; + + if (event.code === 'KeyS' && document.activeElement !== search_input) { + event.preventDefault(); + search_input.focus(); + } +}); From 450e0f28a476342baf1ed14a6c2a230e738f83bf Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 4 Oct 2021 17:52:36 -0400 Subject: [PATCH 1230/1334] docs: remove non-reference uses of single backticks The single backtick markup in ReST is the "default role". Currently, Sphinx's default role is called "content". Sphinx suggests you can use the "Any" role instead to turn any single-backtick enclosed item into a cross-reference. This is useful for things like autodoc for Python docstrings, where it's often nicer to reference other types with `foo` instead of the more laborious :py:meth:`foo`. It's also useful in multi-domain cases to easily reference definitions from other Sphinx domains, such as referencing C code definitions from outside of kerneldoc comments. Before we do that, though, we'll need to turn all existing usages of the "content" role to inline verbatim markup wherever it does not correctly resolve into a cross-refernece by using double backticks instead. Signed-off-by: John Snow Reviewed-by: Eduardo Habkost Reviewed-by: Alexander Bulekov Message-Id: <20211004215238.1523082-2-jsnow@redhat.com> --- docs/devel/fuzzing.rst | 9 +++++---- docs/devel/tcg-plugins.rst | 2 +- docs/interop/live-block-operations.rst | 2 +- docs/system/guest-loader.rst | 2 +- include/qemu/module.h | 6 +++--- qapi/block-core.json | 4 ++-- qemu-options.hx | 4 ++-- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/devel/fuzzing.rst b/docs/devel/fuzzing.rst index 2749bb9bed..784ecb99e6 100644 --- a/docs/devel/fuzzing.rst +++ b/docs/devel/fuzzing.rst @@ -182,10 +182,11 @@ The output should contain a complete list of matched MemoryRegions. OSS-Fuzz -------- -QEMU is continuously fuzzed on `OSS-Fuzz` __(https://github.com/google/oss-fuzz). -By default, the OSS-Fuzz build will try to fuzz every fuzz-target. Since the -generic-fuzz target requires additional information provided in environment -variables, we pre-define some generic-fuzz configs in +QEMU is continuously fuzzed on `OSS-Fuzz +`_. By default, the OSS-Fuzz build +will try to fuzz every fuzz-target. Since the generic-fuzz target +requires additional information provided in environment variables, we +pre-define some generic-fuzz configs in ``tests/qtest/fuzz/generic_fuzz_configs.h``. Each config must specify: - ``.name``: To identify the fuzzer config diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index 59a7d838be..f93ef4fe52 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -211,7 +211,7 @@ The hotpages plugin can be configured using the following arguments: This is an instruction classifier so can be used to count different types of instructions. It has a number of options to refine which get -counted. You can give a value to the `count` argument for a class of +counted. You can give a value to the ``count`` argument for a class of instructions to break it down fully, so for example to see all the system registers accesses:: diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst index 9e3635b233..814c29bbe1 100644 --- a/docs/interop/live-block-operations.rst +++ b/docs/interop/live-block-operations.rst @@ -640,7 +640,7 @@ at this point: (QEMU) block-job-complete device=job0 In either of the above cases, if you once again run the -`query-block-jobs` command, there should not be any active block +``query-block-jobs`` command, there should not be any active block operation. Comparing 'commit' and 'mirror': In both then cases, the overlay images diff --git a/docs/system/guest-loader.rst b/docs/system/guest-loader.rst index 4320d1183f..9ef9776bf0 100644 --- a/docs/system/guest-loader.rst +++ b/docs/system/guest-loader.rst @@ -51,4 +51,4 @@ The full syntax of the guest-loader is:: ``bootargs=`` This is an optional field for kernel blobs which will pass command - like via the `/chosen/module@/bootargs` node. + like via the ``/chosen/module@/bootargs`` node. diff --git a/include/qemu/module.h b/include/qemu/module.h index 3deac0078b..5fcc323b2a 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -77,14 +77,14 @@ void module_allow_arch(const char *arch); /** * DOC: module info annotation macros * - * `scripts/modinfo-collect.py` will collect module info, + * ``scripts/modinfo-collect.py`` will collect module info, * using the preprocessor and -DQEMU_MODINFO. * - * `scripts/modinfo-generate.py` will create a module meta-data database + * ``scripts/modinfo-generate.py`` will create a module meta-data database * from the collected information so qemu knows about module * dependencies and QOM objects implemented by modules. * - * See `*.modinfo` and `modinfo.c` in the build directory to check the + * See ``*.modinfo`` and ``modinfo.c`` in the build directory to check the * script results. */ #ifdef QEMU_MODINFO diff --git a/qapi/block-core.json b/qapi/block-core.json index b290782bf2..33e8507d10 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -491,11 +491,11 @@ # @granularity: granularity of the dirty bitmap in bytes (since 1.4) # # @recording: true if the bitmap is recording new writes from the guest. -# Replaces `active` and `disabled` statuses. (since 4.0) +# Replaces ``active`` and ``disabled`` statuses. (since 4.0) # # @busy: true if the bitmap is in-use by some operation (NBD or jobs) # and cannot be modified via QMP or used by another operation. -# Replaces `locked` and `frozen` statuses. (since 4.0) +# Replaces ``locked`` and ``frozen`` statuses. (since 4.0) # # @persistent: true if the bitmap was stored on disk, is scheduled to be stored # on disk, or both. (since 4.0) diff --git a/qemu-options.hx b/qemu-options.hx index f051536b63..7749f59300 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1895,8 +1895,8 @@ SRST Valid parameters are: ``grab-mod=`` : Used to select the modifier keys for toggling - the mouse grabbing in conjunction with the "g" key. `` can be - either `lshift-lctrl-lalt` or `rctrl`. + the mouse grabbing in conjunction with the "g" key. ```` can be + either ``lshift-lctrl-lalt`` or ``rctrl``. ``alt_grab=on|off`` : Use Control+Alt+Shift-g to toggle mouse grabbing. This parameter is deprecated - use ``grab-mod`` instead. From ca0a0d122c760a0763c68f348560bddfe482813c Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 4 Oct 2021 17:52:37 -0400 Subject: [PATCH 1231/1334] docs: (further) remove non-reference uses of single backticks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The series rotted already. Here's the new changes. Signed-off-by: John Snow Reviewed-by: Damien Hedde [ extra backticks fixes ] Signed-off-by: Marc-André Lureau Message-Id: <20211004215238.1523082-3-jsnow@redhat.com> --- docs/devel/build-system.rst | 16 ++++++++-------- docs/system/i386/sgx.rst | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 7f106d2f1c..ae536ef75d 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -47,14 +47,14 @@ command line options for which a same-named Meson option exists; dashes in the command line are replaced with underscores. Many checks on the compilation environment are still found in configure -rather than `meson.build`, but new checks should be added directly to -`meson.build`. +rather than ``meson.build``, but new checks should be added directly to +``meson.build``. Patches are also welcome to move existing checks from the configure -phase to `meson.build`. When doing so, ensure that `meson.build` does -not use anymore the keys that you have removed from `config-host.mak`. -Typically these will be replaced in `meson.build` by boolean variables, -``get_option('optname')`` invocations, or `dep.found()` expressions. +phase to ``meson.build``. When doing so, ensure that ``meson.build`` does +not use anymore the keys that you have removed from ``config-host.mak``. +Typically these will be replaced in ``meson.build`` by boolean variables, +``get_option('optname')`` invocations, or ``dep.found()`` expressions. In general, the remaining checks have little or no interdependencies, so they can be moved one by one. @@ -298,7 +298,7 @@ comprises the following tasks: - Add code to perform the actual feature check. - - Add code to include the feature status in `config-host.h` + - Add code to include the feature status in ``config-host.h`` - Add code to print out the feature status in the configure summary upon completion. @@ -334,7 +334,7 @@ The other supporting code is generally simple:: For the configure script to parse the new option, the ``scripts/meson-buildoptions.sh`` file must be up-to-date; ``make -update-buildoptions`` (or just `make`) will take care of updating it. +update-buildoptions`` (or just ``make``) will take care of updating it. Support scripts diff --git a/docs/system/i386/sgx.rst b/docs/system/i386/sgx.rst index f103ae2a2f..9aa161af1a 100644 --- a/docs/system/i386/sgx.rst +++ b/docs/system/i386/sgx.rst @@ -77,9 +77,9 @@ CPUID Due to its myriad dependencies, SGX is currently not listed as supported in any of Qemu's built-in CPU configuration. To expose SGX (and SGX Launch -Control) to a guest, you must either use `-cpu host` to pass-through the +Control) to a guest, you must either use ``-cpu host`` to pass-through the host CPU model, or explicitly enable SGX when using a built-in CPU model, -e.g. via `-cpu ,+sgx` or `-cpu ,+sgx,+sgxlc`. +e.g. via ``-cpu ,+sgx`` or ``-cpu ,+sgx,+sgxlc``. All SGX sub-features enumerated through CPUID, e.g. SGX2, MISCSELECT, ATTRIBUTES, etc... can be restricted via CPUID flags. Be aware that enforcing @@ -126,7 +126,7 @@ creating VM with SGX. Feature Control ~~~~~~~~~~~~~~~ -Qemu SGX updates the `etc/msr_feature_control` fw_cfg entry to set the SGX +Qemu SGX updates the ``etc/msr_feature_control`` fw_cfg entry to set the SGX (bit 18) and SGX LC (bit 17) flags based on their respective CPUID support, i.e. existing guest firmware will automatically set SGX and SGX LC accordingly, assuming said firmware supports fw_cfg.msr_feature_control. From c11b3a1dd324d1f7dc8512bb840ffd8226fbd0a7 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 4 Oct 2021 17:52:38 -0400 Subject: [PATCH 1232/1334] docs/sphinx: change default role to "any" This interprets single-backtick syntax in all of our Sphinx docs as a cross-reference to *something*, including Python symbols. From here on out, new uses of `backticks` will cause a build failure if the target cannot be referenced. Signed-off-by: John Snow Reviewed-by: Eduardo Habkost Reviewed-by: Peter Maydell Message-Id: <20211004215238.1523082-4-jsnow@redhat.com> --- docs/conf.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 3161b8b127..763e7d2434 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -85,6 +85,11 @@ source_suffix = '.rst' # The master toctree document. master_doc = 'index' +# Interpret `single-backticks` to be a cross-reference to any kind of +# referenceable object. Unresolvable or ambiguous references will emit a +# warning at build time. +default_role = 'any' + # General information about the project. project = u'QEMU' copyright = u'2021, The QEMU Project Developers' From 565174d08ed34a849f8420f0d9c97d08be3835aa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 4 Nov 2021 14:35:14 +0100 Subject: [PATCH 1233/1334] meson: perform snappy test with the C++ compiler if used Snappy is written in C++ and as such needs to link against libstdc++. When linking statically, this means that the compile test cannot succeed unless performed with a C++ compiler. Do so if link_language is set to C++; if it is C, the test will usually fail and snappy will be disabled. Signed-off-by: Paolo Bonzini --- meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 47df10afc2..6bfed294d0 100644 --- a/meson.build +++ b/meson.build @@ -197,6 +197,10 @@ add_project_arguments('-iquote', '.', link_language = meson.get_external_property('link_language', 'cpp') if link_language == 'cpp' add_languages('cpp', required: true, native: false) + cxx = meson.get_compiler('cpp') + linker = cxx +else + linker = cc endif if host_machine.system() == 'darwin' add_languages('objc', required: false, native: false) @@ -1109,7 +1113,7 @@ if not get_option('snappy').auto() or have_system required: get_option('snappy'), kwargs: static_kwargs) endif -if snappy.found() and not cc.links(''' +if snappy.found() and not linker.links(''' #include int main(void) { snappy_max_compressed_length(4096); return 0; }''', dependencies: snappy) snappy = not_found From 4933436f6a13fb585d8b2e2f625bf52301e98f11 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 5 Nov 2021 13:45:40 +0100 Subject: [PATCH 1234/1334] docs: adjust for demise of scripts/create_config The config-host.h, $TARGET_NAME-config-target.h, $TARGET_NAME-config-devices.h files are now generated by configure_file() rather than scripts/create_config. Adjust he relevant paragraph in docs/devel/build-system.rst, and take the occasion to fix a preexisting confusion of *.h vs *.mak. Reported-by: Markus Armbruster Reviewed-by: Markus Armbruster Signed-off-by: Paolo Bonzini --- docs/devel/build-system.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 7f106d2f1c..3c05032438 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -464,11 +464,10 @@ Built by Meson: scripts/make_device_config.sh program, feeding it the default-configs/$TARGET-NAME file as input. -``config-host.h``, ``$TARGET-NAME/config-target.h``, ``$TARGET-NAME/config-devices.h`` - These files are used by source code to determine what features - are enabled. They are generated from the contents of the corresponding - ``*.h`` files using the scripts/create_config program. This extracts - relevant variables and formats them as C preprocessor macros. +``config-host.h``, ``$TARGET_NAME-config-target.h``, ``$TARGET_NAME-config-devices.h`` + These files are used by source code to determine what features are + enabled. They are generated from the contents of the corresponding + ``*.mak`` files using Meson's ``configure_file()`` function. ``build.ninja`` The build rules. From ac7ebcc589757af7de0a7cba68126a80224fb989 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 5 Nov 2021 10:07:37 +0100 Subject: [PATCH 1235/1334] configure: simplify calls to meson_quote meson_quote assumes a non-empty argument list, and incorrectly returns a one-entry array if passed nothing. Move the check for an empty argument list from the invocations to the function itself. Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/configure b/configure index 33682cb971..369b5455b6 100755 --- a/configure +++ b/configure @@ -3894,6 +3894,7 @@ echo "TOPSRC_DIR=$source_path" >> $config_mak if test "$skip_meson" = no; then cross="config-meson.cross.new" meson_quote() { + test $# = 0 && return echo "'$(echo $* | sed "s/ /','/g")'" } @@ -3908,10 +3909,10 @@ if test "$skip_meson" = no; then test -z "$cxx" && echo "link_language = 'c'" >> $cross echo "[built-in options]" >> $cross - echo "c_args = [${CFLAGS:+$(meson_quote $CFLAGS)}]" >> $cross - echo "cpp_args = [${CXXFLAGS:+$(meson_quote $CXXFLAGS)}]" >> $cross - echo "c_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross - echo "cpp_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross + echo "c_args = [$(meson_quote $CFLAGS)]" >> $cross + echo "cpp_args = [$(meson_quote $CXXFLAGS)]" >> $cross + echo "c_link_args = [$(meson_quote $LDFLAGS)]" >> $cross + echo "cpp_link_args = [$(meson_quote $LDFLAGS)]" >> $cross echo "[binaries]" >> $cross echo "c = [$(meson_quote $cc $CPU_CFLAGS)]" >> $cross test -n "$cxx" && echo "cpp = [$(meson_quote $cxx $CPU_CFLAGS)]" >> $cross From 8009da037418d454d4833e7d3c3367f2f4d7244a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 5 Nov 2021 10:08:43 +0100 Subject: [PATCH 1236/1334] configure: preserve CFLAGS, CXXFLAGS and LDFLAGS in config.status CFLAGS, CXXFLAGS and LDFLAGS influence the tests (for example if they include -L or -I options), so they should be kept from the invocation of configure to the subsequent reinvocations via config.status. Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure b/configure index 369b5455b6..d268f59246 100755 --- a/configure +++ b/configure @@ -4057,9 +4057,12 @@ preserve_env AR preserve_env AS preserve_env CC preserve_env CPP +preserve_env CFLAGS preserve_env CXX +preserve_env CXXFLAGS preserve_env INSTALL preserve_env LD +preserve_env LDFLAGS preserve_env LD_LIBRARY_PATH preserve_env LIBTOOL preserve_env MAKE From a2866660441f114188b7d1025a4a19cbb6188fef Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 5 Nov 2021 10:09:26 +0100 Subject: [PATCH 1237/1334] configure: propagate --extra-cflags and --extra-ldflags to meson compile tests Meson (intentionally) does not add QEMU_CFLAGS to cc.compiles/cc.links tests, as they are supposed to be independent of the specific sets of compilation flags used to build the programs. However, the user can still use CFLAGS or the toolchain file's LANG_args/LANG_link_args option to specify -I or -L options that apply to cc.compiles/cc.links as well. This is also the intended use of configure's --extra-cflags, --extra-cxxflags and --extra-ldflags options. For example, if one has netmap's header in a nonstandard directory, up to commit 837b84b1c078bf3e909 it used to work fine to do: .../configure --enable-netmap \ --extra-cflags=-I/path/to/netmap/sys but since the test was converted to meson, this does not work anymore. Pass these options to meson via the toolchain file instead of via config-host.mak, since both have the same purpose. Reported-by: Owen LaGarde Reported-by: Thomas Huth Fixes: 47b30835e4 ("configure: consistently pass CFLAGS/CXXFLAGS/LDFLAGS to meson", 2020-10-06) Signed-off-by: Paolo Bonzini --- configure | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/configure b/configure index d268f59246..9f1641e79c 100755 --- a/configure +++ b/configure @@ -174,14 +174,14 @@ update_cxxflags() { compile_object() { local_cflags="$1" - do_cc $CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC + do_cc $CFLAGS $EXTRA_CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC } compile_prog() { local_cflags="$1" local_ldflags="$2" - do_cc $CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC \ - $LDFLAGS $CONFIGURE_LDFLAGS $QEMU_LDFLAGS $local_ldflags + do_cc $CFLAGS $EXTRA_CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC \ + $LDFLAGS $EXTRA_LDFLAGS $CONFIGURE_LDFLAGS $QEMU_LDFLAGS $local_ldflags } # symbolically link $1 to $2. Portable version of "ln -sf". @@ -286,6 +286,10 @@ for opt do esac done +EXTRA_CFLAGS="" +EXTRA_CXXFLAGS="" +EXTRA_LDFLAGS="" + xen_ctrl_version="$default_feature" xfs="$default_feature" membarrier="$default_feature" @@ -394,13 +398,13 @@ for opt do ;; --cpu=*) cpu="$optarg" ;; - --extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg" - QEMU_LDFLAGS="$QEMU_LDFLAGS $optarg" + --extra-cflags=*) + EXTRA_CFLAGS="$EXTRA_CFLAGS $optarg" + EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS $optarg" + ;; + --extra-cxxflags=*) EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS $optarg" ;; - --extra-cxxflags=*) QEMU_CXXFLAGS="$QEMU_CXXFLAGS $optarg" - ;; - --extra-ldflags=*) QEMU_LDFLAGS="$QEMU_LDFLAGS $optarg" - EXTRA_LDFLAGS="$optarg" + --extra-ldflags=*) EXTRA_LDFLAGS="$EXTRA_LDFLAGS $optarg" ;; --enable-debug-info) debug_info="yes" ;; @@ -1346,8 +1350,8 @@ Advanced options (experts only): build time --cxx=CXX use C++ compiler CXX [$cxx] --objcc=OBJCC use Objective-C compiler OBJCC [$objcc] - --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS - --extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS + --extra-cflags=CFLAGS append extra C compiler flags CFLAGS + --extra-cxxflags=CXXFLAGS append extra C++ compiler flags CXXFLAGS --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS --cross-cc-ARCH=CC use compiler when building ARCH guest test cases --cross-cc-flags-ARCH= use compiler flags when building ARCH guest tests @@ -3402,7 +3406,7 @@ EOF update_cxxflags - if do_cxx $CXXFLAGS $CONFIGURE_CXXFLAGS $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $QEMU_LDFLAGS; then + if do_cxx $CXXFLAGS $EXTRA_CXXFLAGS $CONFIGURE_CXXFLAGS $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $QEMU_LDFLAGS; then # C++ compiler $cxx works ok with C compiler $cc : else @@ -3909,10 +3913,10 @@ if test "$skip_meson" = no; then test -z "$cxx" && echo "link_language = 'c'" >> $cross echo "[built-in options]" >> $cross - echo "c_args = [$(meson_quote $CFLAGS)]" >> $cross - echo "cpp_args = [$(meson_quote $CXXFLAGS)]" >> $cross - echo "c_link_args = [$(meson_quote $LDFLAGS)]" >> $cross - echo "cpp_link_args = [$(meson_quote $LDFLAGS)]" >> $cross + echo "c_args = [$(meson_quote $CFLAGS $EXTRA_CFLAGS)]" >> $cross + echo "cpp_args = [$(meson_quote $CXXFLAGS $EXTRA_CXXFLAGS)]" >> $cross + echo "c_link_args = [$(meson_quote $CFLAGS $LDFLAGS $EXTRA_CFLAGS $EXTRA_LDFLAGS)]" >> $cross + echo "cpp_link_args = [$(meson_quote $CXXFLAGS $LDFLAGS $EXTRA_CXXFLAGS $EXTRA_LDFLAGS)]" >> $cross echo "[binaries]" >> $cross echo "c = [$(meson_quote $cc $CPU_CFLAGS)]" >> $cross test -n "$cxx" && echo "cpp = [$(meson_quote $cxx $CPU_CFLAGS)]" >> $cross From de38c0cc796047c5df97672921901be7251ec23b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Nov 2021 08:58:23 +0100 Subject: [PATCH 1238/1334] configure: ignore preexisting QEMU_*FLAGS envvars User flags should be passed via CFLAGS/CXXFLAGS/LDFLAGS, or --extra-cflags/extra-cxxflags/--extra-ldflags on the command line. QEMU_CFLAGS, QEMU_CXXFLAGS and QEMU_LDFLAGS are reserved for flags detected by configure, so do not add to them and clear them at the beginning of the script. Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 9f1641e79c..89c1872c3b 100755 --- a/configure +++ b/configure @@ -158,7 +158,7 @@ update_cxxflags() { # Set QEMU_CXXFLAGS from QEMU_CFLAGS by filtering out those # options which some versions of GCC's C++ compiler complain about # because they only make sense for C programs. - QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS" + QEMU_CXXFLAGS="-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS" CONFIGURE_CXXFLAGS=$(echo "$CONFIGURE_CFLAGS" | sed s/-std=gnu11/-std=gnu++11/) for arg in $QEMU_CFLAGS; do case $arg in @@ -465,11 +465,13 @@ sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" # left shift of signed integers is well defined and has the expected # 2s-complement style results. (Both clang and gcc agree that it # provides these semantics.) -QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv $QEMU_CFLAGS" +QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv" QEMU_CFLAGS="-Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" +QEMU_LDFLAGS= + # Flags that are needed during configure but later taken care of by Meson CONFIGURE_CFLAGS="-std=gnu11 -Wall" CONFIGURE_LDFLAGS= From 8b4ed0dabae559ebe1fd6f8eb54e1ec6000a0a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 8 Nov 2021 09:31:29 +0100 Subject: [PATCH 1239/1334] ui/gtk-egl: Fix build failure when libgbm is not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 4872a023a59 ("ui/gtk-egl: guest fb texture needs to be regenerated when reinitializing egl") we get on Ubuntu 18.04.4 LTS and Debian Buster (oldstable): $ ../configure --enable-virglrenderer [...] ui/gtk-egl.c: In function 'gd_egl_refresh': ui/gtk-egl.c:159:13: error: implicit declaration of function 'egl_dmabuf_release_texture' [-Werror=implicit-function-declaration] 159 | egl_dmabuf_release_texture(vc->gfx.guest_fb.dmabuf); | ^~~~~~~~~~~~~~~~~~~~~~~~~~ ui/gtk-egl.c:159:13: error: this function declaration is not a prototype [-Werror,-Wstrict-prototypes] ui/gtk-egl.c:159:13: error: nested extern declaration of 'egl_dmabuf_release_texture' [-Werror=nested-externs] Fix by restricting the egl_dmabuf_release_texture() call to the availability of the generic buffer management library (libgbm). Fixes: 4872a023a593e6519b272a Cc: Dongwon Kim Cc: Vivek Kasireddy Reported-by: Laurent Vivier Reviewed-by: Mark Cave-Ayland Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Tested-by: Laurent Vivier Message-Id: <20211108083129.1262040-1-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- ui/gtk-egl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index f2026e4b5c..45cb67712d 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -155,10 +155,12 @@ void gd_egl_refresh(DisplayChangeListener *dcl) surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); } +#ifdef CONFIG_GBM if (vc->gfx.guest_fb.dmabuf) { egl_dmabuf_release_texture(vc->gfx.guest_fb.dmabuf); gd_egl_scanout_dmabuf(dcl, vc->gfx.guest_fb.dmabuf); } +#endif } graphic_hw_update(dcl->con); From 333d7036ef6cf959a1f883fe93042047bef73497 Mon Sep 17 00:00:00 2001 From: Willian Rampazzo Date: Fri, 5 Nov 2021 12:53:53 -0300 Subject: [PATCH 1240/1334] tests/acceptance: introduce new check-avocado target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces a new `make` target, `check-avocado`, and adds a deprecation message about the `check-acceptance` target. This is a preparation for renaming the `tests/acceptance` folder to `tests/avocado`. The plan is to remove the call to the `check-avocado` target one or two months after the release and leave the warning to force people to move to the new `check-avocado` target. Later, the `check-acceptance` target can be removed. The intent is to avoid a direct impact during the current soft freeze. Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Willian Rampazzo Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Thomas Huth Message-Id: <20211105155354.154864-2-willianr@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/Makefile.include | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 8434a33fe6..8e8ee58493 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -16,7 +16,7 @@ ifneq ($(filter $(all-check-targets), check-softfloat),) @echo " $(MAKE) check-tcg Run TCG tests" @echo " $(MAKE) check-softfloat Run FPU emulation tests" endif - @echo " $(MAKE) check-acceptance Run acceptance (functional) tests for currently configured targets" + @echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets" @echo @echo " $(MAKE) check-report.tap Generates an aggregated TAP test report" @echo " $(MAKE) check-venv Creates a Python venv for tests" @@ -24,7 +24,7 @@ endif @echo @echo "The following are useful for CI builds" @echo " $(MAKE) check-build Build most test binaris" - @echo " $(MAKE) get-vm-images Downloads all images used by acceptance tests, according to configured targets (~350 MB each, 1.5 GB max)" + @echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)" @echo @echo @echo "The variable SPEED can be set to control the gtester speed setting." @@ -83,7 +83,7 @@ clean-tcg: $(CLEAN_TCG_TARGET_RULES) # Python venv for running tests -.PHONY: check-venv check-acceptance +.PHONY: check-venv check-avocado check-acceptance check-acceptance-deprecated-warning TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt @@ -127,12 +127,12 @@ get-vm-image-fedora-31-%: check-venv $(call quiet-command, \ $(TESTS_VENV_DIR)/bin/python -m avocado vmimage get \ --distro=fedora --distro-version=31 --arch=$*, \ - "AVOCADO", "Downloading acceptance tests VM image for $*") + "AVOCADO", "Downloading avocado tests VM image for $*") # download all vm images, according to defined targets get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD)) -check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images +check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images $(call quiet-command, \ $(TESTS_VENV_DIR)/bin/python -m avocado \ --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ @@ -142,6 +142,13 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ "AVOCADO", "tests/acceptance") +check-acceptance-deprecated-warning: + @echo + @echo "Note '$(MAKE) check-acceptance' is deprecated, use '$(MAKE) check-avocado' instead." + @echo + +check-acceptance: check-acceptance-deprecated-warning | check-avocado + # Consolidated targets .PHONY: check-block check check-clean get-vm-images From bbbd9b6ec645ca45c2195e894537da4964f1aa12 Mon Sep 17 00:00:00 2001 From: Willian Rampazzo Date: Fri, 5 Nov 2021 12:53:54 -0300 Subject: [PATCH 1241/1334] tests/acceptance: rename tests acceptance to tests avocado MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the discussion about renaming the `tests/acceptance` [1], the conclusion was that the folders inside `tests` are related to the framework running the tests and not directly related to the type of the tests. This changes the folder to `tests/avocado` and adjusts the MAKEFILE, the CI related files and the documentation. [1] https://lists.gnu.org/archive/html/qemu-devel/2021-05/msg06553.html Reviewed-by: Niek Linnenbank Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Willian Rampazzo Message-Id: <20211105155354.154864-3-willianr@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- .gitlab-ci.d/buildtest-template.yml | 2 +- .gitlab-ci.d/buildtest.yml | 56 +++++++++---------- MAINTAINERS | 52 ++++++++--------- configure | 2 +- docs/devel/build-system.rst | 2 +- docs/devel/ci-definitions.rst.inc | 2 +- docs/devel/testing.rst | 49 ++++++++-------- docs/system/arm/orangepi.rst | 8 +-- python/qemu/machine/README.rst | 2 +- python/qemu/qmp/README.rst | 2 +- python/qemu/utils/README.rst | 2 +- tests/Makefile.include | 4 +- tests/acceptance/README.rst | 10 ---- tests/avocado/README.rst | 10 ++++ .../avocado_qemu/__init__.py | 2 +- tests/{acceptance => avocado}/boot_linux.py | 0 .../boot_linux_console.py | 0 tests/{acceptance => avocado}/boot_xen.py | 0 tests/{acceptance => avocado}/cpu_queries.py | 0 .../empty_cpu_model.py | 0 tests/{acceptance => avocado}/hotplug_cpu.py | 0 tests/{acceptance => avocado}/info_usernet.py | 0 tests/{acceptance => avocado}/intel_iommu.py | 0 tests/{acceptance => avocado}/linux_initrd.py | 2 +- .../linux_ssh_mips_malta.py | 0 .../machine_arm_canona1100.py | 0 .../machine_arm_integratorcp.py | 0 .../machine_arm_n8x0.py | 0 tests/{acceptance => avocado}/machine_avr6.py | 2 +- .../machine_m68k_nextcube.py | 0 .../machine_microblaze.py | 0 .../machine_mips_fuloong2e.py | 0 .../machine_mips_loongson3v.py | 0 .../machine_mips_malta.py | 0 .../machine_rx_gdbsim.py | 0 .../machine_s390_ccw_virtio.py | 0 .../machine_sparc64_sun4u.py | 0 .../machine_sparc_leon3.py | 0 tests/{acceptance => avocado}/migration.py | 0 tests/{acceptance => avocado}/multiprocess.py | 0 .../pc_cpu_hotplug_props.py | 0 tests/{acceptance => avocado}/ppc_405.py | 0 tests/{acceptance => avocado}/ppc_bamboo.py | 0 .../{acceptance => avocado}/ppc_mpc8544ds.py | 0 tests/{acceptance => avocado}/ppc_prep_40p.py | 0 tests/{acceptance => avocado}/ppc_pseries.py | 0 .../ppc_virtex_ml507.py | 0 .../{acceptance => avocado}/replay_kernel.py | 0 tests/{acceptance => avocado}/replay_linux.py | 0 .../reverse_debugging.py | 0 tests/{acceptance => avocado}/smmu.py | 0 tests/{acceptance => avocado}/tcg_plugins.py | 0 .../tesseract_utils.py | 0 tests/{acceptance => avocado}/version.py | 0 tests/{acceptance => avocado}/virtio-gpu.py | 0 .../virtio_check_params.py | 0 .../{acceptance => avocado}/virtio_version.py | 0 .../virtiofs_submounts.py | 0 .../virtiofs_submounts.py.data/cleanup.sh | 0 .../guest-cleanup.sh | 0 .../virtiofs_submounts.py.data/guest.sh | 0 .../virtiofs_submounts.py.data/host.sh | 0 tests/{acceptance => avocado}/vnc.py | 0 .../x86_cpu_model_versions.py | 0 64 files changed, 104 insertions(+), 105 deletions(-) delete mode 100644 tests/acceptance/README.rst create mode 100644 tests/avocado/README.rst rename tests/{acceptance => avocado}/avocado_qemu/__init__.py (99%) rename tests/{acceptance => avocado}/boot_linux.py (100%) rename tests/{acceptance => avocado}/boot_linux_console.py (100%) rename tests/{acceptance => avocado}/boot_xen.py (100%) rename tests/{acceptance => avocado}/cpu_queries.py (100%) rename tests/{acceptance => avocado}/empty_cpu_model.py (100%) rename tests/{acceptance => avocado}/hotplug_cpu.py (100%) rename tests/{acceptance => avocado}/info_usernet.py (100%) rename tests/{acceptance => avocado}/intel_iommu.py (100%) rename tests/{acceptance => avocado}/linux_initrd.py (99%) rename tests/{acceptance => avocado}/linux_ssh_mips_malta.py (100%) rename tests/{acceptance => avocado}/machine_arm_canona1100.py (100%) rename tests/{acceptance => avocado}/machine_arm_integratorcp.py (100%) rename tests/{acceptance => avocado}/machine_arm_n8x0.py (100%) rename tests/{acceptance => avocado}/machine_avr6.py (98%) rename tests/{acceptance => avocado}/machine_m68k_nextcube.py (100%) rename tests/{acceptance => avocado}/machine_microblaze.py (100%) rename tests/{acceptance => avocado}/machine_mips_fuloong2e.py (100%) rename tests/{acceptance => avocado}/machine_mips_loongson3v.py (100%) rename tests/{acceptance => avocado}/machine_mips_malta.py (100%) rename tests/{acceptance => avocado}/machine_rx_gdbsim.py (100%) rename tests/{acceptance => avocado}/machine_s390_ccw_virtio.py (100%) rename tests/{acceptance => avocado}/machine_sparc64_sun4u.py (100%) rename tests/{acceptance => avocado}/machine_sparc_leon3.py (100%) rename tests/{acceptance => avocado}/migration.py (100%) rename tests/{acceptance => avocado}/multiprocess.py (100%) rename tests/{acceptance => avocado}/pc_cpu_hotplug_props.py (100%) rename tests/{acceptance => avocado}/ppc_405.py (100%) rename tests/{acceptance => avocado}/ppc_bamboo.py (100%) rename tests/{acceptance => avocado}/ppc_mpc8544ds.py (100%) rename tests/{acceptance => avocado}/ppc_prep_40p.py (100%) rename tests/{acceptance => avocado}/ppc_pseries.py (100%) rename tests/{acceptance => avocado}/ppc_virtex_ml507.py (100%) rename tests/{acceptance => avocado}/replay_kernel.py (100%) rename tests/{acceptance => avocado}/replay_linux.py (100%) rename tests/{acceptance => avocado}/reverse_debugging.py (100%) rename tests/{acceptance => avocado}/smmu.py (100%) rename tests/{acceptance => avocado}/tcg_plugins.py (100%) rename tests/{acceptance => avocado}/tesseract_utils.py (100%) rename tests/{acceptance => avocado}/version.py (100%) rename tests/{acceptance => avocado}/virtio-gpu.py (100%) rename tests/{acceptance => avocado}/virtio_check_params.py (100%) rename tests/{acceptance => avocado}/virtio_version.py (100%) rename tests/{acceptance => avocado}/virtiofs_submounts.py (100%) rename tests/{acceptance => avocado}/virtiofs_submounts.py.data/cleanup.sh (100%) rename tests/{acceptance => avocado}/virtiofs_submounts.py.data/guest-cleanup.sh (100%) rename tests/{acceptance => avocado}/virtiofs_submounts.py.data/guest.sh (100%) rename tests/{acceptance => avocado}/virtiofs_submounts.py.data/host.sh (100%) rename tests/{acceptance => avocado}/vnc.py (100%) rename tests/{acceptance => avocado}/x86_cpu_model_versions.py (100%) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index fcbcc4e627..2c7980a4f6 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -37,7 +37,7 @@ # Avoid recompiling by hiding ninja with NINJA=":" - make NINJA=":" $MAKE_CHECK_ARGS -.acceptance_test_job_template: +.avocado_test_job_template: extends: .native_test_job_template cache: key: "${CI_JOB_NAME}-cache" diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 6c1301e912..71d0f407ad 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -26,14 +26,14 @@ check-system-alpine: IMAGE: alpine MAKE_CHECK_ARGS: check -acceptance-system-alpine: - extends: .acceptance_test_job_template +avocado-system-alpine: + extends: .avocado_test_job_template needs: - job: build-system-alpine artifacts: true variables: IMAGE: alpine - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado build-system-ubuntu: extends: .native_build_job_template @@ -59,14 +59,14 @@ check-system-ubuntu: IMAGE: ubuntu2004 MAKE_CHECK_ARGS: check -acceptance-system-ubuntu: - extends: .acceptance_test_job_template +avocado-system-ubuntu: + extends: .avocado_test_job_template needs: - job: build-system-ubuntu artifacts: true variables: IMAGE: ubuntu2004 - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado build-system-debian: extends: .native_build_job_template @@ -91,14 +91,14 @@ check-system-debian: IMAGE: debian-amd64 MAKE_CHECK_ARGS: check -acceptance-system-debian: - extends: .acceptance_test_job_template +avocado-system-debian: + extends: .avocado_test_job_template needs: - job: build-system-debian artifacts: true variables: IMAGE: debian-amd64 - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado build-system-fedora: extends: .native_build_job_template @@ -125,14 +125,14 @@ check-system-fedora: IMAGE: fedora MAKE_CHECK_ARGS: check -acceptance-system-fedora: - extends: .acceptance_test_job_template +avocado-system-fedora: + extends: .avocado_test_job_template needs: - job: build-system-fedora artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado build-system-centos: extends: .native_build_job_template @@ -159,14 +159,14 @@ check-system-centos: IMAGE: centos8 MAKE_CHECK_ARGS: check -acceptance-system-centos: - extends: .acceptance_test_job_template +avocado-system-centos: + extends: .avocado_test_job_template needs: - job: build-system-centos artifacts: true variables: IMAGE: centos8 - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado build-system-opensuse: extends: .native_build_job_template @@ -191,14 +191,14 @@ check-system-opensuse: IMAGE: opensuse-leap MAKE_CHECK_ARGS: check -acceptance-system-opensuse: - extends: .acceptance_test_job_template +avocado-system-opensuse: + extends: .avocado_test_job_template needs: - job: build-system-opensuse artifacts: true variables: IMAGE: opensuse-leap - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado # This jobs explicitly disable TCG (--disable-tcg), KVM is detected by @@ -317,7 +317,7 @@ clang-user: # This can be accomplished by using -enable-slirp=git, which avoids the use of # a system-wide version of the library # -# Split in three sets of build/check/acceptance to limit the execution time of each +# Split in three sets of build/check/avocado to limit the execution time of each # job build-cfi-aarch64: extends: .native_build_job_template @@ -352,14 +352,14 @@ check-cfi-aarch64: IMAGE: fedora MAKE_CHECK_ARGS: check -acceptance-cfi-aarch64: - extends: .acceptance_test_job_template +avocado-cfi-aarch64: + extends: .avocado_test_job_template needs: - job: build-cfi-aarch64 artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado build-cfi-ppc64-s390x: extends: .native_build_job_template @@ -394,14 +394,14 @@ check-cfi-ppc64-s390x: IMAGE: fedora MAKE_CHECK_ARGS: check -acceptance-cfi-ppc64-s390x: - extends: .acceptance_test_job_template +avocado-cfi-ppc64-s390x: + extends: .avocado_test_job_template needs: - job: build-cfi-ppc64-s390x artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado build-cfi-x86_64: extends: .native_build_job_template @@ -430,14 +430,14 @@ check-cfi-x86_64: IMAGE: fedora MAKE_CHECK_ARGS: check -acceptance-cfi-x86_64: - extends: .acceptance_test_job_template +avocado-cfi-x86_64: + extends: .avocado_test_job_template needs: - job: build-cfi-x86_64 artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-acceptance + MAKE_CHECK_ARGS: check-avocado tsan-build: extends: .native_build_job_template diff --git a/MAINTAINERS b/MAINTAINERS index 797be5b366..53b63df407 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -177,7 +177,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* -F: tests/acceptance/smmu.py +F: tests/avocado/smmu.py AVR TCG CPUs M: Michael Rolnik @@ -185,7 +185,7 @@ S: Maintained F: docs/system/target-avr.rst F: gdb-xml/avr-cpu.xml F: target/avr/ -F: tests/acceptance/machine_avr6.py +F: tests/avocado/machine_avr6.py CRIS TCG CPUs M: Edgar E. Iglesias @@ -657,7 +657,7 @@ S: Odd Fixes F: include/hw/arm/digic.h F: hw/*/digic* F: include/hw/*/digic* -F: tests/acceptance/machine_arm_canona1100.py +F: tests/avocado/machine_arm_canona1100.py F: docs/system/arm/digic.rst Goldfish RTC @@ -708,7 +708,7 @@ S: Maintained F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h -F: tests/acceptance/machine_arm_integratorcp.py +F: tests/avocado/machine_arm_integratorcp.py F: docs/system/arm/integratorcp.rst MCIMX6UL EVK / i.MX6ul @@ -805,7 +805,7 @@ F: include/hw/display/blizzard.h F: include/hw/input/lm832x.h F: include/hw/input/tsc2xxx.h F: include/hw/misc/cbus.h -F: tests/acceptance/machine_arm_n8x0.py +F: tests/avocado/machine_arm_n8x0.py F: docs/system/arm/nseries.rst Palm @@ -1159,7 +1159,7 @@ M: Edgar E. Iglesias S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c F: include/hw/char/xilinx_uartlite.h -F: tests/acceptance/machine_microblaze.py +F: tests/avocado/machine_microblaze.py petalogix_ml605 M: Edgar E. Iglesias @@ -1192,8 +1192,8 @@ F: hw/acpi/piix4.c F: hw/mips/malta.c F: hw/mips/gt64xxx_pci.c F: include/hw/southbridge/piix.h -F: tests/acceptance/linux_ssh_mips_malta.py -F: tests/acceptance/machine_mips_malta.py +F: tests/avocado/linux_ssh_mips_malta.py +F: tests/avocado/machine_mips_malta.py Mipssim R: Aleksandar Rikalo @@ -1211,7 +1211,7 @@ F: hw/isa/vt82c686.c F: hw/pci-host/bonito.c F: hw/usb/vt82c686-uhci-pci.c F: include/hw/isa/vt82c686.h -F: tests/acceptance/machine_mips_fuloong2e.py +F: tests/avocado/machine_mips_fuloong2e.py Loongson-3 virtual platforms M: Huacai Chen @@ -1221,7 +1221,7 @@ F: hw/intc/loongson_liointc.c F: hw/mips/loongson3_bootp.c F: hw/mips/loongson3_bootp.h F: hw/mips/loongson3_virt.c -F: tests/acceptance/machine_mips_loongson3v.py +F: tests/avocado/machine_mips_loongson3v.py Boston M: Paul Burton @@ -1250,7 +1250,7 @@ Bamboo L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c -F: tests/acceptance/ppc_bamboo.py +F: tests/avocado/ppc_bamboo.py e500 L: qemu-ppc@nongnu.org @@ -1271,7 +1271,7 @@ L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544_guts.c -F: tests/acceptance/ppc_mpc8544ds.py +F: tests/avocado/ppc_mpc8544ds.py New World (mac99) M: Mark Cave-Ayland @@ -1318,7 +1318,7 @@ F: hw/dma/i82374.c F: hw/rtc/m48t59-isa.c F: include/hw/isa/pc87312.h F: include/hw/rtc/m48t59.h -F: tests/acceptance/ppc_prep_40p.py +F: tests/avocado/ppc_prep_40p.py sPAPR M: David Gibson @@ -1336,7 +1336,7 @@ F: tests/qtest/spapr* F: tests/qtest/libqos/*spapr* F: tests/qtest/rtas* F: tests/qtest/libqos/rtas* -F: tests/acceptance/ppc_pseries.py +F: tests/avocado/ppc_pseries.py PowerNV (Non-Virtualized) M: Cédric Le Goater @@ -1356,7 +1356,7 @@ M: Edgar E. Iglesias L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/virtex_ml507.c -F: tests/acceptance/ppc_virtex_ml507.py +F: tests/avocado/ppc_virtex_ml507.py sam460ex M: BALATON Zoltan @@ -1443,7 +1443,7 @@ R: Yoshinori Sato S: Orphan F: docs/system/target-rx.rst F: hw/rx/rx-gdbsim.c -F: tests/acceptance/machine_rx_gdbsim.py +F: tests/avocado/machine_rx_gdbsim.py SH4 Machines ------------ @@ -1497,7 +1497,7 @@ F: include/hw/pci-host/sabre.h F: hw/pci-bridge/simba.c F: include/hw/pci-bridge/simba.h F: pc-bios/openbios-sparc64 -F: tests/acceptance/machine_sparc64_sun4u.py +F: tests/avocado/machine_sparc64_sun4u.py Sun4v M: Artyom Tarasenko @@ -1513,7 +1513,7 @@ S: Maintained F: hw/sparc/leon3.c F: hw/*/grlib* F: include/hw/*/grlib* -F: tests/acceptance/machine_sparc_leon3.py +F: tests/avocado/machine_sparc_leon3.py S390 Machines ------------- @@ -1528,7 +1528,7 @@ F: include/hw/s390x/ F: hw/watchdog/wdt_diag288.c F: include/hw/watchdog/wdt_diag288.h F: configs/devices/s390x-softmmu/default.mak -F: tests/acceptance/machine_s390_ccw_virtio.py +F: tests/avocado/machine_s390_ccw_virtio.py T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -2112,7 +2112,7 @@ M: Alex Bennée S: Maintained F: hw/core/guest-loader.c F: docs/system/guest-loader.rst -F: tests/acceptance/boot_xen.py +F: tests/avocado/boot_xen.py Intel Hexadecimal Object File Loader M: Su Hang @@ -2986,9 +2986,9 @@ F: net/filter-replay.c F: include/sysemu/replay.h F: docs/replay.txt F: stubs/replay.c -F: tests/acceptance/replay_kernel.py -F: tests/acceptance/replay_linux.py -F: tests/acceptance/reverse_debugging.py +F: tests/avocado/replay_kernel.py +F: tests/avocado/replay_linux.py +F: tests/avocado/reverse_debugging.py F: qapi/replay.json IOVA Tree @@ -3105,7 +3105,7 @@ S: Maintained F: docs/devel/tcg-plugins.rst F: plugins/ F: tests/plugin/ -F: tests/acceptance/tcg_plugins.py +F: tests/avocado/tcg_plugins.py F: contrib/plugins/ AArch64 TCG target @@ -3494,14 +3494,14 @@ S: Maintained F: tests/tcg/Makefile F: tests/tcg/Makefile.include -Acceptance (Integration) Testing with the Avocado framework +Integration Testing with the Avocado framework W: https://trello.com/b/6Qi1pxVn/avocado-qemu R: Cleber Rosa R: Philippe Mathieu-Daudé R: Wainer dos Santos Moschetta R: Willian Rampazzo S: Odd Fixes -F: tests/acceptance/ +F: tests/avocado/ Documentation ------------- diff --git a/configure b/configure index 33682cb971..19d5e97b27 100755 --- a/configure +++ b/configure @@ -3832,7 +3832,7 @@ LINKS="$LINKS pc-bios/s390-ccw/Makefile" LINKS="$LINKS roms/seabios/Makefile" LINKS="$LINKS pc-bios/qemu-icon.bmp" LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit -LINKS="$LINKS tests/acceptance tests/data" +LINKS="$LINKS tests/avocado tests/data" LINKS="$LINKS tests/qemu-iotests/check" LINKS="$LINKS python" LINKS="$LINKS contrib/plugins/Makefile " diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index ae536ef75d..aca1f5a273 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -416,7 +416,7 @@ number of dynamically created files listed later. ``tests/Makefile.include`` Rules for external test harnesses. These include the TCG tests, - ``qemu-iotests`` and the Avocado-based acceptance tests. + ``qemu-iotests`` and the Avocado-based integration tests. ``tests/docker/Makefile.include`` Rules for Docker tests. Like tests/Makefile, this file is included diff --git a/docs/devel/ci-definitions.rst.inc b/docs/devel/ci-definitions.rst.inc index 32e22ff468..6d5c6fd9f2 100644 --- a/docs/devel/ci-definitions.rst.inc +++ b/docs/devel/ci-definitions.rst.inc @@ -59,7 +59,7 @@ to system testing [5]_. Note that, in some cases, system testing may require interaction with third-party software, like operating system images, databases, networks, and so on. -On QEMU, system testing is represented by the 'check-acceptance' target from +On QEMU, system testing is represented by the 'check-avocado' target from 'make'. Flaky tests diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 7500f076c2..dc5dbd057d 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -653,17 +653,16 @@ supported. To start the fuzzer, run Alternatively, some command different from "qemu-img info" can be tested, by changing the ``-c`` option. -Acceptance tests using the Avocado Framework --------------------------------------------- +Integration tests using the Avocado Framework +--------------------------------------------- -The ``tests/acceptance`` directory hosts functional tests, also known -as acceptance level tests. They're usually higher level tests, and -may interact with external resources and with various guest operating -systems. +The ``tests/avocado`` directory hosts integration tests. They're usually +higher level tests, and may interact with external resources and with +various guest operating systems. These tests are written using the Avocado Testing Framework (which must be installed separately) in conjunction with a the ``avocado_qemu.Test`` -class, implemented at ``tests/acceptance/avocado_qemu``. +class, implemented at ``tests/avocado/avocado_qemu``. Tests based on ``avocado_qemu.Test`` can easily: @@ -695,11 +694,11 @@ Tests based on ``avocado_qemu.Test`` can easily: Running tests ~~~~~~~~~~~~~ -You can run the acceptance tests simply by executing: +You can run the avocado tests simply by executing: .. code:: - make check-acceptance + make check-avocado This involves the automatic creation of Python virtual environment within the build tree (at ``tests/venv``) which will have all the @@ -714,12 +713,12 @@ specific version, they may be on packages named ``python3-venv`` and ``python3-pip``. It is also possible to run tests based on tags using the -``make check-acceptance`` command and the ``AVOCADO_TAGS`` environment +``make check-avocado`` command and the ``AVOCADO_TAGS`` environment variable: .. code:: - make check-acceptance AVOCADO_TAGS=quick + make check-avocado AVOCADO_TAGS=quick Note that tags separated with commas have an AND behavior, while tags separated by spaces have an OR behavior. For more information on Avocado @@ -728,31 +727,31 @@ tags, see: https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/tags.html To run a single test file, a couple of them, or a test within a file -using the ``make check-acceptance`` command, set the ``AVOCADO_TESTS`` +using the ``make check-avocado`` command, set the ``AVOCADO_TESTS`` environment variable with the test files or test names. To run all tests from a single file, use: .. code:: - make check-acceptance AVOCADO_TESTS=$FILEPATH + make check-avocado AVOCADO_TESTS=$FILEPATH The same is valid to run tests from multiple test files: .. code:: - make check-acceptance AVOCADO_TESTS='$FILEPATH1 $FILEPATH2' + make check-avocado AVOCADO_TESTS='$FILEPATH1 $FILEPATH2' To run a single test within a file, use: .. code:: - make check-acceptance AVOCADO_TESTS=$FILEPATH:$TESTCLASS.$TESTNAME + make check-avocado AVOCADO_TESTS=$FILEPATH:$TESTCLASS.$TESTNAME The same is valid to run single tests from multiple test files: .. code:: - make check-acceptance AVOCADO_TESTS='$FILEPATH1:$TESTCLASS1.$TESTNAME1 $FILEPATH2:$TESTCLASS2.$TESTNAME2' + make check-avocado AVOCADO_TESTS='$FILEPATH1:$TESTCLASS1.$TESTNAME1 $FILEPATH2:$TESTCLASS2.$TESTNAME2' The scripts installed inside the virtual environment may be used without an "activation". For instance, the Avocado test runner @@ -760,9 +759,9 @@ may be invoked by running: .. code:: - tests/venv/bin/avocado run $OPTION1 $OPTION2 tests/acceptance/ + tests/venv/bin/avocado run $OPTION1 $OPTION2 tests/avocado/ -Note that if ``make check-acceptance`` was not executed before, it is +Note that if ``make check-avocado`` was not executed before, it is possible to create the Python virtual environment with the dependencies needed running: @@ -775,20 +774,20 @@ a test file. To run tests from a single file within the build tree, use: .. code:: - tests/venv/bin/avocado run tests/acceptance/$TESTFILE + tests/venv/bin/avocado run tests/avocado/$TESTFILE To run a single test within a test file, use: .. code:: - tests/venv/bin/avocado run tests/acceptance/$TESTFILE:$TESTCLASS.$TESTNAME + tests/venv/bin/avocado run tests/avocado/$TESTFILE:$TESTCLASS.$TESTNAME Valid test names are visible in the output from any previous execution -of Avocado or ``make check-acceptance``, and can also be queried using: +of Avocado or ``make check-avocado``, and can also be queried using: .. code:: - tests/venv/bin/avocado list tests/acceptance + tests/venv/bin/avocado list tests/avocado Manual Installation ~~~~~~~~~~~~~~~~~~~ @@ -806,7 +805,7 @@ Alternatively, follow the instructions on this link: Overview ~~~~~~~~ -The ``tests/acceptance/avocado_qemu`` directory provides the +The ``tests/avocado/avocado_qemu`` directory provides the ``avocado_qemu`` Python module, containing the ``avocado_qemu.Test`` class. Here's a simple usage example: @@ -913,7 +912,7 @@ like this: self.ssh_command('some_command_to_be_run_in_the_guest') Please refer to tests that use ``avocado_qemu.LinuxTest`` under -``tests/acceptance`` for more examples. +``tests/avocado`` for more examples. QEMUMachine ~~~~~~~~~~~ @@ -1204,7 +1203,7 @@ And remove any package you want with:: pip uninstall -If you've used ``make check-acceptance``, the Python virtual environment where +If you've used ``make check-avocado``, the Python virtual environment where Avocado is installed will be cleaned up as part of ``make check-clean``. .. _checktcg-ref: diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst index 6f23907fb6..c55694dd91 100644 --- a/docs/system/arm/orangepi.rst +++ b/docs/system/arm/orangepi.rst @@ -250,14 +250,14 @@ and set the following environment variables before booting: Optionally you may save the environment variables to SD card with 'saveenv'. To continue booting simply give the 'boot' command and NetBSD boots. -Orange Pi PC acceptance tests -""""""""""""""""""""""""""""" +Orange Pi PC integration tests +"""""""""""""""""""""""""""""" -The Orange Pi PC machine has several acceptance tests included. +The Orange Pi PC machine has several integration tests included. To run the whole set of tests, build QEMU from source and simply provide the following command: .. code-block:: bash $ AVOCADO_ALLOW_LARGE_STORAGE=yes avocado --show=app,console run \ - -t machine:orangepi-pc tests/acceptance/boot_linux_console.py + -t machine:orangepi-pc tests/avocado/boot_linux_console.py diff --git a/python/qemu/machine/README.rst b/python/qemu/machine/README.rst index ac2b4fffb4..8de2c3d772 100644 --- a/python/qemu/machine/README.rst +++ b/python/qemu/machine/README.rst @@ -2,7 +2,7 @@ qemu.machine package ==================== This package provides core utilities used for testing and debugging -QEMU. It is used by the iotests, vm tests, acceptance tests, and several +QEMU. It is used by the iotests, vm tests, avocado tests, and several other utilities in the ./scripts directory. It is not a fully-fledged SDK and it is subject to change at any time. diff --git a/python/qemu/qmp/README.rst b/python/qemu/qmp/README.rst index c21951491c..5bfb82535f 100644 --- a/python/qemu/qmp/README.rst +++ b/python/qemu/qmp/README.rst @@ -3,7 +3,7 @@ qemu.qmp package This package provides a library used for connecting to and communicating with QMP servers. It is used extensively by iotests, vm tests, -acceptance tests, and other utilities in the ./scripts directory. It is +avocado tests, and other utilities in the ./scripts directory. It is not a fully-fledged SDK and is subject to change at any time. See the documentation in ``__init__.py`` for more information. diff --git a/python/qemu/utils/README.rst b/python/qemu/utils/README.rst index 975fbf4d7d..d5f2da1454 100644 --- a/python/qemu/utils/README.rst +++ b/python/qemu/utils/README.rst @@ -2,6 +2,6 @@ qemu.utils package ================== This package provides miscellaneous utilities used for testing and -debugging QEMU. It is used primarily by the vm and acceptance tests. +debugging QEMU. It is used primarily by the vm and avocado tests. See the documentation in ``__init__.py`` for more information. diff --git a/tests/Makefile.include b/tests/Makefile.include index 8e8ee58493..4c564cf789 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -89,7 +89,7 @@ TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results ifndef AVOCADO_TESTS - AVOCADO_TESTS=tests/acceptance + AVOCADO_TESTS=tests/avocado endif # Controls the output generated by Avocado when running tests. # Any number of command separated loggers are accepted. For more @@ -140,7 +140,7 @@ check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images --filter-by-tags-include-empty-key) \ $(AVOCADO_CMDLINE_TAGS) \ $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ - "AVOCADO", "tests/acceptance") + "AVOCADO", "tests/avocado") check-acceptance-deprecated-warning: @echo diff --git a/tests/acceptance/README.rst b/tests/acceptance/README.rst deleted file mode 100644 index 89260faed6..0000000000 --- a/tests/acceptance/README.rst +++ /dev/null @@ -1,10 +0,0 @@ -============================================ -Acceptance tests using the Avocado Framework -============================================ - -This directory contains functional tests, also known as acceptance -level tests. They're usually higher level, and may interact with -external resources and with various guest operating systems. - -For more information, please refer to ``docs/devel/testing.rst``, -section "Acceptance tests using the Avocado Framework". diff --git a/tests/avocado/README.rst b/tests/avocado/README.rst new file mode 100644 index 0000000000..94488371bb --- /dev/null +++ b/tests/avocado/README.rst @@ -0,0 +1,10 @@ +============================================= +Integration tests using the Avocado Framework +============================================= + +This directory contains integration tests. They're usually higher +level, and may interact with external resources and with various +guest operating systems. + +For more information, please refer to ``docs/devel/testing.rst``, +section "Integration tests using the Avocado Framework". diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py similarity index 99% rename from tests/acceptance/avocado_qemu/__init__.py rename to tests/avocado/avocado_qemu/__init__.py index 1841053e2c..cd21b59e04 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -27,7 +27,7 @@ from avocado.utils.path import find_command BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) if os.path.islink(os.path.dirname(os.path.dirname(__file__))): - # The link to the acceptance tests dir in the source code directory + # The link to the avocado tests dir in the source code directory lnk = os.path.dirname(os.path.dirname(__file__)) #: The QEMU root source directory SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) diff --git a/tests/acceptance/boot_linux.py b/tests/avocado/boot_linux.py similarity index 100% rename from tests/acceptance/boot_linux.py rename to tests/avocado/boot_linux.py diff --git a/tests/acceptance/boot_linux_console.py b/tests/avocado/boot_linux_console.py similarity index 100% rename from tests/acceptance/boot_linux_console.py rename to tests/avocado/boot_linux_console.py diff --git a/tests/acceptance/boot_xen.py b/tests/avocado/boot_xen.py similarity index 100% rename from tests/acceptance/boot_xen.py rename to tests/avocado/boot_xen.py diff --git a/tests/acceptance/cpu_queries.py b/tests/avocado/cpu_queries.py similarity index 100% rename from tests/acceptance/cpu_queries.py rename to tests/avocado/cpu_queries.py diff --git a/tests/acceptance/empty_cpu_model.py b/tests/avocado/empty_cpu_model.py similarity index 100% rename from tests/acceptance/empty_cpu_model.py rename to tests/avocado/empty_cpu_model.py diff --git a/tests/acceptance/hotplug_cpu.py b/tests/avocado/hotplug_cpu.py similarity index 100% rename from tests/acceptance/hotplug_cpu.py rename to tests/avocado/hotplug_cpu.py diff --git a/tests/acceptance/info_usernet.py b/tests/avocado/info_usernet.py similarity index 100% rename from tests/acceptance/info_usernet.py rename to tests/avocado/info_usernet.py diff --git a/tests/acceptance/intel_iommu.py b/tests/avocado/intel_iommu.py similarity index 100% rename from tests/acceptance/intel_iommu.py rename to tests/avocado/intel_iommu.py diff --git a/tests/acceptance/linux_initrd.py b/tests/avocado/linux_initrd.py similarity index 99% rename from tests/acceptance/linux_initrd.py rename to tests/avocado/linux_initrd.py index a249e2f14a..9b4880cd8c 100644 --- a/tests/acceptance/linux_initrd.py +++ b/tests/avocado/linux_initrd.py @@ -1,4 +1,4 @@ -# Linux initrd acceptance test. +# Linux initrd integration test. # # Copyright (c) 2018 Red Hat, Inc. # diff --git a/tests/acceptance/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py similarity index 100% rename from tests/acceptance/linux_ssh_mips_malta.py rename to tests/avocado/linux_ssh_mips_malta.py diff --git a/tests/acceptance/machine_arm_canona1100.py b/tests/avocado/machine_arm_canona1100.py similarity index 100% rename from tests/acceptance/machine_arm_canona1100.py rename to tests/avocado/machine_arm_canona1100.py diff --git a/tests/acceptance/machine_arm_integratorcp.py b/tests/avocado/machine_arm_integratorcp.py similarity index 100% rename from tests/acceptance/machine_arm_integratorcp.py rename to tests/avocado/machine_arm_integratorcp.py diff --git a/tests/acceptance/machine_arm_n8x0.py b/tests/avocado/machine_arm_n8x0.py similarity index 100% rename from tests/acceptance/machine_arm_n8x0.py rename to tests/avocado/machine_arm_n8x0.py diff --git a/tests/acceptance/machine_avr6.py b/tests/avocado/machine_avr6.py similarity index 98% rename from tests/acceptance/machine_avr6.py rename to tests/avocado/machine_avr6.py index 6baf4e9c7f..6bab31342a 100644 --- a/tests/acceptance/machine_avr6.py +++ b/tests/avocado/machine_avr6.py @@ -1,5 +1,5 @@ # -# QEMU AVR acceptance tests +# QEMU AVR integration tests # # Copyright (c) 2019-2020 Michael Rolnik # diff --git a/tests/acceptance/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py similarity index 100% rename from tests/acceptance/machine_m68k_nextcube.py rename to tests/avocado/machine_m68k_nextcube.py diff --git a/tests/acceptance/machine_microblaze.py b/tests/avocado/machine_microblaze.py similarity index 100% rename from tests/acceptance/machine_microblaze.py rename to tests/avocado/machine_microblaze.py diff --git a/tests/acceptance/machine_mips_fuloong2e.py b/tests/avocado/machine_mips_fuloong2e.py similarity index 100% rename from tests/acceptance/machine_mips_fuloong2e.py rename to tests/avocado/machine_mips_fuloong2e.py diff --git a/tests/acceptance/machine_mips_loongson3v.py b/tests/avocado/machine_mips_loongson3v.py similarity index 100% rename from tests/acceptance/machine_mips_loongson3v.py rename to tests/avocado/machine_mips_loongson3v.py diff --git a/tests/acceptance/machine_mips_malta.py b/tests/avocado/machine_mips_malta.py similarity index 100% rename from tests/acceptance/machine_mips_malta.py rename to tests/avocado/machine_mips_malta.py diff --git a/tests/acceptance/machine_rx_gdbsim.py b/tests/avocado/machine_rx_gdbsim.py similarity index 100% rename from tests/acceptance/machine_rx_gdbsim.py rename to tests/avocado/machine_rx_gdbsim.py diff --git a/tests/acceptance/machine_s390_ccw_virtio.py b/tests/avocado/machine_s390_ccw_virtio.py similarity index 100% rename from tests/acceptance/machine_s390_ccw_virtio.py rename to tests/avocado/machine_s390_ccw_virtio.py diff --git a/tests/acceptance/machine_sparc64_sun4u.py b/tests/avocado/machine_sparc64_sun4u.py similarity index 100% rename from tests/acceptance/machine_sparc64_sun4u.py rename to tests/avocado/machine_sparc64_sun4u.py diff --git a/tests/acceptance/machine_sparc_leon3.py b/tests/avocado/machine_sparc_leon3.py similarity index 100% rename from tests/acceptance/machine_sparc_leon3.py rename to tests/avocado/machine_sparc_leon3.py diff --git a/tests/acceptance/migration.py b/tests/avocado/migration.py similarity index 100% rename from tests/acceptance/migration.py rename to tests/avocado/migration.py diff --git a/tests/acceptance/multiprocess.py b/tests/avocado/multiprocess.py similarity index 100% rename from tests/acceptance/multiprocess.py rename to tests/avocado/multiprocess.py diff --git a/tests/acceptance/pc_cpu_hotplug_props.py b/tests/avocado/pc_cpu_hotplug_props.py similarity index 100% rename from tests/acceptance/pc_cpu_hotplug_props.py rename to tests/avocado/pc_cpu_hotplug_props.py diff --git a/tests/acceptance/ppc_405.py b/tests/avocado/ppc_405.py similarity index 100% rename from tests/acceptance/ppc_405.py rename to tests/avocado/ppc_405.py diff --git a/tests/acceptance/ppc_bamboo.py b/tests/avocado/ppc_bamboo.py similarity index 100% rename from tests/acceptance/ppc_bamboo.py rename to tests/avocado/ppc_bamboo.py diff --git a/tests/acceptance/ppc_mpc8544ds.py b/tests/avocado/ppc_mpc8544ds.py similarity index 100% rename from tests/acceptance/ppc_mpc8544ds.py rename to tests/avocado/ppc_mpc8544ds.py diff --git a/tests/acceptance/ppc_prep_40p.py b/tests/avocado/ppc_prep_40p.py similarity index 100% rename from tests/acceptance/ppc_prep_40p.py rename to tests/avocado/ppc_prep_40p.py diff --git a/tests/acceptance/ppc_pseries.py b/tests/avocado/ppc_pseries.py similarity index 100% rename from tests/acceptance/ppc_pseries.py rename to tests/avocado/ppc_pseries.py diff --git a/tests/acceptance/ppc_virtex_ml507.py b/tests/avocado/ppc_virtex_ml507.py similarity index 100% rename from tests/acceptance/ppc_virtex_ml507.py rename to tests/avocado/ppc_virtex_ml507.py diff --git a/tests/acceptance/replay_kernel.py b/tests/avocado/replay_kernel.py similarity index 100% rename from tests/acceptance/replay_kernel.py rename to tests/avocado/replay_kernel.py diff --git a/tests/acceptance/replay_linux.py b/tests/avocado/replay_linux.py similarity index 100% rename from tests/acceptance/replay_linux.py rename to tests/avocado/replay_linux.py diff --git a/tests/acceptance/reverse_debugging.py b/tests/avocado/reverse_debugging.py similarity index 100% rename from tests/acceptance/reverse_debugging.py rename to tests/avocado/reverse_debugging.py diff --git a/tests/acceptance/smmu.py b/tests/avocado/smmu.py similarity index 100% rename from tests/acceptance/smmu.py rename to tests/avocado/smmu.py diff --git a/tests/acceptance/tcg_plugins.py b/tests/avocado/tcg_plugins.py similarity index 100% rename from tests/acceptance/tcg_plugins.py rename to tests/avocado/tcg_plugins.py diff --git a/tests/acceptance/tesseract_utils.py b/tests/avocado/tesseract_utils.py similarity index 100% rename from tests/acceptance/tesseract_utils.py rename to tests/avocado/tesseract_utils.py diff --git a/tests/acceptance/version.py b/tests/avocado/version.py similarity index 100% rename from tests/acceptance/version.py rename to tests/avocado/version.py diff --git a/tests/acceptance/virtio-gpu.py b/tests/avocado/virtio-gpu.py similarity index 100% rename from tests/acceptance/virtio-gpu.py rename to tests/avocado/virtio-gpu.py diff --git a/tests/acceptance/virtio_check_params.py b/tests/avocado/virtio_check_params.py similarity index 100% rename from tests/acceptance/virtio_check_params.py rename to tests/avocado/virtio_check_params.py diff --git a/tests/acceptance/virtio_version.py b/tests/avocado/virtio_version.py similarity index 100% rename from tests/acceptance/virtio_version.py rename to tests/avocado/virtio_version.py diff --git a/tests/acceptance/virtiofs_submounts.py b/tests/avocado/virtiofs_submounts.py similarity index 100% rename from tests/acceptance/virtiofs_submounts.py rename to tests/avocado/virtiofs_submounts.py diff --git a/tests/acceptance/virtiofs_submounts.py.data/cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/cleanup.sh similarity index 100% rename from tests/acceptance/virtiofs_submounts.py.data/cleanup.sh rename to tests/avocado/virtiofs_submounts.py.data/cleanup.sh diff --git a/tests/acceptance/virtiofs_submounts.py.data/guest-cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh similarity index 100% rename from tests/acceptance/virtiofs_submounts.py.data/guest-cleanup.sh rename to tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh diff --git a/tests/acceptance/virtiofs_submounts.py.data/guest.sh b/tests/avocado/virtiofs_submounts.py.data/guest.sh similarity index 100% rename from tests/acceptance/virtiofs_submounts.py.data/guest.sh rename to tests/avocado/virtiofs_submounts.py.data/guest.sh diff --git a/tests/acceptance/virtiofs_submounts.py.data/host.sh b/tests/avocado/virtiofs_submounts.py.data/host.sh similarity index 100% rename from tests/acceptance/virtiofs_submounts.py.data/host.sh rename to tests/avocado/virtiofs_submounts.py.data/host.sh diff --git a/tests/acceptance/vnc.py b/tests/avocado/vnc.py similarity index 100% rename from tests/acceptance/vnc.py rename to tests/avocado/vnc.py diff --git a/tests/acceptance/x86_cpu_model_versions.py b/tests/avocado/x86_cpu_model_versions.py similarity index 100% rename from tests/acceptance/x86_cpu_model_versions.py rename to tests/avocado/x86_cpu_model_versions.py From 3982feb476e46bbac8f6affbc58bf715e5ca9a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 14 Feb 2021 18:59:08 +0100 Subject: [PATCH 1242/1334] tests/avocado: Extract QemuBaseTest from Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Avocado Test::fetch_asset() is handy to download artifacts before running tests. The current class is named Test but only tests system emulation. As we want to test user emulation, refactor the common code as QemuBaseTest. Reviewed-by: Willian Rampazzo Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211105143416.148332-2-f4bug@amsat.org> --- tests/avocado/avocado_qemu/__init__.py | 72 +++++++++++++++----------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index cd21b59e04..a495e106d0 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -169,7 +169,7 @@ def exec_command_and_wait_for_pattern(test, command, """ _console_interaction(test, success_message, failure_message, command + '\r') -class Test(avocado.Test): +class QemuBaseTest(avocado.Test): def _get_unique_tag_val(self, tag_name): """ Gets a tag value, if unique for a key @@ -179,6 +179,46 @@ class Test(avocado.Test): return vals.pop() return None + def setUp(self): + self.arch = self.params.get('arch', + default=self._get_unique_tag_val('arch')) + + self.cpu = self.params.get('cpu', + default=self._get_unique_tag_val('cpu')) + + default_qemu_bin = pick_default_qemu_bin(arch=self.arch) + self.qemu_bin = self.params.get('qemu_bin', + default=default_qemu_bin) + if self.qemu_bin is None: + self.cancel("No QEMU binary defined or found in the build tree") + + def fetch_asset(self, name, + asset_hash=None, algorithm=None, + locations=None, expire=None, + find_only=False, cancel_on_missing=True): + return super().fetch_asset(name, + asset_hash=asset_hash, + algorithm=algorithm, + locations=locations, + expire=expire, + find_only=find_only, + cancel_on_missing=cancel_on_missing) + + +class Test(QemuBaseTest): + """Facilitates system emulation tests. + + TODO: Rename this class as `QemuSystemTest`. + """ + + def setUp(self): + self._vms = {} + + super().setUp() + + self.machine = self.params.get('machine', + default=self._get_unique_tag_val('machine')) + def require_accelerator(self, accelerator): """ Requires an accelerator to be available for the test to continue @@ -201,24 +241,6 @@ class Test(avocado.Test): self.cancel("%s accelerator does not seem to be " "available" % accelerator) - def setUp(self): - self._vms = {} - - self.arch = self.params.get('arch', - default=self._get_unique_tag_val('arch')) - - self.cpu = self.params.get('cpu', - default=self._get_unique_tag_val('cpu')) - - self.machine = self.params.get('machine', - default=self._get_unique_tag_val('machine')) - - default_qemu_bin = pick_default_qemu_bin(arch=self.arch) - self.qemu_bin = self.params.get('qemu_bin', - default=default_qemu_bin) - if self.qemu_bin is None: - self.cancel("No QEMU binary defined or found in the build tree") - def _new_vm(self, name, *args): self._sd = tempfile.TemporaryDirectory(prefix="avo_qemu_sock_") vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir, @@ -272,18 +294,6 @@ class Test(avocado.Test): self._sd = None super().tearDown() - def fetch_asset(self, name, - asset_hash=None, algorithm=None, - locations=None, expire=None, - find_only=False, cancel_on_missing=True): - return super().fetch_asset(name, - asset_hash=asset_hash, - algorithm=algorithm, - locations=locations, - expire=expire, - find_only=find_only, - cancel_on_missing=cancel_on_missing) - class LinuxSSHMixIn: """Contains utility methods for interacting with a guest via SSH.""" From 9112d4fd493c14a30ebd5ba9853328137490aab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 14 Feb 2021 18:59:09 +0100 Subject: [PATCH 1243/1334] tests/avocado: Make pick_default_qemu_bin() more generic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make pick_default_qemu_bin() generic to find qemu-system or qemu-user binaries. Reviewed-by: Willian Rampazzo Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211105143416.148332-3-f4bug@amsat.org> --- tests/avocado/avocado_qemu/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index a495e106d0..984c554e7d 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -45,7 +45,7 @@ def is_readable_executable_file(path): return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) -def pick_default_qemu_bin(arch=None): +def pick_default_qemu_bin(bin_prefix='qemu-system-', arch=None): """ Picks the path of a QEMU binary, starting either in the current working directory or in the source tree root directory. @@ -64,7 +64,7 @@ def pick_default_qemu_bin(arch=None): # qemu binary path does not match arch for powerpc, handle it if 'ppc64le' in arch: arch = 'ppc64' - qemu_bin_relative_path = "./qemu-system-%s" % arch + qemu_bin_relative_path = os.path.join(".", bin_prefix + arch) if is_readable_executable_file(qemu_bin_relative_path): return qemu_bin_relative_path @@ -179,14 +179,14 @@ class QemuBaseTest(avocado.Test): return vals.pop() return None - def setUp(self): + def setUp(self, bin_prefix): self.arch = self.params.get('arch', default=self._get_unique_tag_val('arch')) self.cpu = self.params.get('cpu', default=self._get_unique_tag_val('cpu')) - default_qemu_bin = pick_default_qemu_bin(arch=self.arch) + default_qemu_bin = pick_default_qemu_bin(bin_prefix, arch=self.arch) self.qemu_bin = self.params.get('qemu_bin', default=default_qemu_bin) if self.qemu_bin is None: @@ -214,7 +214,7 @@ class Test(QemuBaseTest): def setUp(self): self._vms = {} - super().setUp() + super().setUp('qemu-system-') self.machine = self.params.get('machine', default=self._get_unique_tag_val('machine')) From 5334df48224f494303b4717b57b2b0cd42bd1c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 14 Feb 2021 18:59:10 +0100 Subject: [PATCH 1244/1334] tests/avocado: Introduce QemuUserTest base class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to the 'System' Test base class with methods for testing system emulation, the QemuUserTest class contains methods useful to test user-mode emulation. Reviewed-by: Wainer dos Santos Moschetta Reviewed-by: Willian Rampazzo Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211105143416.148332-4-f4bug@amsat.org> --- tests/avocado/avocado_qemu/__init__.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index 984c554e7d..e46b3ecb89 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -17,7 +17,7 @@ import time import uuid import avocado -from avocado.utils import cloudinit, datadrainer, network, ssh, vmimage +from avocado.utils import cloudinit, datadrainer, network, process, ssh, vmimage from avocado.utils.path import find_command #: The QEMU build root directory. It may also be the source directory @@ -295,6 +295,23 @@ class Test(QemuBaseTest): super().tearDown() +class QemuUserTest(QemuBaseTest): + """Facilitates user-mode emulation tests.""" + + def setUp(self): + self._ldpath = [] + super().setUp('qemu-') + + def add_ldpath(self, ldpath): + self._ldpath.append(os.path.abspath(ldpath)) + + def run(self, bin_path, args=[]): + qemu_args = " ".join(["-L %s" % ldpath for ldpath in self._ldpath]) + bin_args = " ".join(args) + return process.run("%s %s %s %s" % (self.qemu_bin, qemu_args, + bin_path, bin_args)) + + class LinuxSSHMixIn: """Contains utility methods for interacting with a guest via SSH.""" From 0e4b1c9435528c30718a2722d09ff35beaa300ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 15 Mar 2021 23:55:43 +0100 Subject: [PATCH 1245/1334] tests/avocado: Share useful helpers from virtiofs_submounts test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the useful has_cmd()/has_cmds() helpers from the virtiofs test to the avocado_qemu public class. Reviewed-by: Wainer dos Santos Moschetta Reviewed-by: Willian Rampazzo Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211105143416.148332-5-f4bug@amsat.org> --- tests/avocado/avocado_qemu/__init__.py | 57 +++++++++++++++++++++++++ tests/avocado/virtiofs_submounts.py | 59 +------------------------- 2 files changed, 59 insertions(+), 57 deletions(-) diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index e46b3ecb89..1efc22dabf 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -11,6 +11,7 @@ import logging import os import shutil +import subprocess import sys import tempfile import time @@ -41,6 +42,62 @@ from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available, tcg_available) +def has_cmd(name, args=None): + """ + This function is for use in a @avocado.skipUnless decorator, e.g.: + + @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true'))) + def test_something_that_needs_sudo(self): + ... + """ + + if args is None: + args = ('which', name) + + try: + _, stderr, exitcode = run_cmd(args) + except Exception as e: + exitcode = -1 + stderr = str(e) + + if exitcode != 0: + cmd_line = ' '.join(args) + err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}' + return (False, err) + else: + return (True, '') + +def has_cmds(*cmds): + """ + This function is for use in a @avocado.skipUnless decorator and + allows checking for the availability of multiple commands, e.g.: + + @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')), + 'cmd2', 'cmd3')) + def test_something_that_needs_cmd1_and_cmd2(self): + ... + """ + + for cmd in cmds: + if isinstance(cmd, str): + cmd = (cmd,) + + ok, errstr = has_cmd(*cmd) + if not ok: + return (False, errstr) + + return (True, '') + +def run_cmd(args): + subp = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + stdout, stderr = subp.communicate() + ret = subp.returncode + + return (stdout, stderr, ret) + def is_readable_executable_file(path): return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) diff --git a/tests/avocado/virtiofs_submounts.py b/tests/avocado/virtiofs_submounts.py index 21ad7d792e..e6dc32ffd4 100644 --- a/tests/avocado/virtiofs_submounts.py +++ b/tests/avocado/virtiofs_submounts.py @@ -6,67 +6,12 @@ import time from avocado import skipUnless from avocado_qemu import LinuxTest, BUILD_DIR +from avocado_qemu import has_cmds +from avocado_qemu import run_cmd from avocado_qemu import wait_for_console_pattern from avocado.utils import ssh -def run_cmd(args): - subp = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - stdout, stderr = subp.communicate() - ret = subp.returncode - - return (stdout, stderr, ret) - -def has_cmd(name, args=None): - """ - This function is for use in a @avocado.skipUnless decorator, e.g.: - - @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true'))) - def test_something_that_needs_sudo(self): - ... - """ - - if args is None: - args = ('which', name) - - try: - _, stderr, exitcode = run_cmd(args) - except Exception as e: - exitcode = -1 - stderr = str(e) - - if exitcode != 0: - cmd_line = ' '.join(args) - err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}' - return (False, err) - else: - return (True, '') - -def has_cmds(*cmds): - """ - This function is for use in a @avocado.skipUnless decorator and - allows checking for the availability of multiple commands, e.g.: - - @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')), - 'cmd2', 'cmd3')) - def test_something_that_needs_cmd1_and_cmd2(self): - ... - """ - - for cmd in cmds: - if isinstance(cmd, str): - cmd = (cmd,) - - ok, errstr = has_cmd(*cmd) - if not ok: - return (False, errstr) - - return (True, '') - - class VirtiofsSubmountsTest(LinuxTest): """ :avocado: tags=arch:x86_64 From 8011837a019182c61bad0d8f3a603d26dd4b6710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Jun 2019 15:26:38 +0200 Subject: [PATCH 1246/1334] tests/avocado: Add bFLT loader linux-user test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a very quick test that runs a busybox binary in bFLT format: $ AVOCADO_ALLOW_UNTRUSTED_CODE=1 \ avocado --show=app run -t linux_user tests/avocado/load_bflt.py JOB ID : db94d5960ce564c50904d666a7e259148c27e88f JOB LOG : ~/avocado/job-results/job-2019-06-25T10.52-db94d59/job.log (1/1) tests/avocado/load_bflt.py:LoadBFLT.test_stm32: PASS (0.15 s) RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0 JOB TIME : 0.54 s Reviewed-by: Willian Rampazzo Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211105143416.148332-6-f4bug@amsat.org> --- tests/avocado/load_bflt.py | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/avocado/load_bflt.py diff --git a/tests/avocado/load_bflt.py b/tests/avocado/load_bflt.py new file mode 100644 index 0000000000..bb50cec1ee --- /dev/null +++ b/tests/avocado/load_bflt.py @@ -0,0 +1,54 @@ +# Test the bFLT loader format +# +# Copyright (C) 2019 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import bz2 +import subprocess + +from avocado import skipUnless +from avocado_qemu import QemuUserTest +from avocado_qemu import has_cmd + + +class LoadBFLT(QemuUserTest): + + def extract_cpio(self, cpio_path): + """ + Extracts a cpio archive into the test workdir + + :param cpio_path: path to the cpio archive + """ + cwd = os.getcwd() + os.chdir(self.workdir) + with bz2.open(cpio_path, 'rb') as archive_cpio: + subprocess.run(['cpio', '-i'], input=archive_cpio.read(), + stderr=subprocess.DEVNULL) + os.chdir(cwd) + + @skipUnless(*has_cmd('cpio')) + @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_stm32(self): + """ + :avocado: tags=arch:arm + :avocado: tags=linux_user + :avocado: tags=quick + """ + # See https://elinux.org/STM32#User_Space + rootfs_url = ('https://elinux.org/images/5/51/' + 'Stm32_mini_rootfs.cpio.bz2') + rootfs_hash = '9f065e6ba40cce7411ba757f924f30fcc57951e6' + rootfs_path_bz2 = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) + busybox_path = os.path.join(self.workdir, "/bin/busybox") + + self.extract_cpio(rootfs_path_bz2) + + res = self.run(busybox_path) + ver = 'BusyBox v1.24.0.git (2015-02-03 22:17:13 CET) multi-call binary.' + self.assertIn(ver, res.stdout_text) + + res = self.run(busybox_path, ['uname', '-a']) + unm = 'armv7l GNU/Linux' + self.assertIn(unm, res.stdout_text) From 2283b627bc6ba2bc7f120b4e7af0e43503282bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Sep 2021 18:14:33 +0200 Subject: [PATCH 1247/1334] tests/avocado: Rename avocado_qemu.Test -> QemuSystemTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To run user-mode emulation tests, we introduced the avocado_qemu.QemuUserTest which inherits from avocado_qemu.QemuBaseTest. System-mode emulation tests are based on the avocado_qemu.Test class, which also inherits avocado_qemu.QemuBaseTest. To avoid confusion, rename it as avocado_qemu.QemuSystemTest. Suggested-by: Wainer dos Santos Moschetta Reviewed-by: Willian Rampazzo Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211105143416.148332-7-f4bug@amsat.org> --- docs/devel/testing.rst | 8 ++++---- tests/avocado/avocado_qemu/__init__.py | 21 +++++++++------------ tests/avocado/boot_linux_console.py | 4 ++-- tests/avocado/cpu_queries.py | 4 ++-- tests/avocado/empty_cpu_model.py | 4 ++-- tests/avocado/info_usernet.py | 4 ++-- tests/avocado/linux_initrd.py | 4 ++-- tests/avocado/linux_ssh_mips_malta.py | 5 +++-- tests/avocado/machine_arm_canona1100.py | 4 ++-- tests/avocado/machine_arm_integratorcp.py | 4 ++-- tests/avocado/machine_arm_n8x0.py | 4 ++-- tests/avocado/machine_avr6.py | 4 ++-- tests/avocado/machine_m68k_nextcube.py | 4 ++-- tests/avocado/machine_microblaze.py | 4 ++-- tests/avocado/machine_mips_fuloong2e.py | 4 ++-- tests/avocado/machine_mips_loongson3v.py | 4 ++-- tests/avocado/machine_mips_malta.py | 4 ++-- tests/avocado/machine_rx_gdbsim.py | 4 ++-- tests/avocado/machine_s390_ccw_virtio.py | 4 ++-- tests/avocado/machine_sparc_leon3.py | 4 ++-- tests/avocado/migration.py | 4 ++-- tests/avocado/multiprocess.py | 4 ++-- tests/avocado/pc_cpu_hotplug_props.py | 4 ++-- tests/avocado/ppc_405.py | 4 ++-- tests/avocado/ppc_bamboo.py | 4 ++-- tests/avocado/ppc_mpc8544ds.py | 4 ++-- tests/avocado/ppc_prep_40p.py | 4 ++-- tests/avocado/ppc_pseries.py | 4 ++-- tests/avocado/ppc_virtex_ml507.py | 4 ++-- tests/avocado/version.py | 4 ++-- tests/avocado/virtio-gpu.py | 4 ++-- tests/avocado/virtio_check_params.py | 4 ++-- tests/avocado/virtio_version.py | 4 ++-- tests/avocado/vnc.py | 4 ++-- tests/avocado/x86_cpu_model_versions.py | 4 ++-- 35 files changed, 80 insertions(+), 82 deletions(-) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index dc5dbd057d..60c59023e5 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -811,10 +811,10 @@ class. Here's a simple usage example: .. code:: - from avocado_qemu import Test + from avocado_qemu import QemuSystemTest - class Version(Test): + class Version(QemuSystemTest): """ :avocado: tags=quick """ @@ -859,10 +859,10 @@ and hypothetical example follows: .. code:: - from avocado_qemu import Test + from avocado_qemu import QemuSystemTest - class MultipleMachines(Test): + class MultipleMachines(QemuSystemTest): def test_multiple_machines(self): first_machine = self.get_vm() second_machine = self.get_vm() diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index 1efc22dabf..75063c0c30 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -176,7 +176,7 @@ def interrupt_interactive_console_until_pattern(test, success_message, :param test: an Avocado test containing a VM that will have its console read and probed for a success or failure message - :type test: :class:`avocado_qemu.Test` + :type test: :class:`avocado_qemu.QemuSystemTest` :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails :param interrupt_string: a string to send to the console before trying @@ -192,7 +192,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None, :param test: an Avocado test containing a VM that will have its console read and probed for a success or failure message - :type test: :class:`avocado_qemu.Test` + :type test: :class:`avocado_qemu.QemuSystemTest` :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails """ @@ -204,7 +204,7 @@ def exec_command(test, command): the content. :param test: an Avocado test containing a VM. - :type test: :class:`avocado_qemu.Test` + :type test: :class:`avocado_qemu.QemuSystemTest` :param command: the command to send :type command: str """ @@ -219,7 +219,7 @@ def exec_command_and_wait_for_pattern(test, command, :param test: an Avocado test containing a VM that will have its console read and probed for a success or failure message - :type test: :class:`avocado_qemu.Test` + :type test: :class:`avocado_qemu.QemuSystemTest` :param command: the command to send :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails @@ -262,11 +262,8 @@ class QemuBaseTest(avocado.Test): cancel_on_missing=cancel_on_missing) -class Test(QemuBaseTest): - """Facilitates system emulation tests. - - TODO: Rename this class as `QemuSystemTest`. - """ +class QemuSystemTest(QemuBaseTest): + """Facilitates system emulation tests.""" def setUp(self): self._vms = {} @@ -508,11 +505,11 @@ class LinuxDistro: return self._info.get('kernel_params', None) -class LinuxTest(LinuxSSHMixIn, Test): +class LinuxTest(LinuxSSHMixIn, QemuSystemTest): """Facilitates having a cloud-image Linux based available. - For tests that indend to interact with guests, this is a better choice - to start with than the more vanilla `Test` class. + For tests that indent to interact with guests, this is a better choice + to start with than the more vanilla `QemuSystemTest` class. """ timeout = 900 diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 06fc967f6c..4ed01ed789 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -15,7 +15,7 @@ import shutil from avocado import skip from avocado import skipUnless -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import exec_command from avocado_qemu import exec_command_and_wait_for_pattern from avocado_qemu import interrupt_interactive_console_until_pattern @@ -46,7 +46,7 @@ def image_pow2ceil_expand(path): with open(path, 'ab+') as fd: fd.truncate(size_aligned) -class LinuxKernelTest(Test): +class LinuxKernelTest(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' def wait_for_console_pattern(self, success_message, vm=None): diff --git a/tests/avocado/cpu_queries.py b/tests/avocado/cpu_queries.py index cc9e380cc7..cf69f69b11 100644 --- a/tests/avocado/cpu_queries.py +++ b/tests/avocado/cpu_queries.py @@ -8,9 +8,9 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest -class QueryCPUModelExpansion(Test): +class QueryCPUModelExpansion(QemuSystemTest): """ Run query-cpu-model-expansion for each CPU model, and validate results """ diff --git a/tests/avocado/empty_cpu_model.py b/tests/avocado/empty_cpu_model.py index a1e59e45e4..22f504418d 100644 --- a/tests/avocado/empty_cpu_model.py +++ b/tests/avocado/empty_cpu_model.py @@ -7,9 +7,9 @@ # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest -class EmptyCPUModel(Test): +class EmptyCPUModel(QemuSystemTest): def test(self): self.vm.add_args('-S', '-display', 'none', '-machine', 'none', '-cpu', '') self.vm.set_qmp_monitor(enabled=False) diff --git a/tests/avocado/info_usernet.py b/tests/avocado/info_usernet.py index 9c1fd903a0..dc01f74150 100644 --- a/tests/avocado/info_usernet.py +++ b/tests/avocado/info_usernet.py @@ -8,12 +8,12 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from qemu.utils import get_info_usernet_hostfwd_port -class InfoUsernet(Test): +class InfoUsernet(QemuSystemTest): def test_hostfwd(self): self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22') diff --git a/tests/avocado/linux_initrd.py b/tests/avocado/linux_initrd.py index 9b4880cd8c..ba02e5a563 100644 --- a/tests/avocado/linux_initrd.py +++ b/tests/avocado/linux_initrd.py @@ -12,11 +12,11 @@ import os import logging import tempfile -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado import skipIf -class LinuxInitrd(Test): +class LinuxInitrd(QemuSystemTest): """ Checks QEMU evaluates correctly the initrd file passed as -initrd option. diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py index 4de1947418..c0f0be5ade 100644 --- a/tests/avocado/linux_ssh_mips_malta.py +++ b/tests/avocado/linux_ssh_mips_malta.py @@ -12,7 +12,8 @@ import logging import time from avocado import skipUnless -from avocado_qemu import Test, LinuxSSHMixIn +from avocado_qemu import LinuxSSHMixIn +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado.utils import process from avocado.utils import archive @@ -21,7 +22,7 @@ from avocado.utils import ssh @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') @skipUnless(ssh.SSH_CLIENT_BINARY, 'No SSH client available') -class LinuxSSH(Test, LinuxSSHMixIn): +class LinuxSSH(QemuSystemTest, LinuxSSHMixIn): timeout = 150 # Not for 'configure --enable-debug --enable-debug-tcg' diff --git a/tests/avocado/machine_arm_canona1100.py b/tests/avocado/machine_arm_canona1100.py index 0e5c43dbcf..182a0b0513 100644 --- a/tests/avocado/machine_arm_canona1100.py +++ b/tests/avocado/machine_arm_canona1100.py @@ -8,11 +8,11 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado.utils import archive -class CanonA1100Machine(Test): +class CanonA1100Machine(QemuSystemTest): """Boots the barebox firmware and checks that the console is operational""" timeout = 90 diff --git a/tests/avocado/machine_arm_integratorcp.py b/tests/avocado/machine_arm_integratorcp.py index 49c8ebff78..1ffe1073ef 100644 --- a/tests/avocado/machine_arm_integratorcp.py +++ b/tests/avocado/machine_arm_integratorcp.py @@ -12,7 +12,7 @@ import os import logging from avocado import skipUnless -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern @@ -29,7 +29,7 @@ except ImportError: CV2_AVAILABLE = False -class IntegratorMachine(Test): +class IntegratorMachine(QemuSystemTest): timeout = 90 diff --git a/tests/avocado/machine_arm_n8x0.py b/tests/avocado/machine_arm_n8x0.py index e5741f2d8d..12e9a6803b 100644 --- a/tests/avocado/machine_arm_n8x0.py +++ b/tests/avocado/machine_arm_n8x0.py @@ -11,10 +11,10 @@ import os from avocado import skipUnless -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern -class N8x0Machine(Test): +class N8x0Machine(QemuSystemTest): """Boots the Linux kernel and checks that the console is operational""" timeout = 90 diff --git a/tests/avocado/machine_avr6.py b/tests/avocado/machine_avr6.py index 6bab31342a..5485db79c6 100644 --- a/tests/avocado/machine_avr6.py +++ b/tests/avocado/machine_avr6.py @@ -19,9 +19,9 @@ import time -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest -class AVR6Machine(Test): +class AVR6Machine(QemuSystemTest): timeout = 5 def test_freertos(self): diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py index 09e2745cc5..6790e7d9cd 100644 --- a/tests/avocado/machine_m68k_nextcube.py +++ b/tests/avocado/machine_m68k_nextcube.py @@ -8,7 +8,7 @@ import os import time -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado import skipUnless from tesseract_utils import tesseract_available, tesseract_ocr @@ -20,7 +20,7 @@ except ImportError: PIL_AVAILABLE = False -class NextCubeMachine(Test): +class NextCubeMachine(QemuSystemTest): """ :avocado: tags=arch:m68k :avocado: tags=machine:next-cube diff --git a/tests/avocado/machine_microblaze.py b/tests/avocado/machine_microblaze.py index 7f6d18495d..4928920f96 100644 --- a/tests/avocado/machine_microblaze.py +++ b/tests/avocado/machine_microblaze.py @@ -5,11 +5,11 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado.utils import archive -class MicroblazeMachine(Test): +class MicroblazeMachine(QemuSystemTest): timeout = 90 diff --git a/tests/avocado/machine_mips_fuloong2e.py b/tests/avocado/machine_mips_fuloong2e.py index 0ac285e2af..89291f47b2 100644 --- a/tests/avocado/machine_mips_fuloong2e.py +++ b/tests/avocado/machine_mips_fuloong2e.py @@ -10,10 +10,10 @@ import os from avocado import skipUnless -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern -class MipsFuloong2e(Test): +class MipsFuloong2e(QemuSystemTest): timeout = 60 diff --git a/tests/avocado/machine_mips_loongson3v.py b/tests/avocado/machine_mips_loongson3v.py index 85b131a40f..5194cf18c9 100644 --- a/tests/avocado/machine_mips_loongson3v.py +++ b/tests/avocado/machine_mips_loongson3v.py @@ -11,10 +11,10 @@ import os import time from avocado import skipUnless -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern -class MipsLoongson3v(Test): +class MipsLoongson3v(QemuSystemTest): timeout = 60 @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') diff --git a/tests/avocado/machine_mips_malta.py b/tests/avocado/machine_mips_malta.py index b67d8cb141..f1895d59f3 100644 --- a/tests/avocado/machine_mips_malta.py +++ b/tests/avocado/machine_mips_malta.py @@ -12,7 +12,7 @@ import gzip import logging from avocado import skipUnless -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado.utils import archive from avocado import skipIf @@ -33,7 +33,7 @@ except ImportError: @skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') @skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') -class MaltaMachineFramebuffer(Test): +class MaltaMachineFramebuffer(QemuSystemTest): timeout = 30 diff --git a/tests/avocado/machine_rx_gdbsim.py b/tests/avocado/machine_rx_gdbsim.py index 32b737b6d8..6cd8704b01 100644 --- a/tests/avocado/machine_rx_gdbsim.py +++ b/tests/avocado/machine_rx_gdbsim.py @@ -11,13 +11,13 @@ import os from avocado import skipIf -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import exec_command_and_wait_for_pattern from avocado_qemu import wait_for_console_pattern from avocado.utils import archive -class RxGdbSimMachine(Test): +class RxGdbSimMachine(QemuSystemTest): timeout = 30 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' diff --git a/tests/avocado/machine_s390_ccw_virtio.py b/tests/avocado/machine_s390_ccw_virtio.py index 4028c99afc..bd03d7160b 100644 --- a/tests/avocado/machine_s390_ccw_virtio.py +++ b/tests/avocado/machine_s390_ccw_virtio.py @@ -13,12 +13,12 @@ import os import tempfile from avocado import skipIf -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import exec_command_and_wait_for_pattern from avocado_qemu import wait_for_console_pattern from avocado.utils import archive -class S390CCWVirtioMachine(Test): +class S390CCWVirtioMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' timeout = 120 diff --git a/tests/avocado/machine_sparc_leon3.py b/tests/avocado/machine_sparc_leon3.py index 2405cd7a0d..e61b223185 100644 --- a/tests/avocado/machine_sparc_leon3.py +++ b/tests/avocado/machine_sparc_leon3.py @@ -5,12 +5,12 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado import skip -class Leon3Machine(Test): +class Leon3Machine(QemuSystemTest): timeout = 60 diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py index 792639cb69..584d6ef53f 100644 --- a/tests/avocado/migration.py +++ b/tests/avocado/migration.py @@ -11,7 +11,7 @@ import tempfile -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado import skipUnless from avocado.utils import network @@ -19,7 +19,7 @@ from avocado.utils import wait from avocado.utils.path import find_command -class Migration(Test): +class Migration(QemuSystemTest): """ :avocado: tags=migration """ diff --git a/tests/avocado/multiprocess.py b/tests/avocado/multiprocess.py index 96627f022a..80a3b8f442 100644 --- a/tests/avocado/multiprocess.py +++ b/tests/avocado/multiprocess.py @@ -7,12 +7,12 @@ import os import socket -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado_qemu import exec_command from avocado_qemu import exec_command_and_wait_for_pattern -class Multiprocess(Test): +class Multiprocess(QemuSystemTest): """ :avocado: tags=multiprocess """ diff --git a/tests/avocado/pc_cpu_hotplug_props.py b/tests/avocado/pc_cpu_hotplug_props.py index 2e86d5017a..52b878188e 100644 --- a/tests/avocado/pc_cpu_hotplug_props.py +++ b/tests/avocado/pc_cpu_hotplug_props.py @@ -20,9 +20,9 @@ # License along with this library; if not, see . # -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest -class OmittedCPUProps(Test): +class OmittedCPUProps(QemuSystemTest): """ :avocado: tags=arch:x86_64 :avocado: tags=cpu:qemu64 diff --git a/tests/avocado/ppc_405.py b/tests/avocado/ppc_405.py index c534d5d32f..a47f89b934 100644 --- a/tests/avocado/ppc_405.py +++ b/tests/avocado/ppc_405.py @@ -6,11 +6,11 @@ # later. See the COPYING file in the top-level directory. from avocado.utils import archive -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado_qemu import exec_command_and_wait_for_pattern -class Ppc405Machine(Test): +class Ppc405Machine(QemuSystemTest): timeout = 90 diff --git a/tests/avocado/ppc_bamboo.py b/tests/avocado/ppc_bamboo.py index dd33bf66f3..40629e3478 100644 --- a/tests/avocado/ppc_bamboo.py +++ b/tests/avocado/ppc_bamboo.py @@ -6,11 +6,11 @@ # later. See the COPYING file in the top-level directory. from avocado.utils import archive -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado_qemu import exec_command_and_wait_for_pattern -class BambooMachine(Test): +class BambooMachine(QemuSystemTest): timeout = 90 diff --git a/tests/avocado/ppc_mpc8544ds.py b/tests/avocado/ppc_mpc8544ds.py index ce840600c1..886f967b15 100644 --- a/tests/avocado/ppc_mpc8544ds.py +++ b/tests/avocado/ppc_mpc8544ds.py @@ -6,10 +6,10 @@ # later. See the COPYING file in the top-level directory. from avocado.utils import archive -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern -class Mpc8544dsMachine(Test): +class Mpc8544dsMachine(QemuSystemTest): timeout = 90 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' diff --git a/tests/avocado/ppc_prep_40p.py b/tests/avocado/ppc_prep_40p.py index 5e61e686bd..4bd956584d 100644 --- a/tests/avocado/ppc_prep_40p.py +++ b/tests/avocado/ppc_prep_40p.py @@ -8,11 +8,11 @@ import os from avocado import skipUnless -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern -class IbmPrep40pMachine(Test): +class IbmPrep40pMachine(QemuSystemTest): timeout = 60 diff --git a/tests/avocado/ppc_pseries.py b/tests/avocado/ppc_pseries.py index f14a884ee1..d8b04dc3ea 100644 --- a/tests/avocado/ppc_pseries.py +++ b/tests/avocado/ppc_pseries.py @@ -6,10 +6,10 @@ # later. See the COPYING file in the top-level directory. from avocado.utils import archive -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern -class pseriesMachine(Test): +class pseriesMachine(QemuSystemTest): timeout = 90 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' diff --git a/tests/avocado/ppc_virtex_ml507.py b/tests/avocado/ppc_virtex_ml507.py index 27f7bf2d49..a6912ee579 100644 --- a/tests/avocado/ppc_virtex_ml507.py +++ b/tests/avocado/ppc_virtex_ml507.py @@ -6,10 +6,10 @@ # later. See the COPYING file in the top-level directory. from avocado.utils import archive -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern -class VirtexMl507Machine(Test): +class VirtexMl507Machine(QemuSystemTest): timeout = 90 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' diff --git a/tests/avocado/version.py b/tests/avocado/version.py index 79b923d4fc..ded7f039c1 100644 --- a/tests/avocado/version.py +++ b/tests/avocado/version.py @@ -9,10 +9,10 @@ # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest -class Version(Test): +class Version(QemuSystemTest): """ :avocado: tags=quick """ diff --git a/tests/avocado/virtio-gpu.py b/tests/avocado/virtio-gpu.py index 4acc1e6d5f..2a249a3a2c 100644 --- a/tests/avocado/virtio-gpu.py +++ b/tests/avocado/virtio-gpu.py @@ -4,8 +4,8 @@ # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test from avocado_qemu import BUILD_DIR +from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado_qemu import exec_command_and_wait_for_pattern from avocado_qemu import is_readable_executable_file @@ -27,7 +27,7 @@ def pick_default_vug_bin(): return bld_dir_path -class VirtioGPUx86(Test): +class VirtioGPUx86(QemuSystemTest): """ :avocado: tags=virtio-gpu :avocado: tags=arch:x86_64 diff --git a/tests/avocado/virtio_check_params.py b/tests/avocado/virtio_check_params.py index 87e6c839d1..e869690473 100644 --- a/tests/avocado/virtio_check_params.py +++ b/tests/avocado/virtio_check_params.py @@ -24,7 +24,7 @@ import logging sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu.machine import QEMUMachine -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest from avocado import skip #list of machine types and virtqueue properties to test @@ -41,7 +41,7 @@ VM_DEV_PARAMS = {'virtio-scsi-pci': ['-device', 'virtio-scsi-pci,id=scsi0'], 'driver=null-co,id=drive0,if=none']} -class VirtioMaxSegSettingsCheck(Test): +class VirtioMaxSegSettingsCheck(QemuSystemTest): @staticmethod def make_pattern(props): pattern_items = ['{0} = \w+'.format(prop) for prop in props] diff --git a/tests/avocado/virtio_version.py b/tests/avocado/virtio_version.py index 33593c29dd..208910bb84 100644 --- a/tests/avocado/virtio_version.py +++ b/tests/avocado/virtio_version.py @@ -13,7 +13,7 @@ import os sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu.machine import QEMUMachine -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest # Virtio Device IDs: VIRTIO_NET = 1 @@ -55,7 +55,7 @@ def get_pci_interfaces(vm, devtype): interfaces = ('pci-express-device', 'conventional-pci-device') return [i for i in interfaces if devtype_implements(vm, devtype, i)] -class VirtioVersionCheck(Test): +class VirtioVersionCheck(QemuSystemTest): """ Check if virtio-version-specific device types result in the same device tree created by `disable-modern` and diff --git a/tests/avocado/vnc.py b/tests/avocado/vnc.py index f301fbb4f5..096432988f 100644 --- a/tests/avocado/vnc.py +++ b/tests/avocado/vnc.py @@ -8,10 +8,10 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import Test +from avocado_qemu import QemuSystemTest -class Vnc(Test): +class Vnc(QemuSystemTest): """ :avocado: tags=vnc,quick """ diff --git a/tests/avocado/x86_cpu_model_versions.py b/tests/avocado/x86_cpu_model_versions.py index 0e9feda62d..a6edf74c1c 100644 --- a/tests/avocado/x86_cpu_model_versions.py +++ b/tests/avocado/x86_cpu_model_versions.py @@ -24,7 +24,7 @@ import avocado_qemu import re -class X86CPUModelAliases(avocado_qemu.Test): +class X86CPUModelAliases(avocado_qemu.QemuSystemTest): """ Validation of PC CPU model versions and CPU model aliases @@ -239,7 +239,7 @@ class X86CPUModelAliases(avocado_qemu.Test): self.validate_aliases(cpus) -class CascadelakeArchCapabilities(avocado_qemu.Test): +class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): """ Validation of Cascadelake arch-capabilities From b94d00898a9ee708b4d4b79432c2272905a56b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 6 Nov 2021 10:07:18 +0100 Subject: [PATCH 1248/1334] tests/avocado: Remove p7zip binary availability check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single use of the 7z binary has been removed in commit a30e114f3 ("tests/acceptance: remove Armbian 19.11.3 test for orangepi-pc"), we don't need to check for this binary availability anymore. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Joaquin de Andres Reviewed-by: Willian Rampazzo Message-Id: <20211106091059.465109-1-philmd@redhat.com> --- tests/avocado/boot_linux_console.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 4ed01ed789..9c618d4809 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -22,13 +22,6 @@ from avocado_qemu import interrupt_interactive_console_until_pattern from avocado_qemu import wait_for_console_pattern from avocado.utils import process from avocado.utils import archive -from avocado.utils.path import find_command, CmdNotFoundError - -P7ZIP_AVAILABLE = True -try: - find_command('7z') -except CmdNotFoundError: - P7ZIP_AVAILABLE = False """ Round up to next power of 2 From eb63efd9f63fabc2ce7ee070f1d8e75585d26543 Mon Sep 17 00:00:00 2001 From: Fernando Eckhardt Valle Date: Fri, 29 Oct 2021 17:23:51 -0300 Subject: [PATCH 1249/1334] target/ppc: introduce do_ea_calc The do_ea_calc function will calculate the effective address(EA) according to PowerIsa 3.1. With that, it was replaced part of do_ldst() that calculates the EA by this new function. Reviewed-by: Richard Henderson Signed-off-by: Fernando Eckhardt Valle (pherde) Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-2-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/translate.c | 14 ++++++++++++++ target/ppc/translate/fixedpoint-impl.c.inc | 10 +--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 518337bcb7..b3264aab58 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3197,6 +3197,20 @@ static inline void gen_align_no_le(DisasContext *ctx) (ctx->opcode & 0x03FF0000) | POWERPC_EXCP_ALIGN_LE); } +static TCGv do_ea_calc(DisasContext *ctx, int ra, TCGv displ) +{ + TCGv ea = tcg_temp_new(); + if (ra) { + tcg_gen_add_tl(ea, cpu_gpr[ra], displ); + } else { + tcg_gen_mov_tl(ea, displ); + } + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(ea, ea); + } + return ea; +} + /*** Integer load ***/ #define DEF_MEMOP(op) ((op) | ctx->default_tcg_memop_mask) #define BSWAP_MEMOP(op) ((op) | (ctx->default_tcg_memop_mask ^ MO_BSWAP)) diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 2e2518ee15..caef5d89cd 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -51,15 +51,7 @@ static bool do_ldst(DisasContext *ctx, int rt, int ra, TCGv displ, bool update, } gen_set_access_type(ctx, ACCESS_INT); - ea = tcg_temp_new(); - if (ra) { - tcg_gen_add_tl(ea, cpu_gpr[ra], displ); - } else { - tcg_gen_mov_tl(ea, displ); - } - if (NARROW_MODE(ctx)) { - tcg_gen_ext32u_tl(ea, ea); - } + ea = do_ea_calc(ctx, ra, displ); mop ^= ctx->default_tcg_memop_mask; if (store) { tcg_gen_qemu_st_tl(cpu_gpr[rt], ea, ctx->mem_idx, mop); From 725b2d4dac67c96c044ed53df2c5e7128e9aa013 Mon Sep 17 00:00:00 2001 From: Fernando Eckhardt Valle Date: Fri, 29 Oct 2021 17:23:52 -0300 Subject: [PATCH 1250/1334] target/ppc: move resolve_PLS_D to translate.c Move resolve_PLS_D from fixedpoint-impl.c.inc to translate.c because this way the function can be used not only by fixed point instructions. Reviewed-by: Richard Henderson Signed-off-by: Fernando Eckhardt Valle Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-3-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/translate.c | 19 +++++++++++++++++++ target/ppc/translate/fixedpoint-impl.c.inc | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index b3264aab58..3f50f44c19 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7502,6 +7502,25 @@ static int times_4(DisasContext *ctx, int x) #include "decode-insn64.c.inc" #include "power8-pmu-regs.c.inc" +/* + * Incorporate CIA into the constant when R=1. + * Validate that when R=1, RA=0. + */ +static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) +{ + d->rt = a->rt; + d->ra = a->ra; + d->si = a->si; + if (a->r) { + if (unlikely(a->ra != 0)) { + gen_invalid(ctx); + return false; + } + d->si += ctx->cia; + } + return true; +} + #include "translate/fixedpoint-impl.c.inc" #include "translate/fp-impl.c.inc" diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index caef5d89cd..812b7ddd0a 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -17,25 +17,6 @@ * License along with this library; if not, see . */ -/* - * Incorporate CIA into the constant when R=1. - * Validate that when R=1, RA=0. - */ -static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) -{ - d->rt = a->rt; - d->ra = a->ra; - d->si = a->si; - if (a->r) { - if (unlikely(a->ra != 0)) { - gen_invalid(ctx); - return false; - } - d->si += ctx->cia; - } - return true; -} - /* * Fixed-Point Load/Store Instructions */ From fbd2e60ef1bdd626c1430a6c1c653e63fed56040 Mon Sep 17 00:00:00 2001 From: Fernando Eckhardt Valle Date: Fri, 29 Oct 2021 17:23:53 -0300 Subject: [PATCH 1251/1334] target/ppc: Move load and store floating point instructions to decodetree Move load floating point instructions (lfs, lfsu, lfsx, lfsux, lfd, lfdu, lfdx, lfdux) and store floating point instructions(stfs, stfsu, stfsx, stfsux, stfd, stfdu, stfdx, stfdux) from legacy system to decodetree. Reviewed-by: Richard Henderson Signed-off-by: Fernando Eckhardt Valle Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-4-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 24 +++ target/ppc/translate/fp-impl.c.inc | 247 +++++++++-------------------- target/ppc/translate/fp-ops.c.inc | 29 ---- 3 files changed, 95 insertions(+), 205 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 9fd8d6b817..19ef9bff67 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -114,6 +114,30 @@ ADDPCIS 010011 ..... ..... .......... 00010 . @DX CFUGED 011111 ..... ..... ..... 0011011100 - @X +### Float-Point Load Instructions + +LFS 110000 ..... ..... ................ @D +LFSU 110001 ..... ..... ................ @D +LFSX 011111 ..... ..... ..... 1000010111 - @X +LFSUX 011111 ..... ..... ..... 1000110111 - @X + +LFD 110010 ..... ..... ................ @D +LFDU 110011 ..... ..... ................ @D +LFDX 011111 ..... ..... ..... 1001010111 - @X +LFDUX 011111 ..... ..... ..... 1001110111 - @X + +### Float-Point Store Instructions + +STFS 110100 ..... ...... ............... @D +STFSU 110101 ..... ...... ............... @D +STFSX 011111 ..... ...... .... 1010010111 - @X +STFSUX 011111 ..... ...... .... 1010110111 - @X + +STFD 110110 ..... ...... ............... @D +STFDU 110111 ..... ...... ............... @D +STFDX 011111 ..... ...... .... 1011010111 - @X +STFDUX 011111 ..... ...... .... 1011110111 - @X + ### Move To/From System Register Instructions SETBC 011111 ..... ..... ----- 0110000000 - @X_bi diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index 9f7868ee28..57a799db1c 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -854,99 +854,6 @@ static void gen_mtfsfi(DisasContext *ctx) gen_helper_float_check_status(cpu_env); } -/*** Floating-point load ***/ -#define GEN_LDF(name, ldop, opc, type) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_FLOAT); \ - EA = tcg_temp_new(); \ - t0 = tcg_temp_new_i64(); \ - gen_addr_imm_index(ctx, EA, 0); \ - gen_qemu_##ldop(ctx, t0, EA); \ - set_fpr(rD(ctx->opcode), t0); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ -} - -#define GEN_LDUF(name, ldop, opc, type) \ -static void glue(gen_, name##u)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - if (unlikely(rA(ctx->opcode) == 0)) { \ - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_FLOAT); \ - EA = tcg_temp_new(); \ - t0 = tcg_temp_new_i64(); \ - gen_addr_imm_index(ctx, EA, 0); \ - gen_qemu_##ldop(ctx, t0, EA); \ - set_fpr(rD(ctx->opcode), t0); \ - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ -} - -#define GEN_LDUXF(name, ldop, opc, type) \ -static void glue(gen_, name##ux)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - if (unlikely(rA(ctx->opcode) == 0)) { \ - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_FLOAT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - gen_qemu_##ldop(ctx, t0, EA); \ - set_fpr(rD(ctx->opcode), t0); \ - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ -} - -#define GEN_LDXF(name, ldop, opc2, opc3, type) \ -static void glue(gen_, name##x)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_FLOAT); \ - EA = tcg_temp_new(); \ - t0 = tcg_temp_new_i64(); \ - gen_addr_reg_index(ctx, EA); \ - gen_qemu_##ldop(ctx, t0, EA); \ - set_fpr(rD(ctx->opcode), t0); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ -} - -#define GEN_LDFS(name, ldop, op, type) \ -GEN_LDF(name, ldop, op | 0x20, type); \ -GEN_LDUF(name, ldop, op | 0x21, type); \ -GEN_LDUXF(name, ldop, op | 0x01, type); \ -GEN_LDXF(name, ldop, 0x17, op | 0x00, type) - static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr) { TCGv_i32 tmp = tcg_temp_new_i32(); @@ -955,11 +862,6 @@ static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr) tcg_temp_free_i32(tmp); } - /* lfd lfdu lfdux lfdx */ -GEN_LDFS(lfd, ld64_i64, 0x12, PPC_FLOAT); - /* lfs lfsu lfsux lfsx */ -GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT); - /* lfdepx (external PID lfdx) */ static void gen_lfdepx(DisasContext *ctx) { @@ -1089,73 +991,6 @@ static void gen_lfiwzx(DisasContext *ctx) tcg_temp_free(EA); tcg_temp_free_i64(t0); } -/*** Floating-point store ***/ -#define GEN_STF(name, stop, opc, type) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_FLOAT); \ - EA = tcg_temp_new(); \ - t0 = tcg_temp_new_i64(); \ - gen_addr_imm_index(ctx, EA, 0); \ - get_fpr(t0, rS(ctx->opcode)); \ - gen_qemu_##stop(ctx, t0, EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ -} - -#define GEN_STUF(name, stop, opc, type) \ -static void glue(gen_, name##u)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - if (unlikely(rA(ctx->opcode) == 0)) { \ - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_FLOAT); \ - EA = tcg_temp_new(); \ - t0 = tcg_temp_new_i64(); \ - gen_addr_imm_index(ctx, EA, 0); \ - get_fpr(t0, rS(ctx->opcode)); \ - gen_qemu_##stop(ctx, t0, EA); \ - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ -} - -#define GEN_STUXF(name, stop, opc, type) \ -static void glue(gen_, name##ux)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - if (unlikely(rA(ctx->opcode) == 0)) { \ - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_FLOAT); \ - EA = tcg_temp_new(); \ - t0 = tcg_temp_new_i64(); \ - gen_addr_reg_index(ctx, EA); \ - get_fpr(t0, rS(ctx->opcode)); \ - gen_qemu_##stop(ctx, t0, EA); \ - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ -} #define GEN_STXF(name, stop, opc2, opc3, type) \ static void glue(gen_, name##x)(DisasContext *ctx) \ @@ -1176,12 +1011,6 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ tcg_temp_free_i64(t0); \ } -#define GEN_STFS(name, stop, op, type) \ -GEN_STF(name, stop, op | 0x20, type); \ -GEN_STUF(name, stop, op | 0x21, type); \ -GEN_STUXF(name, stop, op | 0x01, type); \ -GEN_STXF(name, stop, 0x17, op | 0x00, type) - static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr) { TCGv_i32 tmp = tcg_temp_new_i32(); @@ -1190,11 +1019,6 @@ static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr) tcg_temp_free_i32(tmp); } -/* stfd stfdu stfdux stfdx */ -GEN_STFS(stfd, st64_i64, 0x16, PPC_FLOAT); -/* stfs stfsu stfsux stfsx */ -GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT); - /* stfdepx (external PID lfdx) */ static void gen_stfdepx(DisasContext *ctx) { @@ -1473,6 +1297,77 @@ static void gen_stfqx(DisasContext *ctx) tcg_temp_free_i64(t1); } +/* Floating-point Load/Store Instructions */ +static bool do_lsfpsd(DisasContext *ctx, int rt, int ra, TCGv displ, + bool update, bool store, bool single) +{ + TCGv ea; + TCGv_i64 t0; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + if (update && ra == 0) { + gen_invalid(ctx); + return true; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + t0 = tcg_temp_new_i64(); + ea = do_ea_calc(ctx, ra, displ); + if (store) { + get_fpr(t0, rt); + if (single) { + gen_qemu_st32fs(ctx, t0, ea); + } else { + gen_qemu_st64_i64(ctx, t0, ea); + } + } else { + if (single) { + gen_qemu_ld32fs(ctx, t0, ea); + } else { + gen_qemu_ld64_i64(ctx, t0, ea); + } + set_fpr(rt, t0); + } + if (update) { + tcg_gen_mov_tl(cpu_gpr[rt], ea); + } + tcg_temp_free_i64(t0); + tcg_temp_free(ea); + return true; +} + +static bool do_lsfp_D(DisasContext *ctx, arg_D *a, bool update, bool store, + bool single) +{ + return do_lsfpsd(ctx, a->rt, a->ra, tcg_constant_tl(a->si), update, store, + single); +} + +static bool do_lsfp_X(DisasContext *ctx, arg_X *a, bool update, + bool store, bool single) +{ + return do_lsfpsd(ctx, a->rt, a->ra, cpu_gpr[a->rb], update, store, single); +} + +TRANS(LFS, do_lsfp_D, false, false, true) +TRANS(LFSU, do_lsfp_D, true, false, true) +TRANS(LFSX, do_lsfp_X, false, false, true) +TRANS(LFSUX, do_lsfp_X, true, false, true) + +TRANS(LFD, do_lsfp_D, false, false, false) +TRANS(LFDU, do_lsfp_D, true, false, false) +TRANS(LFDX, do_lsfp_X, false, false, false) +TRANS(LFDUX, do_lsfp_X, true, false, false) + +TRANS(STFS, do_lsfp_D, false, true, true) +TRANS(STFSU, do_lsfp_D, true, true, true) +TRANS(STFSX, do_lsfp_X, false, true, true) +TRANS(STFSUX, do_lsfp_X, true, true, true) + +TRANS(STFD, do_lsfp_D, false, true, false) +TRANS(STFDU, do_lsfp_D, true, true, false) +TRANS(STFDX, do_lsfp_X, false, true, false) +TRANS(STFDUX, do_lsfp_X, true, true, false) + #undef _GEN_FLOAT_ACB #undef GEN_FLOAT_ACB #undef _GEN_FLOAT_AB diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index 88fab65628..4260635a12 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -50,43 +50,14 @@ GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT), GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT), GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT), -#define GEN_LDF(name, ldop, opc, type) \ -GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type), -#define GEN_LDUF(name, ldop, opc, type) \ -GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type), -#define GEN_LDUXF(name, ldop, opc, type) \ -GEN_HANDLER(name##ux, 0x1F, 0x17, opc, 0x00000001, type), -#define GEN_LDXF(name, ldop, opc2, opc3, type) \ -GEN_HANDLER(name##x, 0x1F, opc2, opc3, 0x00000001, type), -#define GEN_LDFS(name, ldop, op, type) \ -GEN_LDF(name, ldop, op | 0x20, type) \ -GEN_LDUF(name, ldop, op | 0x21, type) \ -GEN_LDUXF(name, ldop, op | 0x01, type) \ -GEN_LDXF(name, ldop, 0x17, op | 0x00, type) - -GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT) -GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT) GEN_HANDLER_E(lfdepx, 0x1F, 0x1F, 0x12, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(lfiwax, 0x1f, 0x17, 0x1a, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(lfiwzx, 0x1f, 0x17, 0x1b, 0x1, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_HANDLER_E(lfdpx, 0x1F, 0x17, 0x18, 0x00200001, PPC_NONE, PPC2_ISA205), -#define GEN_STF(name, stop, opc, type) \ -GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type), -#define GEN_STUF(name, stop, opc, type) \ -GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type), -#define GEN_STUXF(name, stop, opc, type) \ -GEN_HANDLER(name##ux, 0x1F, 0x17, opc, 0x00000001, type), #define GEN_STXF(name, stop, opc2, opc3, type) \ GEN_HANDLER(name##x, 0x1F, opc2, opc3, 0x00000001, type), -#define GEN_STFS(name, stop, op, type) \ -GEN_STF(name, stop, op | 0x20, type) \ -GEN_STUF(name, stop, op | 0x21, type) \ -GEN_STUXF(name, stop, op | 0x01, type) \ -GEN_STXF(name, stop, 0x17, op | 0x00, type) -GEN_STFS(stfd, st64_i64, 0x16, PPC_FLOAT) -GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT) GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) GEN_HANDLER_E(stfdepx, 0x1F, 0x1F, 0x16, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), From dcb4e5b72cffbbb09b149ce34898e58c3f581c62 Mon Sep 17 00:00:00 2001 From: Fernando Eckhardt Valle Date: Fri, 29 Oct 2021 17:23:54 -0300 Subject: [PATCH 1252/1334] target/ppc: Implement PLFS, PLFD, PSTFS and PSTFD instructions Reviewed-by: Richard Henderson Signed-off-by: Fernando Eckhardt Valle Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-5-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn64.decode | 11 +++++++++++ target/ppc/translate/fp-impl.c.inc | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index 72c5944a53..11e5ea81d6 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -56,6 +56,17 @@ PSTD 000001 00 0--.-- .................. \ PADDI 000001 10 0--.-- .................. \ 001110 ..... ..... ................ @PLS_D +### Float-Point Load and Store Instructions + +PLFS 000001 10 0--.-- .................. \ + 110000 ..... ..... ................ @PLS_D +PLFD 000001 10 0--.-- .................. \ + 110010 ..... ..... ................ @PLS_D +PSTFS 000001 10 0--.-- .................. \ + 110100 ..... ..... ................ @PLS_D +PSTFD 000001 10 0--.-- .................. \ + 110110 ..... ..... ................ @PLS_D + ### Prefixed No-operation Instruction @PNOP 000001 11 0000-- 000000000000000000 \ diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index 57a799db1c..d1dbb1b96b 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -1342,6 +1342,16 @@ static bool do_lsfp_D(DisasContext *ctx, arg_D *a, bool update, bool store, single); } +static bool do_lsfp_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool update, + bool store, bool single) +{ + arg_D d; + if (!resolve_PLS_D(ctx, &d, a)) { + return true; + } + return do_lsfp_D(ctx, &d, update, store, single); +} + static bool do_lsfp_X(DisasContext *ctx, arg_X *a, bool update, bool store, bool single) { @@ -1352,21 +1362,25 @@ TRANS(LFS, do_lsfp_D, false, false, true) TRANS(LFSU, do_lsfp_D, true, false, true) TRANS(LFSX, do_lsfp_X, false, false, true) TRANS(LFSUX, do_lsfp_X, true, false, true) +TRANS(PLFS, do_lsfp_PLS_D, false, false, true) TRANS(LFD, do_lsfp_D, false, false, false) TRANS(LFDU, do_lsfp_D, true, false, false) TRANS(LFDX, do_lsfp_X, false, false, false) TRANS(LFDUX, do_lsfp_X, true, false, false) +TRANS(PLFD, do_lsfp_PLS_D, false, false, false) TRANS(STFS, do_lsfp_D, false, true, true) TRANS(STFSU, do_lsfp_D, true, true, true) TRANS(STFSX, do_lsfp_X, false, true, true) TRANS(STFSUX, do_lsfp_X, true, true, true) +TRANS(PSTFS, do_lsfp_PLS_D, false, true, true) TRANS(STFD, do_lsfp_D, false, true, false) TRANS(STFDU, do_lsfp_D, true, true, false) TRANS(STFDX, do_lsfp_X, false, true, false) TRANS(STFDUX, do_lsfp_X, true, true, false) +TRANS(PSTFD, do_lsfp_PLS_D, false, true, false) #undef _GEN_FLOAT_ACB #undef GEN_FLOAT_ACB From e10271e1044d5d744e4e200fd8414deaf16b0e18 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Fri, 29 Oct 2021 17:23:55 -0300 Subject: [PATCH 1253/1334] target/ppc: Move LQ and STQ to decodetree Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-6-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 11 ++ target/ppc/translate.c | 156 +-------------------- target/ppc/translate/fixedpoint-impl.c.inc | 97 +++++++++++++ 3 files changed, 113 insertions(+), 151 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 19ef9bff67..7ff8c25ac5 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -24,9 +24,16 @@ @D_bfs ...... bf:3 - l:1 ra:5 imm:s16 &D_bf @D_bfu ...... bf:3 - l:1 ra:5 imm:16 &D_bf +%dq_si 4:s12 !function=times_16 +%dq_rtp 22:4 !function=times_2 +@DQ_rtp ...... ....0 ra:5 ............ .... &D rt=%dq_rtp si=%dq_si + %ds_si 2:s14 !function=times_4 @DS ...... rt:5 ra:5 .............. .. &D si=%ds_si +%ds_rtp 22:4 !function=times_2 +@DS_rtp ...... ....0 ra:5 .............. .. &D rt=%ds_rtp si=%ds_si + &DX rt d %dx_d 6:s10 16:5 0:1 @DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d @@ -74,6 +81,8 @@ LDU 111010 ..... ..... ..............01 @DS LDX 011111 ..... ..... ..... 0000010101 - @X LDUX 011111 ..... ..... ..... 0000110101 - @X +LQ 111000 ..... ..... ............ ---- @DQ_rtp + ### Fixed-Point Store Instructions STB 100110 ..... ..... ................ @D @@ -96,6 +105,8 @@ STDU 111110 ..... ..... ..............01 @DS STDX 011111 ..... ..... ..... 0010010101 - @X STDUX 011111 ..... ..... ..... 0010110101 - @X +STQ 111110 ..... ..... ..............10 @DS_rtp + ### Fixed-Point Compare Instructions CMP 011111 ... - . ..... ..... 0000000000 - @X_bfl diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 3f50f44c19..8d5141497f 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3327,69 +3327,6 @@ GEN_LDX_HVRM(ldcix, ld64_i64, 0x15, 0x1b, PPC_CILDST) GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x15, PPC_CILDST) GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST) GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST) - -/* lq */ -static void gen_lq(DisasContext *ctx) -{ - int ra, rd; - TCGv EA, hi, lo; - - /* lq is a legal user mode instruction starting in ISA 2.07 */ - bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; - bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; - - if (!legal_in_user_mode && ctx->pr) { - gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - - if (!le_is_supported && ctx->le_mode) { - gen_align_no_le(ctx); - return; - } - ra = rA(ctx->opcode); - rd = rD(ctx->opcode); - if (unlikely((rd & 1) || rd == ra)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - return; - } - - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_imm_index(ctx, EA, 0x0F); - - /* Note that the low part is always in RD+1, even in LE mode. */ - lo = cpu_gpr[rd + 1]; - hi = cpu_gpr[rd]; - - if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { - if (HAVE_ATOMIC128) { - TCGv_i32 oi = tcg_temp_new_i32(); - if (ctx->le_mode) { - tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); - gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); - } else { - tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); - gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); - } - tcg_temp_free_i32(oi); - tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); - } else { - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; - } - } else if (ctx->le_mode) { - tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_LEQ); - } else { - tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_BEQ); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_BEQ); - } - tcg_temp_free(EA); -} #endif /*** Integer store ***/ @@ -3435,90 +3372,6 @@ GEN_STX_HVRM(stdcix, st64_i64, 0x15, 0x1f, PPC_CILDST) GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST) GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST) GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST) - -static void gen_std(DisasContext *ctx) -{ - int rs; - TCGv EA; - - rs = rS(ctx->opcode); - if ((ctx->opcode & 0x3) == 0x2) { /* stq */ - bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; - bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; - TCGv hi, lo; - - if (!(ctx->insns_flags & PPC_64BX)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - } - - if (!legal_in_user_mode && ctx->pr) { - gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - - if (!le_is_supported && ctx->le_mode) { - gen_align_no_le(ctx); - return; - } - - if (unlikely(rs & 1)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - return; - } - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_imm_index(ctx, EA, 0x03); - - /* Note that the low part is always in RS+1, even in LE mode. */ - lo = cpu_gpr[rs + 1]; - hi = cpu_gpr[rs]; - - if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { - if (HAVE_ATOMIC128) { - TCGv_i32 oi = tcg_temp_new_i32(); - if (ctx->le_mode) { - tcg_gen_movi_i32(oi, make_memop_idx(MO_LE | MO_128, - ctx->mem_idx)); - gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi); - } else { - tcg_gen_movi_i32(oi, make_memop_idx(MO_BE | MO_128, - ctx->mem_idx)); - gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi); - } - tcg_temp_free_i32(oi); - } else { - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; - } - } else if (ctx->le_mode) { - tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_LEQ); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_st_i64(hi, EA, ctx->mem_idx, MO_LEQ); - } else { - tcg_gen_qemu_st_i64(hi, EA, ctx->mem_idx, MO_BEQ); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_BEQ); - } - tcg_temp_free(EA); - } else { - /* std / stdu */ - if (Rc(ctx->opcode)) { - if (unlikely(rA(ctx->opcode) == 0)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - return; - } - } - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_imm_index(ctx, EA, 0x03); - gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA); - if (Rc(ctx->opcode)) { - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); - } - tcg_temp_free(EA); - } -} #endif /*** Integer load and store with byte reverse ***/ @@ -7457,6 +7310,11 @@ static int times_4(DisasContext *ctx, int x) return x * 4; } +static int times_16(DisasContext *ctx, int x) +{ + return x * 16; +} + /* * Helpers for trans_* functions to check for specific insns flags. * Use token pasting to ensure that we use the proper flag with the @@ -7696,10 +7554,6 @@ GEN_HANDLER2_E(extswsli0, "extswsli", 0x1F, 0x1A, 0x1B, 0x00000000, GEN_HANDLER2_E(extswsli1, "extswsli", 0x1F, 0x1B, 0x1B, 0x00000000, PPC_NONE, PPC2_ISA300), #endif -#if defined(TARGET_PPC64) -GEN_HANDLER(lq, 0x38, 0xFF, 0xFF, 0x00000000, PPC_64BX), -GEN_HANDLER(std, 0x3E, 0xFF, 0xFF, 0x00000000, PPC_64B), -#endif /* handles lfdp, lxsd, lxssp */ GEN_HANDLER_E(dform39, 0x39, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205), /* handles stfdp, lxv, stxsd, stxssp, stxv */ diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 812b7ddd0a..ff35a96459 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -69,6 +69,97 @@ static bool do_ldst_X(DisasContext *ctx, arg_X *a, bool update, return do_ldst(ctx, a->rt, a->ra, cpu_gpr[a->rb], update, store, mop); } +static bool do_ldst_quad(DisasContext *ctx, arg_D *a, bool store, bool prefixed) +{ +#if defined(TARGET_PPC64) + TCGv ea; + TCGv_i64 low_addr_gpr, high_addr_gpr; + MemOp mop; + + REQUIRE_INSNS_FLAGS(ctx, 64BX); + + if (!prefixed && !(ctx->insns_flags2 & PPC2_LSQ_ISA207)) { + if (ctx->pr) { + /* lq and stq were privileged prior to V. 2.07 */ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return true; + } + + if (ctx->le_mode) { + gen_align_no_le(ctx); + return true; + } + } + + if (!store && unlikely(a->ra == a->rt)) { + gen_invalid(ctx); + return true; + } + + gen_set_access_type(ctx, ACCESS_INT); + ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->si)); + + if (prefixed || !ctx->le_mode) { + low_addr_gpr = cpu_gpr[a->rt]; + high_addr_gpr = cpu_gpr[a->rt + 1]; + } else { + low_addr_gpr = cpu_gpr[a->rt + 1]; + high_addr_gpr = cpu_gpr[a->rt]; + } + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + if (HAVE_ATOMIC128) { + mop = DEF_MEMOP(MO_128); + TCGv_i32 oi = tcg_constant_i32(make_memop_idx(mop, ctx->mem_idx)); + if (store) { + if (ctx->le_mode) { + gen_helper_stq_le_parallel(cpu_env, ea, low_addr_gpr, + high_addr_gpr, oi); + } else { + gen_helper_stq_be_parallel(cpu_env, ea, high_addr_gpr, + low_addr_gpr, oi); + + } + } else { + if (ctx->le_mode) { + gen_helper_lq_le_parallel(low_addr_gpr, cpu_env, ea, oi); + tcg_gen_ld_i64(high_addr_gpr, cpu_env, + offsetof(CPUPPCState, retxh)); + } else { + gen_helper_lq_be_parallel(high_addr_gpr, cpu_env, ea, oi); + tcg_gen_ld_i64(low_addr_gpr, cpu_env, + offsetof(CPUPPCState, retxh)); + } + } + } else { + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + } + } else { + mop = DEF_MEMOP(MO_Q); + if (store) { + tcg_gen_qemu_st_i64(low_addr_gpr, ea, ctx->mem_idx, mop); + } else { + tcg_gen_qemu_ld_i64(low_addr_gpr, ea, ctx->mem_idx, mop); + } + + gen_addr_add(ctx, ea, ea, 8); + + if (store) { + tcg_gen_qemu_st_i64(high_addr_gpr, ea, ctx->mem_idx, mop); + } else { + tcg_gen_qemu_ld_i64(high_addr_gpr, ea, ctx->mem_idx, mop); + } + } + tcg_temp_free(ea); +#else + qemu_build_not_reached(); +#endif + + return true; +} + /* Load Byte and Zero */ TRANS(LBZ, do_ldst_D, false, false, MO_UB) TRANS(LBZX, do_ldst_X, false, false, MO_UB) @@ -110,6 +201,9 @@ TRANS64(LDU, do_ldst_D, true, false, MO_Q) TRANS64(LDUX, do_ldst_X, true, false, MO_Q) TRANS64(PLD, do_ldst_PLS_D, false, false, MO_Q) +/* Load Quadword */ +TRANS64(LQ, do_ldst_quad, false, false); + /* Store Byte */ TRANS(STB, do_ldst_D, false, true, MO_UB) TRANS(STBX, do_ldst_X, false, true, MO_UB) @@ -138,6 +232,9 @@ TRANS64(STDU, do_ldst_D, true, true, MO_Q) TRANS64(STDUX, do_ldst_X, true, true, MO_Q) TRANS64(PSTD, do_ldst_PLS_D, false, true, MO_Q) +/* Store Quadword */ +TRANS64(STQ, do_ldst_quad, true, false); + /* * Fixed-Point Compare Instructions */ From 49de064889b9af091767b78d575d361cba6c8f82 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Fri, 29 Oct 2021 17:23:56 -0300 Subject: [PATCH 1254/1334] target/ppc: Implement PLQ and PSTQ Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-7-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn64.decode | 4 ++++ target/ppc/translate/fixedpoint-impl.c.inc | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index 11e5ea81d6..48756cd4ca 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -38,6 +38,8 @@ PLWA 000001 00 0--.-- .................. \ 101001 ..... ..... ................ @PLS_D PLD 000001 00 0--.-- .................. \ 111001 ..... ..... ................ @PLS_D +PLQ 000001 00 0--.-- .................. \ + 111000 ..... ..... ................ @PLS_D ### Fixed-Point Store Instructions @@ -50,6 +52,8 @@ PSTH 000001 10 0--.-- .................. \ PSTD 000001 00 0--.-- .................. \ 111101 ..... ..... ................ @PLS_D +PSTQ 000001 00 0--.-- .................. \ + 111100 ..... ..... ................ @PLS_D ### Fixed-Point Arithmetic Instructions diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index ff35a96459..0d9c6e0996 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -160,6 +160,16 @@ static bool do_ldst_quad(DisasContext *ctx, arg_D *a, bool store, bool prefixed) return true; } +static bool do_ldst_quad_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool store) +{ + arg_D d; + if (!resolve_PLS_D(ctx, &d, a)) { + return true; + } + + return do_ldst_quad(ctx, &d, store, true); +} + /* Load Byte and Zero */ TRANS(LBZ, do_ldst_D, false, false, MO_UB) TRANS(LBZX, do_ldst_X, false, false, MO_UB) @@ -203,6 +213,7 @@ TRANS64(PLD, do_ldst_PLS_D, false, false, MO_Q) /* Load Quadword */ TRANS64(LQ, do_ldst_quad, false, false); +TRANS64(PLQ, do_ldst_quad_PLS_D, false); /* Store Byte */ TRANS(STB, do_ldst_D, false, true, MO_UB) @@ -234,6 +245,7 @@ TRANS64(PSTD, do_ldst_PLS_D, false, true, MO_Q) /* Store Quadword */ TRANS64(STQ, do_ldst_quad, true, false); +TRANS64(PSTQ, do_ldst_quad_PLS_D, true); /* * Fixed-Point Compare Instructions From 82be6e02b480b2c7427b0ab91f2c297717d3cfa6 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 17:23:57 -0300 Subject: [PATCH 1255/1334] target/ppc: Implement cntlzdm Implement the following PowerISA v3.1 instruction: cntlzdm: Count Leading Zeros Doubleword Under Bit Mask Suggested-by: Richard Henderson Signed-off-by: Luis Pires Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-8-matheus.ferst@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/insn32.decode | 1 + target/ppc/translate/fixedpoint-impl.c.inc | 36 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 7ff8c25ac5..eb3383c99c 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -124,6 +124,7 @@ ADDPCIS 010011 ..... ..... .......... 00010 . @DX ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X +CNTLZDM 011111 ..... ..... ..... 0000111011 - @X ### Float-Point Load Instructions diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 0d9c6e0996..c9e9ae35df 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -413,3 +413,39 @@ static bool trans_CFUGED(DisasContext *ctx, arg_X *a) #endif return true; } + +#if defined(TARGET_PPC64) +static void do_cntlzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask) +{ + TCGv_i64 tmp; + TCGLabel *l1; + + tmp = tcg_temp_local_new_i64(); + l1 = gen_new_label(); + + tcg_gen_and_i64(tmp, src, mask); + tcg_gen_clzi_i64(tmp, tmp, 64); + + tcg_gen_brcondi_i64(TCG_COND_EQ, tmp, 0, l1); + + tcg_gen_subfi_i64(tmp, 64, tmp); + tcg_gen_shr_i64(tmp, mask, tmp); + tcg_gen_ctpop_i64(tmp, tmp); + + gen_set_label(l1); + + tcg_gen_mov_i64(dst, tmp); +} +#endif + +static bool trans_CNTLZDM(DisasContext *ctx, arg_X *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA310); +#if defined(TARGET_PPC64) + do_cntlzdm(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} From f356b3ba4745d892156931ba7c82067c4a9c18ab Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 17:23:58 -0300 Subject: [PATCH 1256/1334] target/ppc: Implement cnttzdm Implement the following PowerISA v3.1 instruction: cnttzdm: Count Trailing Zeros Doubleword Under Bit Mask Suggested-by: Richard Henderson Signed-off-by: Luis Pires Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-9-matheus.ferst@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/insn32.decode | 1 + target/ppc/translate/fixedpoint-impl.c.inc | 28 ++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index eb3383c99c..ee8c90d520 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -125,6 +125,7 @@ ADDPCIS 010011 ..... ..... .......... 00010 . @DX CFUGED 011111 ..... ..... ..... 0011011100 - @X CNTLZDM 011111 ..... ..... ..... 0000111011 - @X +CNTTZDM 011111 ..... ..... ..... 1000111011 - @X ### Float-Point Load Instructions diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index c9e9ae35df..d3dc0a474e 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -415,7 +415,7 @@ static bool trans_CFUGED(DisasContext *ctx, arg_X *a) } #if defined(TARGET_PPC64) -static void do_cntlzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask) +static void do_cntzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask, bool trail) { TCGv_i64 tmp; TCGLabel *l1; @@ -424,12 +424,20 @@ static void do_cntlzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask) l1 = gen_new_label(); tcg_gen_and_i64(tmp, src, mask); - tcg_gen_clzi_i64(tmp, tmp, 64); + if (trail) { + tcg_gen_ctzi_i64(tmp, tmp, 64); + } else { + tcg_gen_clzi_i64(tmp, tmp, 64); + } tcg_gen_brcondi_i64(TCG_COND_EQ, tmp, 0, l1); tcg_gen_subfi_i64(tmp, 64, tmp); - tcg_gen_shr_i64(tmp, mask, tmp); + if (trail) { + tcg_gen_shl_i64(tmp, mask, tmp); + } else { + tcg_gen_shr_i64(tmp, mask, tmp); + } tcg_gen_ctpop_i64(tmp, tmp); gen_set_label(l1); @@ -443,7 +451,19 @@ static bool trans_CNTLZDM(DisasContext *ctx, arg_X *a) REQUIRE_64BIT(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA310); #if defined(TARGET_PPC64) - do_cntlzdm(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + do_cntzdm(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb], false); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_CNTTZDM(DisasContext *ctx, arg_X *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA310); +#if defined(TARGET_PPC64) + do_cntzdm(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb], true); #else qemu_build_not_reached(); #endif From 21ba6e58733b650b6af3505c4a7a44e14ff9492e Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Fri, 29 Oct 2021 17:23:59 -0300 Subject: [PATCH 1257/1334] target/ppc: Implement pdepd instruction Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-10-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/helper.h | 1 + target/ppc/insn32.decode | 1 + target/ppc/int_helper.c | 20 ++++++++++++++++++++ target/ppc/translate/fixedpoint-impl.c.inc | 12 ++++++++++++ 4 files changed, 34 insertions(+) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 4076aa281e..3e22957559 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -48,6 +48,7 @@ DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_3(sraw, tl, env, tl, tl) DEF_HELPER_FLAGS_2(cfuged, TCG_CALL_NO_RWG_SE, i64, i64, i64) #if defined(TARGET_PPC64) +DEF_HELPER_FLAGS_2(PDEPD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index ee8c90d520..c47a6a02e1 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -126,6 +126,7 @@ ADDPCIS 010011 ..... ..... .......... 00010 . @DX CFUGED 011111 ..... ..... ..... 0011011100 - @X CNTLZDM 011111 ..... ..... ..... 0000111011 - @X CNTTZDM 011111 ..... ..... ..... 1000111011 - @X +PDEPD 011111 ..... ..... ..... 0010011100 - @X ### Float-Point Load Instructions diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index eeb7781a9e..337bb7f4d3 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -386,6 +386,26 @@ uint64_t helper_cfuged(uint64_t src, uint64_t mask) return left | (right >> n); } +#if defined(TARGET_PPC64) +uint64_t helper_PDEPD(uint64_t src, uint64_t mask) +{ + int i, o; + uint64_t result = 0; + + if (mask == -1) { + return src; + } + + for (i = 0; mask != 0; i++) { + o = ctz64(mask); + mask &= mask - 1; + result |= ((src >> i) & 1) << o; + } + + return result; +} +#endif + /*****************************************************************************/ /* PowerPC 601 specific instructions (POWER bridge) */ target_ulong helper_div(CPUPPCState *env, target_ulong arg1, target_ulong arg2) diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index d3dc0a474e..f0bf69fbac 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -469,3 +469,15 @@ static bool trans_CNTTZDM(DisasContext *ctx, arg_X *a) #endif return true; } + +static bool trans_PDEPD(DisasContext *ctx, arg_X *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA310); +#if defined(TARGET_PPC64) + gen_helper_PDEPD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} From 8bdb760606dadf6b7454aac84f214bb0f2c80654 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Fri, 29 Oct 2021 17:24:00 -0300 Subject: [PATCH 1258/1334] target/ppc: Implement pextd instruction Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211029202424.175401-11-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/helper.h | 1 + target/ppc/insn32.decode | 1 + target/ppc/int_helper.c | 18 ++++++++++++++++++ target/ppc/translate/fixedpoint-impl.c.inc | 12 ++++++++++++ 4 files changed, 32 insertions(+) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 3e22957559..808c582382 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -49,6 +49,7 @@ DEF_HELPER_3(sraw, tl, env, tl, tl) DEF_HELPER_FLAGS_2(cfuged, TCG_CALL_NO_RWG_SE, i64, i64, i64) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(PDEPD, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(PEXTD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index c47a6a02e1..e13f920702 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -127,6 +127,7 @@ CFUGED 011111 ..... ..... ..... 0011011100 - @X CNTLZDM 011111 ..... ..... ..... 0000111011 - @X CNTTZDM 011111 ..... ..... ..... 1000111011 - @X PDEPD 011111 ..... ..... ..... 0010011100 - @X +PEXTD 011111 ..... ..... ..... 0010111100 - @X ### Float-Point Load Instructions diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 337bb7f4d3..913d76be6e 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -404,6 +404,24 @@ uint64_t helper_PDEPD(uint64_t src, uint64_t mask) return result; } + +uint64_t helper_PEXTD(uint64_t src, uint64_t mask) +{ + int i, o; + uint64_t result = 0; + + if (mask == -1) { + return src; + } + + for (o = 0; mask != 0; o++) { + i = ctz64(mask); + mask &= mask - 1; + result |= ((src >> i) & 1) << o; + } + + return result; +} #endif /*****************************************************************************/ diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index f0bf69fbac..220b099fcd 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -481,3 +481,15 @@ static bool trans_PDEPD(DisasContext *ctx, arg_X *a) #endif return true; } + +static bool trans_PEXTD(DisasContext *ctx, arg_X *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA310); +#if defined(TARGET_PPC64) + gen_helper_PEXTD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} From 727385c4e13e1a5a985124a20a2370855141111d Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:03 -0300 Subject: [PATCH 1259/1334] libdecnumber: introduce decNumberFrom[U]Int128 This will be used to implement PowerPC's dcffixqq. Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-2-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- include/libdecnumber/decNumber.h | 2 ++ libdecnumber/decNumber.c | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/libdecnumber/decNumber.h b/include/libdecnumber/decNumber.h index aa115fed07..0cf69c7db2 100644 --- a/include/libdecnumber/decNumber.h +++ b/include/libdecnumber/decNumber.h @@ -116,6 +116,8 @@ decNumber * decNumberFromUInt32(decNumber *, uint32_t); decNumber *decNumberFromInt64(decNumber *, int64_t); decNumber *decNumberFromUInt64(decNumber *, uint64_t); + decNumber *decNumberFromInt128(decNumber *, uint64_t, int64_t); + decNumber *decNumberFromUInt128(decNumber *, uint64_t, uint64_t); decNumber * decNumberFromString(decNumber *, const char *, decContext *); char * decNumberToString(const decNumber *, char *); char * decNumberToEngString(const decNumber *, char *); diff --git a/libdecnumber/decNumber.c b/libdecnumber/decNumber.c index 1ffe458ad8..d7716ce175 100644 --- a/libdecnumber/decNumber.c +++ b/libdecnumber/decNumber.c @@ -167,6 +167,7 @@ /* ------------------------------------------------------------------ */ #include "qemu/osdep.h" +#include "qemu/host-utils.h" #include "libdecnumber/dconfig.h" #include "libdecnumber/decNumber.h" #include "libdecnumber/decNumberLocal.h" @@ -462,6 +463,41 @@ decNumber *decNumberFromUInt64(decNumber *dn, uint64_t uin) return dn; } /* decNumberFromUInt64 */ +decNumber *decNumberFromInt128(decNumber *dn, uint64_t lo, int64_t hi) +{ + uint64_t unsig_hi = hi; + if (hi < 0) { + if (lo == 0) { + unsig_hi = -unsig_hi; + } else { + unsig_hi = ~unsig_hi; + lo = -lo; + } + } + + decNumberFromUInt128(dn, lo, unsig_hi); + if (hi < 0) { + dn->bits = DECNEG; /* sign needed */ + } + return dn; +} /* decNumberFromInt128 */ + +decNumber *decNumberFromUInt128(decNumber *dn, uint64_t lo, uint64_t hi) +{ + uint64_t rem; + Unit *up; /* work pointer */ + decNumberZero(dn); /* clean */ + if (lo == 0 && hi == 0) { + return dn; /* [or decGetDigits bad call] */ + } + for (up = dn->lsu; hi > 0 || lo > 0; up++) { + rem = divu128(&lo, &hi, DECDPUNMAX + 1); + *up = (Unit)rem; + } + dn->digits = decGetDigits(dn->lsu, up - dn->lsu); + return dn; +} /* decNumberFromUInt128 */ + /* ------------------------------------------------------------------ */ /* to-int64 -- conversion to int64 */ /* */ From e2205a4609750f824362a2110f49e9d60c4315e0 Mon Sep 17 00:00:00 2001 From: Bruno Larsen Date: Fri, 29 Oct 2021 16:24:04 -0300 Subject: [PATCH 1260/1334] target/ppc: Move REQUIRE_ALTIVEC/VECTOR to translate.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move REQUIRE_ALTIVEC to translate.c and rename it to REQUIRE_VECTOR. Signed-off-by: Bruno Larsen Signed-off-by: Matheus Ferst Signed-off-by: Fernando Valle Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Acked-by: David Gibson Message-Id: <20211029192417.400707-3-luis.pires@eldorado.org.br> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- target/ppc/translate.c | 8 ++++++++ target/ppc/translate/vector-impl.c.inc | 10 +--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 8d5141497f..1d24b85746 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7341,6 +7341,14 @@ static int times_16(DisasContext *ctx, int x) # define REQUIRE_64BIT(CTX) REQUIRE_INSNS_FLAGS(CTX, 64B) #endif +#define REQUIRE_VECTOR(CTX) \ + do { \ + if (unlikely(!(CTX)->altivec_enabled)) { \ + gen_exception((CTX), POWERPC_EXCP_VPU); \ + return true; \ + } \ + } while (0) + /* * Helpers for implementing sets of trans_* functions. * Defer the implementation of NAME to FUNC, with optional extra arguments. diff --git a/target/ppc/translate/vector-impl.c.inc b/target/ppc/translate/vector-impl.c.inc index 117ce9b137..197e903337 100644 --- a/target/ppc/translate/vector-impl.c.inc +++ b/target/ppc/translate/vector-impl.c.inc @@ -17,20 +17,12 @@ * License along with this library; if not, see . */ -#define REQUIRE_ALTIVEC(CTX) \ - do { \ - if (unlikely(!(CTX)->altivec_enabled)) { \ - gen_exception((CTX), POWERPC_EXCP_VPU); \ - return true; \ - } \ - } while (0) - static bool trans_VCFUGED(DisasContext *ctx, arg_VX *a) { TCGv_i64 tgt, src, mask; REQUIRE_INSNS_FLAGS2(ctx, ISA310); - REQUIRE_ALTIVEC(ctx); + REQUIRE_VECTOR(ctx); tgt = tcg_temp_new_i64(); src = tcg_temp_new_i64(); From 86057426d037ca023632156777a28d8493eaeb9d Mon Sep 17 00:00:00 2001 From: Fernando Valle Date: Fri, 29 Oct 2021 16:24:05 -0300 Subject: [PATCH 1261/1334] target/ppc: Introduce REQUIRE_FPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fernando Valle Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-4-luis.pires@eldorado.org.br> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- target/ppc/translate.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 1d24b85746..d4e72affa6 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7349,6 +7349,14 @@ static int times_16(DisasContext *ctx, int x) } \ } while (0) +#define REQUIRE_FPU(ctx) \ + do { \ + if (unlikely(!(ctx)->fpu_enabled)) { \ + gen_exception((ctx), POWERPC_EXCP_FPU); \ + return true; \ + } \ + } while (0) + /* * Helpers for implementing sets of trans_* functions. * Defer the implementation of NAME to FUNC, with optional extra arguments. From d39b2cc7d0b87d95bbecbd142179efdc0b85c4e5 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:06 -0300 Subject: [PATCH 1262/1334] target/ppc: Implement DCFFIXQQ Implement the following PowerISA v3.1 instruction: dcffixqq: DFP Convert From Fixed Quadword Quad Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-5-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 12 ++++++++++++ target/ppc/helper.h | 1 + target/ppc/insn32.decode | 8 ++++++++ target/ppc/translate.c | 5 +++++ target/ppc/translate/dfp-impl.c.inc | 17 +++++++++++++++++ 5 files changed, 43 insertions(+) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index 07341a69f5..6b837c4450 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -970,6 +970,18 @@ static void CFFIX_PPs(struct PPC_DFP *dfp) DFP_HELPER_CFFIX(dcffix, 64) DFP_HELPER_CFFIX(dcffixq, 128) +void helper_DCFFIXQQ(CPUPPCState *env, ppc_fprp_t *t, ppc_avr_t *b) +{ + struct PPC_DFP dfp; + + dfp_prepare_decimal128(&dfp, NULL, NULL, env); + decNumberFromInt128(&dfp.t, (uint64_t)b->VsrD(1), (int64_t)b->VsrD(0)); + dfp_finalize_decimal128(&dfp); + CFFIX_PPs(&dfp); + + set_dfp128(t, &dfp.vt); +} + #define DFP_HELPER_CTFIX(op, size) \ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) \ { \ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 808c582382..8e4f67ceac 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -736,6 +736,7 @@ DEF_HELPER_3(drsp, void, env, fprp, fprp) DEF_HELPER_3(drdpq, void, env, fprp, fprp) DEF_HELPER_3(dcffix, void, env, fprp, fprp) DEF_HELPER_3(dcffixq, void, env, fprp, fprp) +DEF_HELPER_3(DCFFIXQQ, void, env, fprp, avr) DEF_HELPER_3(dctfix, void, env, fprp, fprp) DEF_HELPER_3(dctfixq, void, env, fprp, fprp) DEF_HELPER_4(ddedpd, void, env, fprp, fprp, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index e13f920702..36586dfa59 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -50,6 +50,10 @@ &X_bfl bf l:bool ra rb @X_bfl ...... bf:3 - l:1 ra:5 rb:5 ..........- &X_bfl +&X_frtp_vrb frtp vrb +%x_frtp 22:4 !function=times_2 +@X_frtp_vrb ...... ....0 ..... vrb:5 .......... . &X_frtp_vrb frtp=%x_frtp + ### Fixed-Point Load Instructions LBZ 100010 ..... ..... ................ @D @@ -160,6 +164,10 @@ SETBCR 011111 ..... ..... ----- 0110100000 - @X_bi SETNBC 011111 ..... ..... ----- 0111000000 - @X_bi SETNBCR 011111 ..... ..... ----- 0111100000 - @X_bi +### Decimal Floating-Point Conversion Instructions + +DCFFIXQQ 111111 ..... 00000 ..... 1111100010 - @X_frtp_vrb + ## Vector Bit Manipulation Instruction VCFUGED 000100 ..... ..... ..... 10101001101 @VX diff --git a/target/ppc/translate.c b/target/ppc/translate.c index d4e72affa6..486339d402 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7305,6 +7305,11 @@ static inline void set_avr64(int regno, TCGv_i64 src, bool high) /* * Helpers for decodetree used by !function for decoding arguments. */ +static int times_2(DisasContext *ctx, int x) +{ + return x * 2; +} + static int times_4(DisasContext *ctx, int x) { return x * 4; diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index 6c556dc2e1..d5b66567a6 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -230,3 +230,20 @@ GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) #undef GEN_DFP_T_A_B_I32_Rc #undef GEN_DFP_T_B_Rc #undef GEN_DFP_T_FPR_I32_Rc + +static bool trans_DCFFIXQQ(DisasContext *ctx, arg_DCFFIXQQ *a) +{ + TCGv_ptr rt, rb; + + REQUIRE_INSNS_FLAGS2(ctx, DFP); + REQUIRE_FPU(ctx); + REQUIRE_VECTOR(ctx); + + rt = gen_fprp_ptr(a->frtp); + rb = gen_avr_ptr(a->vrb); + gen_helper_DCFFIXQQ(cpu_env, rt, rb); + tcg_temp_free_ptr(rt); + tcg_temp_free_ptr(rb); + + return true; +} From e06049f38074b9533c4beef37d634dad3bd97c73 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:07 -0300 Subject: [PATCH 1263/1334] host-utils: Introduce mulu128 Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-6-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- include/qemu/host-utils.h | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index a3a7ced78d..ca979dc6cc 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -590,6 +590,42 @@ static inline bool umul64_overflow(uint64_t x, uint64_t y, uint64_t *ret) #endif } +/* + * Unsigned 128x64 multiplication. + * Returns true if the result got truncated to 128 bits. + * Otherwise, returns false and the multiplication result via plow and phigh. + */ +static inline bool mulu128(uint64_t *plow, uint64_t *phigh, uint64_t factor) +{ +#if defined(CONFIG_INT128) && \ + (__has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5) + bool res; + __uint128_t r; + __uint128_t f = ((__uint128_t)*phigh << 64) | *plow; + res = __builtin_mul_overflow(f, factor, &r); + + *plow = r; + *phigh = r >> 64; + + return res; +#else + uint64_t dhi = *phigh; + uint64_t dlo = *plow; + uint64_t ahi; + uint64_t blo, bhi; + + if (dhi == 0) { + mulu64(plow, phigh, dlo, factor); + return false; + } + + mulu64(plow, &ahi, dlo, factor); + mulu64(&blo, &bhi, dhi, factor); + + return uadd64_overflow(ahi, blo, phigh) || bhi != 0; +#endif +} + /** * uadd64_carry - addition with carry-in and carry-out * @x, @y: addends From 21d7826fdbf13bc3180f8f23f3f87967604fdf7e Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:08 -0300 Subject: [PATCH 1264/1334] libdecnumber: Introduce decNumberIntegralToInt128 This will be used to implement PowerPC's dctfixqq. Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-7-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- include/libdecnumber/decNumber.h | 2 + include/libdecnumber/decNumberLocal.h | 2 +- libdecnumber/decContext.c | 7 +- libdecnumber/decNumber.c | 95 +++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/include/libdecnumber/decNumber.h b/include/libdecnumber/decNumber.h index 0cf69c7db2..41bc2a0d36 100644 --- a/include/libdecnumber/decNumber.h +++ b/include/libdecnumber/decNumber.h @@ -124,6 +124,8 @@ uint32_t decNumberToUInt32(const decNumber *, decContext *); int32_t decNumberToInt32(const decNumber *, decContext *); int64_t decNumberIntegralToInt64(const decNumber *dn, decContext *set); + void decNumberIntegralToInt128(const decNumber *dn, decContext *set, + uint64_t *plow, uint64_t *phigh); uint8_t * decNumberGetBCD(const decNumber *, uint8_t *); decNumber * decNumberSetBCD(decNumber *, const uint8_t *, uint32_t); diff --git a/include/libdecnumber/decNumberLocal.h b/include/libdecnumber/decNumberLocal.h index 4d53c077f2..6198ca8593 100644 --- a/include/libdecnumber/decNumberLocal.h +++ b/include/libdecnumber/decNumberLocal.h @@ -98,7 +98,7 @@ /* Shared lookup tables */ extern const uByte DECSTICKYTAB[10]; /* re-round digits if sticky */ - extern const uLong DECPOWERS[19]; /* powers of ten table */ + extern const uLong DECPOWERS[20]; /* powers of ten table */ /* The following are included from decDPD.h */ extern const uShort DPD2BIN[1024]; /* DPD -> 0-999 */ extern const uShort BIN2DPD[1000]; /* 0-999 -> DPD */ diff --git a/libdecnumber/decContext.c b/libdecnumber/decContext.c index 7d97a65ac5..1956edf0a7 100644 --- a/libdecnumber/decContext.c +++ b/libdecnumber/decContext.c @@ -53,12 +53,13 @@ static const Flag *mfctop=(Flag *)&mfcone; /* -> top byte */ const uByte DECSTICKYTAB[10]={1,1,2,3,4,6,6,7,8,9}; /* used if sticky */ /* ------------------------------------------------------------------ */ -/* Powers of ten (powers[n]==10**n, 0<=n<=9) */ +/* Powers of ten (powers[n]==10**n, 0<=n<=19) */ /* ------------------------------------------------------------------ */ -const uLong DECPOWERS[19] = {1, 10, 100, 1000, 10000, 100000, 1000000, +const uLong DECPOWERS[20] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000ULL, 100000000000ULL, 1000000000000ULL, 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, - 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, }; + 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, + 10000000000000000000ULL,}; /* ------------------------------------------------------------------ */ /* decContextClearStatus -- clear bits in current status */ diff --git a/libdecnumber/decNumber.c b/libdecnumber/decNumber.c index d7716ce175..31282adafd 100644 --- a/libdecnumber/decNumber.c +++ b/libdecnumber/decNumber.c @@ -264,6 +264,7 @@ static decNumber * decTrim(decNumber *, decContext *, Flag, Int *); static Int decUnitAddSub(const Unit *, Int, const Unit *, Int, Int, Unit *, Int); static Int decUnitCompare(const Unit *, Int, const Unit *, Int, Int); +static bool mulUInt128ByPowOf10(uLong *, uLong *, uInt); #if !DECSUBSET /* decFinish == decFinalize when no subset arithmetic needed */ @@ -542,6 +543,68 @@ Invalid: return 0; } /* decNumberIntegralToInt64 */ +/* ------------------------------------------------------------------ */ +/* decNumberIntegralToInt128 -- conversion to int128 */ +/* */ +/* dn is the decNumber to convert. dn is assumed to have been */ +/* rounded to a floating point integer value. */ +/* set is the context for reporting errors */ +/* returns the converted decNumber via plow and phigh */ +/* */ +/* Invalid is set if the decNumber is a NaN, Infinite or is out of */ +/* range for a signed 128 bit integer. */ +/* ------------------------------------------------------------------ */ + +void decNumberIntegralToInt128(const decNumber *dn, decContext *set, + uint64_t *plow, uint64_t *phigh) +{ + int d; /* work */ + const Unit *up; /* .. */ + uint64_t lo = 0, hi = 0; + + if (decNumberIsSpecial(dn) || (dn->exponent < 0) || + (dn->digits + dn->exponent > 39)) { + goto Invalid; + } + + up = dn->lsu; /* -> lsu */ + + for (d = (dn->digits - 1) / DECDPUN; d >= 0; d--) { + if (mulu128(&lo, &hi, DECDPUNMAX + 1)) { + /* overflow */ + goto Invalid; + } + if (uadd64_overflow(lo, up[d], &lo)) { + if (uadd64_overflow(hi, 1, &hi)) { + /* overflow */ + goto Invalid; + } + } + } + + if (mulUInt128ByPowOf10(&lo, &hi, dn->exponent)) { + /* overflow */ + goto Invalid; + } + + if (decNumberIsNegative(dn)) { + if (lo == 0) { + *phigh = -hi; + *plow = 0; + } else { + *phigh = ~hi; + *plow = -lo; + } + } else { + *plow = lo; + *phigh = hi; + } + + return; + +Invalid: + decContextSetStatus(set, DEC_Invalid_operation); +} /* decNumberIntegralToInt128 */ /* ------------------------------------------------------------------ */ /* to-scientific-string -- conversion to numeric string */ @@ -7885,6 +7948,38 @@ static Int decGetDigits(Unit *uar, Int len) { return digits; } /* decGetDigits */ +/* ------------------------------------------------------------------ */ +/* mulUInt128ByPowOf10 -- multiply a 128-bit unsigned integer by a */ +/* power of 10. */ +/* */ +/* The 128-bit factor composed of plow and phigh is multiplied */ +/* by 10^exp. */ +/* */ +/* plow pointer to the low 64 bits of the first factor */ +/* phigh pointer to the high 64 bits of the first factor */ +/* exp the exponent of the power of 10 of the second factor */ +/* */ +/* If the result fits in 128 bits, returns false and the */ +/* multiplication result through plow and phigh. */ +/* Otherwise, returns true. */ +/* ------------------------------------------------------------------ */ +static bool mulUInt128ByPowOf10(uLong *plow, uLong *phigh, uInt pow10) +{ + while (pow10 >= ARRAY_SIZE(powers)) { + if (mulu128(plow, phigh, powers[ARRAY_SIZE(powers) - 1])) { + /* Overflow */ + return true; + } + pow10 -= ARRAY_SIZE(powers) - 1; + } + + if (pow10 > 0) { + return mulu128(plow, phigh, powers[pow10]); + } else { + return false; + } +} + #if DECTRACE | DECCHECK /* ------------------------------------------------------------------ */ /* decNumberShow -- display a number [debug aid] */ From 328747f32f50491e9a6a2ec245b09058c55f9682 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:09 -0300 Subject: [PATCH 1265/1334] target/ppc: Implement DCTFIXQQ Implement the following PowerISA v3.1 instruction: dctfixqq: DFP Convert To Fixed Quadword Quad Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-8-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 52 +++++++++++++++++++++++++++++ target/ppc/helper.h | 1 + target/ppc/insn32.decode | 5 +++ target/ppc/translate/dfp-impl.c.inc | 17 ++++++++++ 4 files changed, 75 insertions(+) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index 6b837c4450..6ab46d7db5 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -51,6 +51,11 @@ static void set_dfp128(ppc_fprp_t *dfp, ppc_vsr_t *src) dfp[1].VsrD(0) = src->VsrD(1); } +static void set_dfp128_to_avr(ppc_avr_t *dst, ppc_vsr_t *src) +{ + *dst = *src; +} + struct PPC_DFP { CPUPPCState *env; ppc_vsr_t vt, va, vb; @@ -1020,6 +1025,53 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) \ DFP_HELPER_CTFIX(dctfix, 64) DFP_HELPER_CTFIX(dctfixq, 128) +void helper_DCTFIXQQ(CPUPPCState *env, ppc_avr_t *t, ppc_fprp_t *b) +{ + struct PPC_DFP dfp; + dfp_prepare_decimal128(&dfp, 0, b, env); + + if (unlikely(decNumberIsSpecial(&dfp.b))) { + uint64_t invalid_flags = FP_VX | FP_VXCVI; + if (decNumberIsInfinite(&dfp.b)) { + if (decNumberIsNegative(&dfp.b)) { + dfp.vt.VsrD(0) = INT64_MIN; + dfp.vt.VsrD(1) = 0; + } else { + dfp.vt.VsrD(0) = INT64_MAX; + dfp.vt.VsrD(1) = UINT64_MAX; + } + } else { /* NaN */ + dfp.vt.VsrD(0) = INT64_MIN; + dfp.vt.VsrD(1) = 0; + if (decNumberIsSNaN(&dfp.b)) { + invalid_flags |= FP_VXSNAN; + } + } + dfp_set_FPSCR_flag(&dfp, invalid_flags, FP_VE); + } else if (unlikely(decNumberIsZero(&dfp.b))) { + dfp.vt.VsrD(0) = 0; + dfp.vt.VsrD(1) = 0; + } else { + decNumberToIntegralExact(&dfp.b, &dfp.b, &dfp.context); + decNumberIntegralToInt128(&dfp.b, &dfp.context, + &dfp.vt.VsrD(1), &dfp.vt.VsrD(0)); + if (decContextTestStatus(&dfp.context, DEC_Invalid_operation)) { + if (decNumberIsNegative(&dfp.b)) { + dfp.vt.VsrD(0) = INT64_MIN; + dfp.vt.VsrD(1) = 0; + } else { + dfp.vt.VsrD(0) = INT64_MAX; + dfp.vt.VsrD(1) = UINT64_MAX; + } + dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FP_VE); + } else { + dfp_check_for_XX(&dfp); + } + } + + set_dfp128_to_avr(t, &dfp.vt); +} + static inline void dfp_set_bcd_digit_64(ppc_vsr_t *t, uint8_t digit, unsigned n) { diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 8e4f67ceac..3812bb0270 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -739,6 +739,7 @@ DEF_HELPER_3(dcffixq, void, env, fprp, fprp) DEF_HELPER_3(DCFFIXQQ, void, env, fprp, avr) DEF_HELPER_3(dctfix, void, env, fprp, fprp) DEF_HELPER_3(dctfixq, void, env, fprp, fprp) +DEF_HELPER_3(DCTFIXQQ, void, env, avr, fprp) DEF_HELPER_4(ddedpd, void, env, fprp, fprp, i32) DEF_HELPER_4(ddedpdq, void, env, fprp, fprp, i32) DEF_HELPER_4(denbcd, void, env, fprp, fprp, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 36586dfa59..33463c14ea 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -54,6 +54,10 @@ %x_frtp 22:4 !function=times_2 @X_frtp_vrb ...... ....0 ..... vrb:5 .......... . &X_frtp_vrb frtp=%x_frtp +&X_vrt_frbp vrt frbp +%x_frbp 12:4 !function=times_2 +@X_vrt_frbp ...... vrt:5 ..... ....0 .......... . &X_vrt_frbp frbp=%x_frbp + ### Fixed-Point Load Instructions LBZ 100010 ..... ..... ................ @D @@ -167,6 +171,7 @@ SETNBCR 011111 ..... ..... ----- 0111100000 - @X_bi ### Decimal Floating-Point Conversion Instructions DCFFIXQQ 111111 ..... 00000 ..... 1111100010 - @X_frtp_vrb +DCTFIXQQ 111111 ..... 00001 ..... 1111100010 - @X_vrt_frbp ## Vector Bit Manipulation Instruction diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index d5b66567a6..e149777481 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -247,3 +247,20 @@ static bool trans_DCFFIXQQ(DisasContext *ctx, arg_DCFFIXQQ *a) return true; } + +static bool trans_DCTFIXQQ(DisasContext *ctx, arg_DCTFIXQQ *a) +{ + TCGv_ptr rt, rb; + + REQUIRE_INSNS_FLAGS2(ctx, DFP); + REQUIRE_FPU(ctx); + REQUIRE_VECTOR(ctx); + + rt = gen_avr_ptr(a->vrt); + rb = gen_fprp_ptr(a->frbp); + gen_helper_DCTFIXQQ(cpu_env, rt, rb); + tcg_temp_free_ptr(rt); + tcg_temp_free_ptr(rb); + + return true; +} From 17fded9d963c767781485fdbffd6be005f7eea5a Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:10 -0300 Subject: [PATCH 1266/1334] target/ppc: Do not update nip on DFP instructions Before moving the existing DFP instructions to decodetree, drop the nip update that shouldn't be done for these instructions. Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-9-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/translate/dfp-impl.c.inc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index e149777481..1431d955c6 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -15,7 +15,6 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ - gen_update_nip(ctx, ctx->base.pc_next - 4); \ rd = gen_fprp_ptr(rD(ctx->opcode)); \ ra = gen_fprp_ptr(rA(ctx->opcode)); \ rb = gen_fprp_ptr(rB(ctx->opcode)); \ @@ -36,7 +35,6 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ - gen_update_nip(ctx, ctx->base.pc_next - 4); \ ra = gen_fprp_ptr(rA(ctx->opcode)); \ rb = gen_fprp_ptr(rB(ctx->opcode)); \ gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ @@ -54,7 +52,6 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ - gen_update_nip(ctx, ctx->base.pc_next - 4); \ uim = tcg_const_i32(UIMM5(ctx->opcode)); \ rb = gen_fprp_ptr(rB(ctx->opcode)); \ gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ @@ -72,7 +69,6 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ - gen_update_nip(ctx, ctx->base.pc_next - 4); \ ra = gen_fprp_ptr(rA(ctx->opcode)); \ dcm = tcg_const_i32(DCM(ctx->opcode)); \ gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ @@ -90,7 +86,6 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ - gen_update_nip(ctx, ctx->base.pc_next - 4); \ rt = gen_fprp_ptr(rD(ctx->opcode)); \ rb = gen_fprp_ptr(rB(ctx->opcode)); \ u32_1 = tcg_const_i32(u32f1(ctx->opcode)); \ @@ -114,7 +109,6 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ - gen_update_nip(ctx, ctx->base.pc_next - 4); \ rt = gen_fprp_ptr(rD(ctx->opcode)); \ ra = gen_fprp_ptr(rA(ctx->opcode)); \ rb = gen_fprp_ptr(rB(ctx->opcode)); \ @@ -137,7 +131,6 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ - gen_update_nip(ctx, ctx->base.pc_next - 4); \ rt = gen_fprp_ptr(rD(ctx->opcode)); \ rb = gen_fprp_ptr(rB(ctx->opcode)); \ gen_helper_##name(cpu_env, rt, rb); \ @@ -157,7 +150,6 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ - gen_update_nip(ctx, ctx->base.pc_next - 4); \ rt = gen_fprp_ptr(rD(ctx->opcode)); \ rs = gen_fprp_ptr(fprfld(ctx->opcode)); \ i32 = tcg_const_i32(i32fld(ctx->opcode)); \ From 87bc8e52b146706ab55b986bf27daba16cb7fbb8 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:11 -0300 Subject: [PATCH 1267/1334] target/ppc: Move dtstdc[q]/dtstdg[q] to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the following instructions to decodetree: dtstdc: DFP Test Data Class dtstdcq: DFP Test Data Class Quad dtstdg: DFP Test Data Group dtstdgq: DFP Test Data Group Quad Signed-off-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-10-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 8 +++---- target/ppc/helper.h | 8 +++---- target/ppc/insn32.decode | 14 ++++++++++++ target/ppc/translate/dfp-impl.c.inc | 35 +++++++++++++---------------- target/ppc/translate/dfp-ops.c.inc | 10 --------- 5 files changed, 37 insertions(+), 38 deletions(-) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index 6ab46d7db5..f3c1e525a3 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -546,8 +546,8 @@ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, uint32_t dcm) \ return dfp.crbf; \ } -DFP_HELPER_TSTDC(dtstdc, 64) -DFP_HELPER_TSTDC(dtstdcq, 128) +DFP_HELPER_TSTDC(DTSTDC, 64) +DFP_HELPER_TSTDC(DTSTDCQ, 128) #define DFP_HELPER_TSTDG(op, size) \ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, uint32_t dcm) \ @@ -601,8 +601,8 @@ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, uint32_t dcm) \ return dfp.crbf; \ } -DFP_HELPER_TSTDG(dtstdg, 64) -DFP_HELPER_TSTDG(dtstdgq, 128) +DFP_HELPER_TSTDG(DTSTDG, 64) +DFP_HELPER_TSTDG(DTSTDGQ, 128) #define DFP_HELPER_TSTEX(op, size) \ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, ppc_fprp_t *b) \ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 3812bb0270..2f11411e14 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -710,10 +710,10 @@ DEF_HELPER_3(dcmpo, i32, env, fprp, fprp) DEF_HELPER_3(dcmpoq, i32, env, fprp, fprp) DEF_HELPER_3(dcmpu, i32, env, fprp, fprp) DEF_HELPER_3(dcmpuq, i32, env, fprp, fprp) -DEF_HELPER_3(dtstdc, i32, env, fprp, i32) -DEF_HELPER_3(dtstdcq, i32, env, fprp, i32) -DEF_HELPER_3(dtstdg, i32, env, fprp, i32) -DEF_HELPER_3(dtstdgq, i32, env, fprp, i32) +DEF_HELPER_3(DTSTDC, i32, env, fprp, i32) +DEF_HELPER_3(DTSTDCQ, i32, env, fprp, i32) +DEF_HELPER_3(DTSTDG, i32, env, fprp, i32) +DEF_HELPER_3(DTSTDGQ, i32, env, fprp, i32) DEF_HELPER_3(dtstex, i32, env, fprp, fprp) DEF_HELPER_3(dtstexq, i32, env, fprp, fprp) DEF_HELPER_3(dtstsf, i32, env, fprp, fprp) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 33463c14ea..49bdd3531e 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -58,6 +58,12 @@ %x_frbp 12:4 !function=times_2 @X_vrt_frbp ...... vrt:5 ..... ....0 .......... . &X_vrt_frbp frbp=%x_frbp +&Z22_bf_fra bf fra dm +@Z22_bf_fra ...... bf:3 .. fra:5 dm:6 ......... . &Z22_bf_fra + +%z22_frap 17:4 !function=times_2 +@Z22_bf_frap ...... bf:3 .. ....0 dm:6 ......... . &Z22_bf_fra fra=%z22_frap + ### Fixed-Point Load Instructions LBZ 100010 ..... ..... ................ @D @@ -168,6 +174,14 @@ SETBCR 011111 ..... ..... ----- 0110100000 - @X_bi SETNBC 011111 ..... ..... ----- 0111000000 - @X_bi SETNBCR 011111 ..... ..... ----- 0111100000 - @X_bi +### Decimal Floating-Point Test Instructions + +DTSTDC 111011 ... -- ..... ...... 011000010 - @Z22_bf_fra +DTSTDCQ 111111 ... -- ..... ...... 011000010 - @Z22_bf_frap + +DTSTDG 111011 ... -- ..... ...... 011100010 - @Z22_bf_fra +DTSTDGQ 111111 ... -- ..... ...... 011100010 - @Z22_bf_frap + ### Decimal Floating-Point Conversion Instructions DCFFIXQQ 111111 ..... 00000 ..... 1111100010 - @X_frtp_vrb diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index 1431d955c6..1a30c51467 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -60,21 +60,17 @@ static void gen_##name(DisasContext *ctx) \ tcg_temp_free_ptr(rb); \ } -#define GEN_DFP_BF_A_DCM(name) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr ra; \ - TCGv_i32 dcm; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - ra = gen_fprp_ptr(rA(ctx->opcode)); \ - dcm = tcg_const_i32(DCM(ctx->opcode)); \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ - cpu_env, ra, dcm); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_i32(dcm); \ +#define TRANS_DFP_BF_A_DCM(NAME) \ +static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ +{ \ + TCGv_ptr ra; \ + REQUIRE_INSNS_FLAGS2(ctx, DFP); \ + REQUIRE_FPU(ctx); \ + ra = gen_fprp_ptr(a->fra); \ + gen_helper_##NAME(cpu_crf[a->bf], \ + cpu_env, ra, tcg_constant_i32(a->dm)); \ + tcg_temp_free_ptr(ra); \ + return true; \ } #define GEN_DFP_T_B_U32_U32_Rc(name, u32f1, u32f2) \ @@ -174,10 +170,10 @@ GEN_DFP_BF_A_B(dcmpu) GEN_DFP_BF_A_B(dcmpuq) GEN_DFP_BF_A_B(dcmpo) GEN_DFP_BF_A_B(dcmpoq) -GEN_DFP_BF_A_DCM(dtstdc) -GEN_DFP_BF_A_DCM(dtstdcq) -GEN_DFP_BF_A_DCM(dtstdg) -GEN_DFP_BF_A_DCM(dtstdgq) +TRANS_DFP_BF_A_DCM(DTSTDC) +TRANS_DFP_BF_A_DCM(DTSTDCQ) +TRANS_DFP_BF_A_DCM(DTSTDG) +TRANS_DFP_BF_A_DCM(DTSTDGQ) GEN_DFP_BF_A_B(dtstex) GEN_DFP_BF_A_B(dtstexq) GEN_DFP_BF_A_B(dtstsf) @@ -217,7 +213,6 @@ GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) #undef GEN_DFP_T_A_B_Rc #undef GEN_DFP_BF_A_B -#undef GEN_DFP_BF_A_DCM #undef GEN_DFP_T_B_U32_U32_Rc #undef GEN_DFP_T_A_B_I32_Rc #undef GEN_DFP_T_B_Rc diff --git a/target/ppc/translate/dfp-ops.c.inc b/target/ppc/translate/dfp-ops.c.inc index 6ef38e5712..e1df98d52e 100644 --- a/target/ppc/translate/dfp-ops.c.inc +++ b/target/ppc/translate/dfp-ops.c.inc @@ -66,12 +66,6 @@ _GEN_DFP_QUAD(name, op1, op2, 0x00600801) #define GEN_DFP_BF_A_Bp_300(name, op1, op2) \ _GEN_DFP_QUAD_300(name, op1, op2, 0x00400001) -#define GEN_DFP_BF_A_DCM(name, op1, op2) \ -_GEN_DFP_LONGx2(name, op1, op2, 0x00600001) - -#define GEN_DFP_BF_Ap_DCM(name, op1, op2) \ -_GEN_DFP_QUADx2(name, op1, op2, 0x00610001) - #define GEN_DFP_T_A_B_RMC_Rc(name, op1, op2) \ _GEN_DFP_LONGx4(name, op1, op2, 0x00000000) @@ -123,10 +117,6 @@ GEN_DFP_BF_A_B(dcmpu, 0x02, 0x14), GEN_DFP_BF_Ap_Bp(dcmpuq, 0x02, 0x14), GEN_DFP_BF_A_B(dcmpo, 0x02, 0x04), GEN_DFP_BF_Ap_Bp(dcmpoq, 0x02, 0x04), -GEN_DFP_BF_A_DCM(dtstdc, 0x02, 0x06), -GEN_DFP_BF_Ap_DCM(dtstdcq, 0x02, 0x06), -GEN_DFP_BF_A_DCM(dtstdg, 0x02, 0x07), -GEN_DFP_BF_Ap_DCM(dtstdgq, 0x02, 0x07), GEN_DFP_BF_A_B(dtstex, 0x02, 0x05), GEN_DFP_BF_Ap_Bp(dtstexq, 0x02, 0x05), GEN_DFP_BF_A_B(dtstsf, 0x02, 0x15), From afdc9310134a0aab6966b895bd560b64f46a21ca Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:12 -0300 Subject: [PATCH 1268/1334] target/ppc: Move d{add,sub,mul,div,iex}[q] to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the following instructions to decodetree: dadd: DFP Add daddq: DFP Add Quad dsub: DFP Subtract dsubq: DFP Subtract Quad dmul: DFP Multiply dmulq: DFP Multiply Quad ddiv: DFP Divide ddivq: DFP Divide Quad diex: DFP Insert Biased Exponent diexq: DFP Insert Biased Exponent Quad Signed-off-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-11-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 20 +++++------ target/ppc/helper.h | 20 +++++------ target/ppc/insn32.decode | 31 ++++++++++++++-- target/ppc/translate/dfp-impl.c.inc | 56 ++++++++++++++--------------- target/ppc/translate/dfp-ops.c.inc | 19 ---------- 5 files changed, 76 insertions(+), 70 deletions(-) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index f3c1e525a3..da8eaaaff1 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -445,8 +445,8 @@ static void ADD_PPs(struct PPC_DFP *dfp) dfp_check_for_VXISI_add(dfp); } -DFP_HELPER_TAB(dadd, decNumberAdd, ADD_PPs, 64) -DFP_HELPER_TAB(daddq, decNumberAdd, ADD_PPs, 128) +DFP_HELPER_TAB(DADD, decNumberAdd, ADD_PPs, 64) +DFP_HELPER_TAB(DADDQ, decNumberAdd, ADD_PPs, 128) static void SUB_PPs(struct PPC_DFP *dfp) { @@ -458,8 +458,8 @@ static void SUB_PPs(struct PPC_DFP *dfp) dfp_check_for_VXISI_subtract(dfp); } -DFP_HELPER_TAB(dsub, decNumberSubtract, SUB_PPs, 64) -DFP_HELPER_TAB(dsubq, decNumberSubtract, SUB_PPs, 128) +DFP_HELPER_TAB(DSUB, decNumberSubtract, SUB_PPs, 64) +DFP_HELPER_TAB(DSUBQ, decNumberSubtract, SUB_PPs, 128) static void MUL_PPs(struct PPC_DFP *dfp) { @@ -471,8 +471,8 @@ static void MUL_PPs(struct PPC_DFP *dfp) dfp_check_for_VXIMZ(dfp); } -DFP_HELPER_TAB(dmul, decNumberMultiply, MUL_PPs, 64) -DFP_HELPER_TAB(dmulq, decNumberMultiply, MUL_PPs, 128) +DFP_HELPER_TAB(DMUL, decNumberMultiply, MUL_PPs, 64) +DFP_HELPER_TAB(DMULQ, decNumberMultiply, MUL_PPs, 128) static void DIV_PPs(struct PPC_DFP *dfp) { @@ -486,8 +486,8 @@ static void DIV_PPs(struct PPC_DFP *dfp) dfp_check_for_VXIDI(dfp); } -DFP_HELPER_TAB(ddiv, decNumberDivide, DIV_PPs, 64) -DFP_HELPER_TAB(ddivq, decNumberDivide, DIV_PPs, 128) +DFP_HELPER_TAB(DDIV, decNumberDivide, DIV_PPs, 64) +DFP_HELPER_TAB(DDIVQ, decNumberDivide, DIV_PPs, 128) #define DFP_HELPER_BF_AB(op, dnop, postprocs, size) \ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, ppc_fprp_t *b) \ @@ -1299,8 +1299,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a, \ set_dfp##size(t, &dfp.vt); \ } -DFP_HELPER_IEX(diex, 64) -DFP_HELPER_IEX(diexq, 128) +DFP_HELPER_IEX(DIEX, 64) +DFP_HELPER_IEX(DIEXQ, 128) static void dfp_clear_lmd_from_g5msb(uint64_t *t) { diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 2f11411e14..f802392614 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -698,14 +698,14 @@ DEF_HELPER_3(store_601_batu, void, env, i32, tl) #define dh_alias_fprp ptr #define dh_ctype_fprp ppc_fprp_t * -DEF_HELPER_4(dadd, void, env, fprp, fprp, fprp) -DEF_HELPER_4(daddq, void, env, fprp, fprp, fprp) -DEF_HELPER_4(dsub, void, env, fprp, fprp, fprp) -DEF_HELPER_4(dsubq, void, env, fprp, fprp, fprp) -DEF_HELPER_4(dmul, void, env, fprp, fprp, fprp) -DEF_HELPER_4(dmulq, void, env, fprp, fprp, fprp) -DEF_HELPER_4(ddiv, void, env, fprp, fprp, fprp) -DEF_HELPER_4(ddivq, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DADD, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DADDQ, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DSUB, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DSUBQ, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DMUL, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DMULQ, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DDIV, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DDIVQ, void, env, fprp, fprp, fprp) DEF_HELPER_3(dcmpo, i32, env, fprp, fprp) DEF_HELPER_3(dcmpoq, i32, env, fprp, fprp) DEF_HELPER_3(dcmpu, i32, env, fprp, fprp) @@ -746,8 +746,8 @@ DEF_HELPER_4(denbcd, void, env, fprp, fprp, i32) DEF_HELPER_4(denbcdq, void, env, fprp, fprp, i32) DEF_HELPER_3(dxex, void, env, fprp, fprp) DEF_HELPER_3(dxexq, void, env, fprp, fprp) -DEF_HELPER_4(diex, void, env, fprp, fprp, fprp) -DEF_HELPER_4(diexq, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DIEX, void, env, fprp, fprp, fprp) +DEF_HELPER_4(DIEXQ, void, env, fprp, fprp, fprp) DEF_HELPER_4(dscri, void, env, fprp, fprp, i32) DEF_HELPER_4(dscriq, void, env, fprp, fprp, i32) DEF_HELPER_4(dscli, void, env, fprp, fprp, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 49bdd3531e..bb3322dc00 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -44,6 +44,16 @@ &X rt ra rb @X ...... rt:5 ra:5 rb:5 .......... . &X +&X_rc rt ra rb rc:bool +@X_rc ...... rt:5 ra:5 rb:5 .......... rc:1 &X_rc + +%x_frtp 22:4 !function=times_2 +%x_frap 17:4 !function=times_2 +%x_frbp 12:4 !function=times_2 +@X_tp_ap_bp_rc ...... ....0 ....0 ....0 .......... rc:1 &X_rc rt=%x_frtp ra=%x_frap rb=%x_frbp + +@X_tp_a_bp_rc ...... ....0 ra:5 ....0 .......... rc:1 &X_rc rt=%x_frtp rb=%x_frbp + &X_bi rt bi @X_bi ...... rt:5 bi:5 ----- .......... - &X_bi @@ -51,11 +61,9 @@ @X_bfl ...... bf:3 - l:1 ra:5 rb:5 ..........- &X_bfl &X_frtp_vrb frtp vrb -%x_frtp 22:4 !function=times_2 @X_frtp_vrb ...... ....0 ..... vrb:5 .......... . &X_frtp_vrb frtp=%x_frtp &X_vrt_frbp vrt frbp -%x_frbp 12:4 !function=times_2 @X_vrt_frbp ...... vrt:5 ..... ....0 .......... . &X_vrt_frbp frbp=%x_frbp &Z22_bf_fra bf fra dm @@ -174,6 +182,20 @@ SETBCR 011111 ..... ..... ----- 0110100000 - @X_bi SETNBC 011111 ..... ..... ----- 0111000000 - @X_bi SETNBCR 011111 ..... ..... ----- 0111100000 - @X_bi +### Decimal Floating-Point Arithmetic Instructions + +DADD 111011 ..... ..... ..... 0000000010 . @X_rc +DADDQ 111111 ..... ..... ..... 0000000010 . @X_tp_ap_bp_rc + +DSUB 111011 ..... ..... ..... 1000000010 . @X_rc +DSUBQ 111111 ..... ..... ..... 1000000010 . @X_tp_ap_bp_rc + +DMUL 111011 ..... ..... ..... 0000100010 . @X_rc +DMULQ 111111 ..... ..... ..... 0000100010 . @X_tp_ap_bp_rc + +DDIV 111011 ..... ..... ..... 1000100010 . @X_rc +DDIVQ 111111 ..... ..... ..... 1000100010 . @X_tp_ap_bp_rc + ### Decimal Floating-Point Test Instructions DTSTDC 111011 ... -- ..... ...... 011000010 - @Z22_bf_fra @@ -187,6 +209,11 @@ DTSTDGQ 111111 ... -- ..... ...... 011100010 - @Z22_bf_frap DCFFIXQQ 111111 ..... 00000 ..... 1111100010 - @X_frtp_vrb DCTFIXQQ 111111 ..... 00001 ..... 1111100010 - @X_vrt_frbp +### Decimal Floating-Point Format Instructions + +DIEX 111011 ..... ..... ..... 1101100010 . @X_rc +DIEXQ 111111 ..... ..... ..... 1101100010 . @X_tp_a_bp_rc + ## Vector Bit Manipulation Instruction VCFUGED 000100 ..... ..... ..... 10101001101 @VX diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index 1a30c51467..b08b38355b 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -7,24 +7,23 @@ static inline TCGv_ptr gen_fprp_ptr(int reg) return r; } -#define GEN_DFP_T_A_B_Rc(name) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr rd, ra, rb; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - rd = gen_fprp_ptr(rD(ctx->opcode)); \ - ra = gen_fprp_ptr(rA(ctx->opcode)); \ - rb = gen_fprp_ptr(rB(ctx->opcode)); \ - gen_helper_##name(cpu_env, rd, ra, rb); \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ - tcg_temp_free_ptr(rd); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ +#define TRANS_DFP_T_A_B_Rc(NAME) \ +static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ +{ \ + TCGv_ptr rt, ra, rb; \ + REQUIRE_INSNS_FLAGS2(ctx, DFP); \ + REQUIRE_FPU(ctx); \ + rt = gen_fprp_ptr(a->rt); \ + ra = gen_fprp_ptr(a->ra); \ + rb = gen_fprp_ptr(a->rb); \ + gen_helper_##NAME(cpu_env, rt, ra, rb); \ + if (unlikely(a->rc)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + return true; \ } #define GEN_DFP_BF_A_B(name) \ @@ -158,14 +157,14 @@ static void gen_##name(DisasContext *ctx) \ tcg_temp_free_i32(i32); \ } -GEN_DFP_T_A_B_Rc(dadd) -GEN_DFP_T_A_B_Rc(daddq) -GEN_DFP_T_A_B_Rc(dsub) -GEN_DFP_T_A_B_Rc(dsubq) -GEN_DFP_T_A_B_Rc(dmul) -GEN_DFP_T_A_B_Rc(dmulq) -GEN_DFP_T_A_B_Rc(ddiv) -GEN_DFP_T_A_B_Rc(ddivq) +TRANS_DFP_T_A_B_Rc(DADD) +TRANS_DFP_T_A_B_Rc(DADDQ) +TRANS_DFP_T_A_B_Rc(DSUB) +TRANS_DFP_T_A_B_Rc(DSUBQ) +TRANS_DFP_T_A_B_Rc(DMUL) +TRANS_DFP_T_A_B_Rc(DMULQ) +TRANS_DFP_T_A_B_Rc(DDIV) +TRANS_DFP_T_A_B_Rc(DDIVQ) GEN_DFP_BF_A_B(dcmpu) GEN_DFP_BF_A_B(dcmpuq) GEN_DFP_BF_A_B(dcmpo) @@ -204,14 +203,13 @@ GEN_DFP_T_FPR_I32_Rc(denbcd, rB, SP) GEN_DFP_T_FPR_I32_Rc(denbcdq, rB, SP) GEN_DFP_T_B_Rc(dxex) GEN_DFP_T_B_Rc(dxexq) -GEN_DFP_T_A_B_Rc(diex) -GEN_DFP_T_A_B_Rc(diexq) +TRANS_DFP_T_A_B_Rc(DIEX) +TRANS_DFP_T_A_B_Rc(DIEXQ) GEN_DFP_T_FPR_I32_Rc(dscli, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscliq, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscri, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) -#undef GEN_DFP_T_A_B_Rc #undef GEN_DFP_BF_A_B #undef GEN_DFP_T_B_U32_U32_Rc #undef GEN_DFP_T_A_B_I32_Rc diff --git a/target/ppc/translate/dfp-ops.c.inc b/target/ppc/translate/dfp-ops.c.inc index e1df98d52e..38ea551488 100644 --- a/target/ppc/translate/dfp-ops.c.inc +++ b/target/ppc/translate/dfp-ops.c.inc @@ -30,15 +30,6 @@ GEN_HANDLER_E(name, 0x3F, op1, 0x08 | op2, mask, PPC_NONE, PPC2_DFP), \ GEN_HANDLER_E(name, 0x3F, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP), \ GEN_HANDLER_E(name, 0x3F, op1, 0x18 | op2, mask, PPC_NONE, PPC2_DFP) -#define GEN_DFP_T_A_B_Rc(name, op1, op2) \ -_GEN_DFP_LONG(name, op1, op2, 0x00000000) - -#define GEN_DFP_Tp_Ap_Bp_Rc(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x00210800) - -#define GEN_DFP_Tp_A_Bp_Rc(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x00200800) - #define GEN_DFP_T_B_Rc(name, op1, op2) \ _GEN_DFP_LONG(name, op1, op2, 0x001F0000) @@ -105,14 +96,6 @@ _GEN_DFP_LONGx2(name, op1, op2, 0x00000000) #define GEN_DFP_Tp_Ap_SH_Rc(name, op1, op2) \ _GEN_DFP_QUADx2(name, op1, op2, 0x00210000) -GEN_DFP_T_A_B_Rc(dadd, 0x02, 0x00), -GEN_DFP_Tp_Ap_Bp_Rc(daddq, 0x02, 0x00), -GEN_DFP_T_A_B_Rc(dsub, 0x02, 0x10), -GEN_DFP_Tp_Ap_Bp_Rc(dsubq, 0x02, 0x10), -GEN_DFP_T_A_B_Rc(dmul, 0x02, 0x01), -GEN_DFP_Tp_Ap_Bp_Rc(dmulq, 0x02, 0x01), -GEN_DFP_T_A_B_Rc(ddiv, 0x02, 0x11), -GEN_DFP_Tp_Ap_Bp_Rc(ddivq, 0x02, 0x11), GEN_DFP_BF_A_B(dcmpu, 0x02, 0x14), GEN_DFP_BF_Ap_Bp(dcmpuq, 0x02, 0x14), GEN_DFP_BF_A_B(dcmpo, 0x02, 0x04), @@ -147,8 +130,6 @@ GEN_DFP_S_T_B_Rc(denbcd, 0x02, 0x1a), GEN_DFP_S_Tp_Bp_Rc(denbcdq, 0x02, 0x1a), GEN_DFP_T_B_Rc(dxex, 0x02, 0x0b), GEN_DFP_T_Bp_Rc(dxexq, 0x02, 0x0b), -GEN_DFP_T_A_B_Rc(diex, 0x02, 0x1b), -GEN_DFP_Tp_A_Bp_Rc(diexq, 0x02, 0x1b), GEN_DFP_T_A_SH_Rc(dscli, 0x02, 0x02), GEN_DFP_Tp_Ap_SH_Rc(dscliq, 0x02, 0x02), GEN_DFP_T_A_SH_Rc(dscri, 0x02, 0x03), From 85c38a460c4034b2f0aab0e4a19ec22180a7433e Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:13 -0300 Subject: [PATCH 1269/1334] target/ppc: Move dcmp{u,o}[q],dts{tex,tsf,tsfi}[q] to decodetree Move the following instructions to decodetree: dcmpu: DFP Compare Unordered dcmpuq: DFP Compare Unordered Quad dcmpo: DFP Compare Ordered dcmpoq: DFP Compare Ordered Quad dtstex: DFP Test Exponent dtstexq: DFP Test Exponent Quad dtstsf: DFP Test Significance dtstsfq: DFP Test Significance Quad dtstsfi: DFP Test Significance Immediate dtstsfiq: DFP Test Significance Immediate Quad Signed-off-by: Luis Pires Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-12-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 20 ++++---- target/ppc/helper.h | 20 ++++---- target/ppc/insn32.decode | 29 +++++++++++ target/ppc/translate/dfp-impl.c.inc | 74 +++++++++++++---------------- target/ppc/translate/dfp-ops.c.inc | 31 ------------ 5 files changed, 83 insertions(+), 91 deletions(-) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index da8eaaaff1..9be6809b33 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -507,8 +507,8 @@ static void CMPU_PPs(struct PPC_DFP *dfp) dfp_check_for_VXSNAN(dfp); } -DFP_HELPER_BF_AB(dcmpu, decNumberCompare, CMPU_PPs, 64) -DFP_HELPER_BF_AB(dcmpuq, decNumberCompare, CMPU_PPs, 128) +DFP_HELPER_BF_AB(DCMPU, decNumberCompare, CMPU_PPs, 64) +DFP_HELPER_BF_AB(DCMPUQ, decNumberCompare, CMPU_PPs, 128) static void CMPO_PPs(struct PPC_DFP *dfp) { @@ -518,8 +518,8 @@ static void CMPO_PPs(struct PPC_DFP *dfp) dfp_check_for_VXVC(dfp); } -DFP_HELPER_BF_AB(dcmpo, decNumberCompare, CMPO_PPs, 64) -DFP_HELPER_BF_AB(dcmpoq, decNumberCompare, CMPO_PPs, 128) +DFP_HELPER_BF_AB(DCMPO, decNumberCompare, CMPO_PPs, 64) +DFP_HELPER_BF_AB(DCMPOQ, decNumberCompare, CMPO_PPs, 128) #define DFP_HELPER_TSTDC(op, size) \ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, uint32_t dcm) \ @@ -633,8 +633,8 @@ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, ppc_fprp_t *b) \ return dfp.crbf; \ } -DFP_HELPER_TSTEX(dtstex, 64) -DFP_HELPER_TSTEX(dtstexq, 128) +DFP_HELPER_TSTEX(DTSTEX, 64) +DFP_HELPER_TSTEX(DTSTEXQ, 128) #define DFP_HELPER_TSTSF(op, size) \ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, ppc_fprp_t *b) \ @@ -670,8 +670,8 @@ uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, ppc_fprp_t *b) \ return dfp.crbf; \ } -DFP_HELPER_TSTSF(dtstsf, 64) -DFP_HELPER_TSTSF(dtstsfq, 128) +DFP_HELPER_TSTSF(DTSTSF, 64) +DFP_HELPER_TSTSF(DTSTSFQ, 128) #define DFP_HELPER_TSTSFI(op, size) \ uint32_t helper_##op(CPUPPCState *env, uint32_t a, ppc_fprp_t *b) \ @@ -705,8 +705,8 @@ uint32_t helper_##op(CPUPPCState *env, uint32_t a, ppc_fprp_t *b) \ return dfp.crbf; \ } -DFP_HELPER_TSTSFI(dtstsfi, 64) -DFP_HELPER_TSTSFI(dtstsfiq, 128) +DFP_HELPER_TSTSFI(DTSTSFI, 64) +DFP_HELPER_TSTSFI(DTSTSFIQ, 128) static void QUA_PPs(struct PPC_DFP *dfp) { diff --git a/target/ppc/helper.h b/target/ppc/helper.h index f802392614..a21c04ecf1 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -706,20 +706,20 @@ DEF_HELPER_4(DMUL, void, env, fprp, fprp, fprp) DEF_HELPER_4(DMULQ, void, env, fprp, fprp, fprp) DEF_HELPER_4(DDIV, void, env, fprp, fprp, fprp) DEF_HELPER_4(DDIVQ, void, env, fprp, fprp, fprp) -DEF_HELPER_3(dcmpo, i32, env, fprp, fprp) -DEF_HELPER_3(dcmpoq, i32, env, fprp, fprp) -DEF_HELPER_3(dcmpu, i32, env, fprp, fprp) -DEF_HELPER_3(dcmpuq, i32, env, fprp, fprp) +DEF_HELPER_3(DCMPO, i32, env, fprp, fprp) +DEF_HELPER_3(DCMPOQ, i32, env, fprp, fprp) +DEF_HELPER_3(DCMPU, i32, env, fprp, fprp) +DEF_HELPER_3(DCMPUQ, i32, env, fprp, fprp) DEF_HELPER_3(DTSTDC, i32, env, fprp, i32) DEF_HELPER_3(DTSTDCQ, i32, env, fprp, i32) DEF_HELPER_3(DTSTDG, i32, env, fprp, i32) DEF_HELPER_3(DTSTDGQ, i32, env, fprp, i32) -DEF_HELPER_3(dtstex, i32, env, fprp, fprp) -DEF_HELPER_3(dtstexq, i32, env, fprp, fprp) -DEF_HELPER_3(dtstsf, i32, env, fprp, fprp) -DEF_HELPER_3(dtstsfq, i32, env, fprp, fprp) -DEF_HELPER_3(dtstsfi, i32, env, i32, fprp) -DEF_HELPER_3(dtstsfiq, i32, env, i32, fprp) +DEF_HELPER_3(DTSTEX, i32, env, fprp, fprp) +DEF_HELPER_3(DTSTEXQ, i32, env, fprp, fprp) +DEF_HELPER_3(DTSTSF, i32, env, fprp, fprp) +DEF_HELPER_3(DTSTSFQ, i32, env, fprp, fprp) +DEF_HELPER_3(DTSTSFI, i32, env, i32, fprp) +DEF_HELPER_3(DTSTSFIQ, i32, env, i32, fprp) DEF_HELPER_5(dquai, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(dquaiq, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(dqua, void, env, fprp, fprp, fprp, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index bb3322dc00..263911d358 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -57,6 +57,18 @@ &X_bi rt bi @X_bi ...... rt:5 bi:5 ----- .......... - &X_bi +&X_bf bf ra rb +@X_bf ...... bf:3 .. ra:5 rb:5 .......... . &X_bf + +@X_bf_ap_bp ...... bf:3 .. ....0 ....0 .......... . &X_bf ra=%x_frap rb=%x_frbp + +@X_bf_a_bp ...... bf:3 .. ra:5 ....0 .......... . &X_bf rb=%x_frbp + +&X_bf_uim bf uim rb +@X_bf_uim ...... bf:3 . uim:6 rb:5 .......... . &X_bf_uim + +@X_bf_uim_bp ...... bf:3 . uim:6 ....0 .......... . &X_bf_uim rb=%x_frbp + &X_bfl bf l:bool ra rb @X_bfl ...... bf:3 - l:1 ra:5 rb:5 ..........- &X_bfl @@ -196,6 +208,14 @@ DMULQ 111111 ..... ..... ..... 0000100010 . @X_tp_ap_bp_rc DDIV 111011 ..... ..... ..... 1000100010 . @X_rc DDIVQ 111111 ..... ..... ..... 1000100010 . @X_tp_ap_bp_rc +### Decimal Floating-Point Compare Instructions + +DCMPU 111011 ... -- ..... ..... 1010000010 - @X_bf +DCMPUQ 111111 ... -- ..... ..... 1010000010 - @X_bf_ap_bp + +DCMPO 111011 ... -- ..... ..... 0010000010 - @X_bf +DCMPOQ 111111 ... -- ..... ..... 0010000010 - @X_bf_ap_bp + ### Decimal Floating-Point Test Instructions DTSTDC 111011 ... -- ..... ...... 011000010 - @Z22_bf_fra @@ -204,6 +224,15 @@ DTSTDCQ 111111 ... -- ..... ...... 011000010 - @Z22_bf_frap DTSTDG 111011 ... -- ..... ...... 011100010 - @Z22_bf_fra DTSTDGQ 111111 ... -- ..... ...... 011100010 - @Z22_bf_frap +DTSTEX 111011 ... -- ..... ..... 0010100010 - @X_bf +DTSTEXQ 111111 ... -- ..... ..... 0010100010 - @X_bf_ap_bp + +DTSTSF 111011 ... -- ..... ..... 1010100010 - @X_bf +DTSTSFQ 111111 ... -- ..... ..... 1010100010 - @X_bf_a_bp + +DTSTSFI 111011 ... - ...... ..... 1010100011 - @X_bf_uim +DTSTSFIQ 111111 ... - ...... ..... 1010100011 - @X_bf_uim_bp + ### Decimal Floating-Point Conversion Instructions DCFFIXQQ 111111 ..... 00000 ..... 1111100010 - @X_frtp_vrb diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index b08b38355b..d8dcc4c807 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -26,37 +26,32 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ return true; \ } -#define GEN_DFP_BF_A_B(name) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr ra, rb; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - ra = gen_fprp_ptr(rA(ctx->opcode)); \ - rb = gen_fprp_ptr(rB(ctx->opcode)); \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ - cpu_env, ra, rb); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ +#define TRANS_DFP_BF_A_B(NAME) \ +static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ +{ \ + TCGv_ptr ra, rb; \ + REQUIRE_INSNS_FLAGS2(ctx, DFP); \ + REQUIRE_FPU(ctx); \ + ra = gen_fprp_ptr(a->ra); \ + rb = gen_fprp_ptr(a->rb); \ + gen_helper_##NAME(cpu_crf[a->bf], \ + cpu_env, ra, rb); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + return true; \ } -#define GEN_DFP_BF_I_B(name) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_i32 uim; \ - TCGv_ptr rb; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - uim = tcg_const_i32(UIMM5(ctx->opcode)); \ - rb = gen_fprp_ptr(rB(ctx->opcode)); \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ - cpu_env, uim, rb); \ - tcg_temp_free_i32(uim); \ - tcg_temp_free_ptr(rb); \ +#define TRANS_DFP_BF_I_B(NAME) \ +static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ +{ \ + TCGv_ptr rb; \ + REQUIRE_INSNS_FLAGS2(ctx, DFP); \ + REQUIRE_FPU(ctx); \ + rb = gen_fprp_ptr(a->rb); \ + gen_helper_##NAME(cpu_crf[a->bf], \ + cpu_env, tcg_constant_i32(a->uim), rb);\ + tcg_temp_free_ptr(rb); \ + return true; \ } #define TRANS_DFP_BF_A_DCM(NAME) \ @@ -165,20 +160,20 @@ TRANS_DFP_T_A_B_Rc(DMUL) TRANS_DFP_T_A_B_Rc(DMULQ) TRANS_DFP_T_A_B_Rc(DDIV) TRANS_DFP_T_A_B_Rc(DDIVQ) -GEN_DFP_BF_A_B(dcmpu) -GEN_DFP_BF_A_B(dcmpuq) -GEN_DFP_BF_A_B(dcmpo) -GEN_DFP_BF_A_B(dcmpoq) +TRANS_DFP_BF_A_B(DCMPU) +TRANS_DFP_BF_A_B(DCMPUQ) +TRANS_DFP_BF_A_B(DCMPO) +TRANS_DFP_BF_A_B(DCMPOQ) TRANS_DFP_BF_A_DCM(DTSTDC) TRANS_DFP_BF_A_DCM(DTSTDCQ) TRANS_DFP_BF_A_DCM(DTSTDG) TRANS_DFP_BF_A_DCM(DTSTDGQ) -GEN_DFP_BF_A_B(dtstex) -GEN_DFP_BF_A_B(dtstexq) -GEN_DFP_BF_A_B(dtstsf) -GEN_DFP_BF_A_B(dtstsfq) -GEN_DFP_BF_I_B(dtstsfi) -GEN_DFP_BF_I_B(dtstsfiq) +TRANS_DFP_BF_A_B(DTSTEX) +TRANS_DFP_BF_A_B(DTSTEXQ) +TRANS_DFP_BF_A_B(DTSTSF) +TRANS_DFP_BF_A_B(DTSTSFQ) +TRANS_DFP_BF_I_B(DTSTSFI) +TRANS_DFP_BF_I_B(DTSTSFIQ) GEN_DFP_T_B_U32_U32_Rc(dquai, SIMM5, RMC) GEN_DFP_T_B_U32_U32_Rc(dquaiq, SIMM5, RMC) GEN_DFP_T_A_B_I32_Rc(dqua, RMC) @@ -210,7 +205,6 @@ GEN_DFP_T_FPR_I32_Rc(dscliq, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscri, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) -#undef GEN_DFP_BF_A_B #undef GEN_DFP_T_B_U32_U32_Rc #undef GEN_DFP_T_A_B_I32_Rc #undef GEN_DFP_T_B_Rc diff --git a/target/ppc/translate/dfp-ops.c.inc b/target/ppc/translate/dfp-ops.c.inc index 38ea551488..e59425c8b2 100644 --- a/target/ppc/translate/dfp-ops.c.inc +++ b/target/ppc/translate/dfp-ops.c.inc @@ -1,9 +1,6 @@ #define _GEN_DFP_LONG(name, op1, op2, mask) \ GEN_HANDLER_E(name, 0x3B, op1, op2, mask, PPC_NONE, PPC2_DFP) -#define _GEN_DFP_LONG_300(name, op1, op2, mask) \ -GEN_HANDLER_E(name, 0x3B, op1, op2, mask, PPC_NONE, PPC2_ISA300) - #define _GEN_DFP_LONGx2(name, op1, op2, mask) \ GEN_HANDLER_E(name, 0x3B, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ GEN_HANDLER_E(name, 0x3B, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) @@ -17,9 +14,6 @@ GEN_HANDLER_E(name, 0x3B, op1, 0x18 | op2, mask, PPC_NONE, PPC2_DFP) #define _GEN_DFP_QUAD(name, op1, op2, mask) \ GEN_HANDLER_E(name, 0x3F, op1, op2, mask, PPC_NONE, PPC2_DFP) -#define _GEN_DFP_QUAD_300(name, op1, op2, mask) \ -GEN_HANDLER_E(name, 0x3F, op1, op2, mask, PPC_NONE, PPC2_ISA300) - #define _GEN_DFP_QUADx2(name, op1, op2, mask) \ GEN_HANDLER_E(name, 0x3F, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ GEN_HANDLER_E(name, 0x3F, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) @@ -42,21 +36,6 @@ _GEN_DFP_QUAD(name, op1, op2, 0x003F0000) #define GEN_DFP_T_Bp_Rc(name, op1, op2) \ _GEN_DFP_QUAD(name, op1, op2, 0x001F0800) -#define GEN_DFP_BF_A_B(name, op1, op2) \ -_GEN_DFP_LONG(name, op1, op2, 0x00000001) - -#define GEN_DFP_BF_A_B_300(name, op1, op2) \ -_GEN_DFP_LONG_300(name, op1, op2, 0x00400001) - -#define GEN_DFP_BF_Ap_Bp(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x00610801) - -#define GEN_DFP_BF_A_Bp(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x00600801) - -#define GEN_DFP_BF_A_Bp_300(name, op1, op2) \ -_GEN_DFP_QUAD_300(name, op1, op2, 0x00400001) - #define GEN_DFP_T_A_B_RMC_Rc(name, op1, op2) \ _GEN_DFP_LONGx4(name, op1, op2, 0x00000000) @@ -96,16 +75,6 @@ _GEN_DFP_LONGx2(name, op1, op2, 0x00000000) #define GEN_DFP_Tp_Ap_SH_Rc(name, op1, op2) \ _GEN_DFP_QUADx2(name, op1, op2, 0x00210000) -GEN_DFP_BF_A_B(dcmpu, 0x02, 0x14), -GEN_DFP_BF_Ap_Bp(dcmpuq, 0x02, 0x14), -GEN_DFP_BF_A_B(dcmpo, 0x02, 0x04), -GEN_DFP_BF_Ap_Bp(dcmpoq, 0x02, 0x04), -GEN_DFP_BF_A_B(dtstex, 0x02, 0x05), -GEN_DFP_BF_Ap_Bp(dtstexq, 0x02, 0x05), -GEN_DFP_BF_A_B(dtstsf, 0x02, 0x15), -GEN_DFP_BF_A_Bp(dtstsfq, 0x02, 0x15), -GEN_DFP_BF_A_B_300(dtstsfi, 0x03, 0x15), -GEN_DFP_BF_A_Bp_300(dtstsfiq, 0x03, 0x15), GEN_DFP_TE_T_B_RMC_Rc(dquai, 0x03, 0x02), GEN_DFP_TE_Tp_Bp_RMC_Rc(dquaiq, 0x03, 0x02), GEN_DFP_T_A_B_RMC_Rc(dqua, 0x03, 0x00), From 78464edb8fe7b59b76303dc6666868a30c643d0e Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:14 -0300 Subject: [PATCH 1270/1334] target/ppc: Move dquai[q], drint{x,n}[q] to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the following instructions to decodetree: dquai: DFP Quantize Immediate dquaiq: DFP Quantize Immediate Quad drintx: DFP Round to FP Integer With Inexact drintxq: DFP Round to FP Integer With Inexact Quad drintn: DFP Round to FP Integer Without Inexact drintnq: DFP Round to FP Integer Without Inexact Quad Signed-off-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-13-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 12 +++---- target/ppc/helper.h | 12 +++---- target/ppc/insn32.decode | 23 +++++++++++++ target/ppc/translate/dfp-impl.c.inc | 51 +++++++++++++---------------- target/ppc/translate/dfp-ops.c.inc | 18 ---------- 5 files changed, 58 insertions(+), 58 deletions(-) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index 9be6809b33..df3e6c7cb1 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -751,8 +751,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b, \ set_dfp##size(t, &dfp.vt); \ } -DFP_HELPER_QUAI(dquai, 64) -DFP_HELPER_QUAI(dquaiq, 128) +DFP_HELPER_QUAI(DQUAI, 64) +DFP_HELPER_QUAI(DQUAIQ, 128) #define DFP_HELPER_QUA(op, size) \ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a, \ @@ -873,8 +873,8 @@ static void RINTX_PPs(struct PPC_DFP *dfp) dfp_check_for_VXSNAN(dfp); } -DFP_HELPER_RINT(drintx, RINTX_PPs, 64) -DFP_HELPER_RINT(drintxq, RINTX_PPs, 128) +DFP_HELPER_RINT(DRINTX, RINTX_PPs, 64) +DFP_HELPER_RINT(DRINTXQ, RINTX_PPs, 128) static void RINTN_PPs(struct PPC_DFP *dfp) { @@ -882,8 +882,8 @@ static void RINTN_PPs(struct PPC_DFP *dfp) dfp_check_for_VXSNAN(dfp); } -DFP_HELPER_RINT(drintn, RINTN_PPs, 64) -DFP_HELPER_RINT(drintnq, RINTN_PPs, 128) +DFP_HELPER_RINT(DRINTN, RINTN_PPs, 64) +DFP_HELPER_RINT(DRINTNQ, RINTN_PPs, 128) void helper_dctdp(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) { diff --git a/target/ppc/helper.h b/target/ppc/helper.h index a21c04ecf1..70bc8ac579 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -720,16 +720,16 @@ DEF_HELPER_3(DTSTSF, i32, env, fprp, fprp) DEF_HELPER_3(DTSTSFQ, i32, env, fprp, fprp) DEF_HELPER_3(DTSTSFI, i32, env, i32, fprp) DEF_HELPER_3(DTSTSFIQ, i32, env, i32, fprp) -DEF_HELPER_5(dquai, void, env, fprp, fprp, i32, i32) -DEF_HELPER_5(dquaiq, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(DQUAI, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(DQUAIQ, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(dqua, void, env, fprp, fprp, fprp, i32) DEF_HELPER_5(dquaq, void, env, fprp, fprp, fprp, i32) DEF_HELPER_5(drrnd, void, env, fprp, fprp, fprp, i32) DEF_HELPER_5(drrndq, void, env, fprp, fprp, fprp, i32) -DEF_HELPER_5(drintx, void, env, fprp, fprp, i32, i32) -DEF_HELPER_5(drintxq, void, env, fprp, fprp, i32, i32) -DEF_HELPER_5(drintn, void, env, fprp, fprp, i32, i32) -DEF_HELPER_5(drintnq, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(DRINTX, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(DRINTXQ, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(DRINTN, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(DRINTNQ, void, env, fprp, fprp, i32, i32) DEF_HELPER_3(dctdp, void, env, fprp, fprp) DEF_HELPER_3(dctqpq, void, env, fprp, fprp) DEF_HELPER_3(drsp, void, env, fprp, fprp) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 263911d358..5369af17b6 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -84,6 +84,18 @@ %z22_frap 17:4 !function=times_2 @Z22_bf_frap ...... bf:3 .. ....0 dm:6 ......... . &Z22_bf_fra fra=%z22_frap +&Z23_tb frt frb r:bool rmc rc:bool +@Z23_tb ...... frt:5 .... r:1 frb:5 rmc:2 ........ rc:1 &Z23_tb + +%z23_frtp 22:4 !function=times_2 +%z23_frbp 12:4 !function=times_2 +@Z23_tbp ...... ....0 .... r:1 ....0 rmc:2 ........ rc:1 &Z23_tb frt=%z23_frtp frb=%z23_frbp + +&Z23_te_tb te frt frb rmc rc:bool +@Z23_te_tb ...... frt:5 te:5 frb:5 rmc:2 ........ rc:1 &Z23_te_tb + +@Z23_te_tbp ...... ....0 te:5 ....0 rmc:2 ........ rc:1 &Z23_te_tb frt=%z23_frtp frb=%z23_frbp + ### Fixed-Point Load Instructions LBZ 100010 ..... ..... ................ @D @@ -233,6 +245,17 @@ DTSTSFQ 111111 ... -- ..... ..... 1010100010 - @X_bf_a_bp DTSTSFI 111011 ... - ...... ..... 1010100011 - @X_bf_uim DTSTSFIQ 111111 ... - ...... ..... 1010100011 - @X_bf_uim_bp +### Decimal Floating-Point Quantum Adjustment Instructions + +DQUAI 111011 ..... ..... ..... .. 01000011 . @Z23_te_tb +DQUAIQ 111111 ..... ..... ..... .. 01000011 . @Z23_te_tbp + +DRINTX 111011 ..... ---- . ..... .. 01100011 . @Z23_tb +DRINTXQ 111111 ..... ---- . ..... .. 01100011 . @Z23_tbp + +DRINTN 111011 ..... ---- . ..... .. 11100011 . @Z23_tb +DRINTNQ 111111 ..... ---- . ..... .. 11100011 . @Z23_tbp + ### Decimal Floating-Point Conversion Instructions DCFFIXQQ 111111 ..... 00000 ..... 1111100010 - @X_frtp_vrb diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index d8dcc4c807..cb481d028a 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -67,27 +67,23 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ return true; \ } -#define GEN_DFP_T_B_U32_U32_Rc(name, u32f1, u32f2) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr rt, rb; \ - TCGv_i32 u32_1, u32_2; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - rt = gen_fprp_ptr(rD(ctx->opcode)); \ - rb = gen_fprp_ptr(rB(ctx->opcode)); \ - u32_1 = tcg_const_i32(u32f1(ctx->opcode)); \ - u32_2 = tcg_const_i32(u32f2(ctx->opcode)); \ - gen_helper_##name(cpu_env, rt, rb, u32_1, u32_2); \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_i32(u32_1); \ - tcg_temp_free_i32(u32_2); \ +#define TRANS_DFP_T_B_U32_U32_Rc(NAME, U32F1, U32F2) \ +static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ +{ \ + TCGv_ptr rt, rb; \ + REQUIRE_INSNS_FLAGS2(ctx, DFP); \ + REQUIRE_FPU(ctx); \ + rt = gen_fprp_ptr(a->frt); \ + rb = gen_fprp_ptr(a->frb); \ + gen_helper_##NAME(cpu_env, rt, rb, \ + tcg_constant_i32(a->U32F1), \ + tcg_constant_i32(a->U32F2)); \ + if (unlikely(a->rc)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(rb); \ + return true; \ } #define GEN_DFP_T_A_B_I32_Rc(name, i32fld) \ @@ -174,16 +170,16 @@ TRANS_DFP_BF_A_B(DTSTSF) TRANS_DFP_BF_A_B(DTSTSFQ) TRANS_DFP_BF_I_B(DTSTSFI) TRANS_DFP_BF_I_B(DTSTSFIQ) -GEN_DFP_T_B_U32_U32_Rc(dquai, SIMM5, RMC) -GEN_DFP_T_B_U32_U32_Rc(dquaiq, SIMM5, RMC) +TRANS_DFP_T_B_U32_U32_Rc(DQUAI, te, rmc) +TRANS_DFP_T_B_U32_U32_Rc(DQUAIQ, te, rmc) GEN_DFP_T_A_B_I32_Rc(dqua, RMC) GEN_DFP_T_A_B_I32_Rc(dquaq, RMC) GEN_DFP_T_A_B_I32_Rc(drrnd, RMC) GEN_DFP_T_A_B_I32_Rc(drrndq, RMC) -GEN_DFP_T_B_U32_U32_Rc(drintx, FPW, RMC) -GEN_DFP_T_B_U32_U32_Rc(drintxq, FPW, RMC) -GEN_DFP_T_B_U32_U32_Rc(drintn, FPW, RMC) -GEN_DFP_T_B_U32_U32_Rc(drintnq, FPW, RMC) +TRANS_DFP_T_B_U32_U32_Rc(DRINTX, r, rmc) +TRANS_DFP_T_B_U32_U32_Rc(DRINTXQ, r, rmc) +TRANS_DFP_T_B_U32_U32_Rc(DRINTN, r, rmc) +TRANS_DFP_T_B_U32_U32_Rc(DRINTNQ, r, rmc) GEN_DFP_T_B_Rc(dctdp) GEN_DFP_T_B_Rc(dctqpq) GEN_DFP_T_B_Rc(drsp) @@ -205,7 +201,6 @@ GEN_DFP_T_FPR_I32_Rc(dscliq, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscri, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) -#undef GEN_DFP_T_B_U32_U32_Rc #undef GEN_DFP_T_A_B_I32_Rc #undef GEN_DFP_T_B_Rc #undef GEN_DFP_T_FPR_I32_Rc diff --git a/target/ppc/translate/dfp-ops.c.inc b/target/ppc/translate/dfp-ops.c.inc index e59425c8b2..c563f84a0b 100644 --- a/target/ppc/translate/dfp-ops.c.inc +++ b/target/ppc/translate/dfp-ops.c.inc @@ -45,18 +45,6 @@ _GEN_DFP_QUADx4(name, op1, op2, 0x02010800) #define GEN_DFP_Tp_A_Bp_RMC_Rc(name, op1, op2) \ _GEN_DFP_QUADx4(name, op1, op2, 0x02000800) -#define GEN_DFP_TE_T_B_RMC_Rc(name, op1, op2) \ -_GEN_DFP_LONGx4(name, op1, op2, 0x00000000) - -#define GEN_DFP_TE_Tp_Bp_RMC_Rc(name, op1, op2) \ -_GEN_DFP_QUADx4(name, op1, op2, 0x00200800) - -#define GEN_DFP_R_T_B_RMC_Rc(name, op1, op2) \ -_GEN_DFP_LONGx4(name, op1, op2, 0x001E0000) - -#define GEN_DFP_R_Tp_Bp_RMC_Rc(name, op1, op2) \ -_GEN_DFP_QUADx4(name, op1, op2, 0x003E0800) - #define GEN_DFP_SP_T_B_Rc(name, op1, op2) \ _GEN_DFP_LONG(name, op1, op2, 0x00070000) @@ -75,16 +63,10 @@ _GEN_DFP_LONGx2(name, op1, op2, 0x00000000) #define GEN_DFP_Tp_Ap_SH_Rc(name, op1, op2) \ _GEN_DFP_QUADx2(name, op1, op2, 0x00210000) -GEN_DFP_TE_T_B_RMC_Rc(dquai, 0x03, 0x02), -GEN_DFP_TE_Tp_Bp_RMC_Rc(dquaiq, 0x03, 0x02), GEN_DFP_T_A_B_RMC_Rc(dqua, 0x03, 0x00), GEN_DFP_Tp_Ap_Bp_RMC_Rc(dquaq, 0x03, 0x00), GEN_DFP_T_A_B_RMC_Rc(drrnd, 0x03, 0x01), GEN_DFP_Tp_A_Bp_RMC_Rc(drrndq, 0x03, 0x01), -GEN_DFP_R_T_B_RMC_Rc(drintx, 0x03, 0x03), -GEN_DFP_R_Tp_Bp_RMC_Rc(drintxq, 0x03, 0x03), -GEN_DFP_R_T_B_RMC_Rc(drintn, 0x03, 0x07), -GEN_DFP_R_Tp_Bp_RMC_Rc(drintnq, 0x03, 0x07), GEN_DFP_T_B_Rc(dctdp, 0x02, 0x08), GEN_DFP_Tp_B_Rc(dctqpq, 0x02, 0x08), GEN_DFP_T_B_Rc(drsp, 0x02, 0x18), From a8f4bce6f8eeb51d4682bf1f14bd9fe04f6b1d78 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:15 -0300 Subject: [PATCH 1271/1334] target/ppc: Move dqua[q], drrnd[q] to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the following instructions to decodetree: dqua: DFP Quantize dquaq: DFP Quantize Quad drrnd: DFP Reround drrndq: DFP Reround Quad Signed-off-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-14-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 8 ++--- target/ppc/helper.h | 8 ++--- target/ppc/insn32.decode | 18 +++++++++-- target/ppc/translate/dfp-impl.c.inc | 50 +++++++++++++---------------- target/ppc/translate/dfp-ops.c.inc | 25 --------------- 5 files changed, 47 insertions(+), 62 deletions(-) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index df3e6c7cb1..a50a73d3c0 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -769,8 +769,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a, \ set_dfp##size(t, &dfp.vt); \ } -DFP_HELPER_QUA(dqua, 64) -DFP_HELPER_QUA(dquaq, 128) +DFP_HELPER_QUA(DQUA, 64) +DFP_HELPER_QUA(DQUAQ, 128) static void _dfp_reround(uint8_t rmc, int32_t ref_sig, int32_t xmax, struct PPC_DFP *dfp) @@ -847,8 +847,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a, \ set_dfp##size(t, &dfp.vt); \ } -DFP_HELPER_RRND(drrnd, 64) -DFP_HELPER_RRND(drrndq, 128) +DFP_HELPER_RRND(DRRND, 64) +DFP_HELPER_RRND(DRRNDQ, 128) #define DFP_HELPER_RINT(op, postprocs, size) \ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b, \ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 70bc8ac579..70aebdfeff 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -722,10 +722,10 @@ DEF_HELPER_3(DTSTSFI, i32, env, i32, fprp) DEF_HELPER_3(DTSTSFIQ, i32, env, i32, fprp) DEF_HELPER_5(DQUAI, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(DQUAIQ, void, env, fprp, fprp, i32, i32) -DEF_HELPER_5(dqua, void, env, fprp, fprp, fprp, i32) -DEF_HELPER_5(dquaq, void, env, fprp, fprp, fprp, i32) -DEF_HELPER_5(drrnd, void, env, fprp, fprp, fprp, i32) -DEF_HELPER_5(drrndq, void, env, fprp, fprp, fprp, i32) +DEF_HELPER_5(DQUA, void, env, fprp, fprp, fprp, i32) +DEF_HELPER_5(DQUAQ, void, env, fprp, fprp, fprp, i32) +DEF_HELPER_5(DRRND, void, env, fprp, fprp, fprp, i32) +DEF_HELPER_5(DRRNDQ, void, env, fprp, fprp, fprp, i32) DEF_HELPER_5(DRINTX, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(DRINTXQ, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(DRINTN, void, env, fprp, fprp, i32, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 5369af17b6..f7c8d50bd9 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -84,11 +84,19 @@ %z22_frap 17:4 !function=times_2 @Z22_bf_frap ...... bf:3 .. ....0 dm:6 ......... . &Z22_bf_fra fra=%z22_frap +&Z23_tab frt fra frb rmc rc:bool +@Z23_tab ...... frt:5 fra:5 frb:5 rmc:2 ........ rc:1 &Z23_tab + +%z23_frtp 22:4 !function=times_2 +%z23_frap 17:4 !function=times_2 +%z23_frbp 12:4 !function=times_2 +@Z23_tabp ...... ....0 ....0 ....0 rmc:2 ........ rc:1 &Z23_tab frt=%z23_frtp fra=%z23_frap frb=%z23_frbp + +@Z23_tp_a_bp ...... ....0 fra:5 ....0 rmc:2 ........ rc:1 &Z23_tab frt=%z23_frtp frb=%z23_frbp + &Z23_tb frt frb r:bool rmc rc:bool @Z23_tb ...... frt:5 .... r:1 frb:5 rmc:2 ........ rc:1 &Z23_tb -%z23_frtp 22:4 !function=times_2 -%z23_frbp 12:4 !function=times_2 @Z23_tbp ...... ....0 .... r:1 ....0 rmc:2 ........ rc:1 &Z23_tb frt=%z23_frtp frb=%z23_frbp &Z23_te_tb te frt frb rmc rc:bool @@ -250,6 +258,12 @@ DTSTSFIQ 111111 ... - ...... ..... 1010100011 - @X_bf_uim_bp DQUAI 111011 ..... ..... ..... .. 01000011 . @Z23_te_tb DQUAIQ 111111 ..... ..... ..... .. 01000011 . @Z23_te_tbp +DQUA 111011 ..... ..... ..... .. 00000011 . @Z23_tab +DQUAQ 111111 ..... ..... ..... .. 00000011 . @Z23_tabp + +DRRND 111011 ..... ..... ..... .. 00100011 . @Z23_tab +DRRNDQ 111111 ..... ..... ..... .. 00100011 . @Z23_tp_a_bp + DRINTX 111011 ..... ---- . ..... .. 01100011 . @Z23_tb DRINTXQ 111111 ..... ---- . ..... .. 01100011 . @Z23_tbp diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index cb481d028a..30d65ffb46 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -86,28 +86,25 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ return true; \ } -#define GEN_DFP_T_A_B_I32_Rc(name, i32fld) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr rt, ra, rb; \ - TCGv_i32 i32; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - rt = gen_fprp_ptr(rD(ctx->opcode)); \ - ra = gen_fprp_ptr(rA(ctx->opcode)); \ - rb = gen_fprp_ptr(rB(ctx->opcode)); \ - i32 = tcg_const_i32(i32fld(ctx->opcode)); \ - gen_helper_##name(cpu_env, rt, ra, rb, i32); \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_i32(i32); \ - } +#define TRANS_DFP_T_A_B_I32_Rc(NAME, I32FLD) \ +static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ +{ \ + TCGv_ptr rt, ra, rb; \ + REQUIRE_INSNS_FLAGS2(ctx, DFP); \ + REQUIRE_FPU(ctx); \ + rt = gen_fprp_ptr(a->frt); \ + ra = gen_fprp_ptr(a->fra); \ + rb = gen_fprp_ptr(a->frb); \ + gen_helper_##NAME(cpu_env, rt, ra, rb, \ + tcg_constant_i32(a->I32FLD)); \ + if (unlikely(a->rc)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + return true; \ +} #define GEN_DFP_T_B_Rc(name) \ static void gen_##name(DisasContext *ctx) \ @@ -172,10 +169,10 @@ TRANS_DFP_BF_I_B(DTSTSFI) TRANS_DFP_BF_I_B(DTSTSFIQ) TRANS_DFP_T_B_U32_U32_Rc(DQUAI, te, rmc) TRANS_DFP_T_B_U32_U32_Rc(DQUAIQ, te, rmc) -GEN_DFP_T_A_B_I32_Rc(dqua, RMC) -GEN_DFP_T_A_B_I32_Rc(dquaq, RMC) -GEN_DFP_T_A_B_I32_Rc(drrnd, RMC) -GEN_DFP_T_A_B_I32_Rc(drrndq, RMC) +TRANS_DFP_T_A_B_I32_Rc(DQUA, rmc) +TRANS_DFP_T_A_B_I32_Rc(DQUAQ, rmc) +TRANS_DFP_T_A_B_I32_Rc(DRRND, rmc) +TRANS_DFP_T_A_B_I32_Rc(DRRNDQ, rmc) TRANS_DFP_T_B_U32_U32_Rc(DRINTX, r, rmc) TRANS_DFP_T_B_U32_U32_Rc(DRINTXQ, r, rmc) TRANS_DFP_T_B_U32_U32_Rc(DRINTN, r, rmc) @@ -201,7 +198,6 @@ GEN_DFP_T_FPR_I32_Rc(dscliq, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscri, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) -#undef GEN_DFP_T_A_B_I32_Rc #undef GEN_DFP_T_B_Rc #undef GEN_DFP_T_FPR_I32_Rc diff --git a/target/ppc/translate/dfp-ops.c.inc b/target/ppc/translate/dfp-ops.c.inc index c563f84a0b..3e0dfae796 100644 --- a/target/ppc/translate/dfp-ops.c.inc +++ b/target/ppc/translate/dfp-ops.c.inc @@ -5,12 +5,6 @@ GEN_HANDLER_E(name, 0x3B, op1, op2, mask, PPC_NONE, PPC2_DFP) GEN_HANDLER_E(name, 0x3B, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ GEN_HANDLER_E(name, 0x3B, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) -#define _GEN_DFP_LONGx4(name, op1, op2, mask) \ -GEN_HANDLER_E(name, 0x3B, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ -GEN_HANDLER_E(name, 0x3B, op1, 0x08 | op2, mask, PPC_NONE, PPC2_DFP), \ -GEN_HANDLER_E(name, 0x3B, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP), \ -GEN_HANDLER_E(name, 0x3B, op1, 0x18 | op2, mask, PPC_NONE, PPC2_DFP) - #define _GEN_DFP_QUAD(name, op1, op2, mask) \ GEN_HANDLER_E(name, 0x3F, op1, op2, mask, PPC_NONE, PPC2_DFP) @@ -18,12 +12,6 @@ GEN_HANDLER_E(name, 0x3F, op1, op2, mask, PPC_NONE, PPC2_DFP) GEN_HANDLER_E(name, 0x3F, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ GEN_HANDLER_E(name, 0x3F, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) -#define _GEN_DFP_QUADx4(name, op1, op2, mask) \ -GEN_HANDLER_E(name, 0x3F, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ -GEN_HANDLER_E(name, 0x3F, op1, 0x08 | op2, mask, PPC_NONE, PPC2_DFP), \ -GEN_HANDLER_E(name, 0x3F, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP), \ -GEN_HANDLER_E(name, 0x3F, op1, 0x18 | op2, mask, PPC_NONE, PPC2_DFP) - #define GEN_DFP_T_B_Rc(name, op1, op2) \ _GEN_DFP_LONG(name, op1, op2, 0x001F0000) @@ -36,15 +24,6 @@ _GEN_DFP_QUAD(name, op1, op2, 0x003F0000) #define GEN_DFP_T_Bp_Rc(name, op1, op2) \ _GEN_DFP_QUAD(name, op1, op2, 0x001F0800) -#define GEN_DFP_T_A_B_RMC_Rc(name, op1, op2) \ -_GEN_DFP_LONGx4(name, op1, op2, 0x00000000) - -#define GEN_DFP_Tp_Ap_Bp_RMC_Rc(name, op1, op2) \ -_GEN_DFP_QUADx4(name, op1, op2, 0x02010800) - -#define GEN_DFP_Tp_A_Bp_RMC_Rc(name, op1, op2) \ -_GEN_DFP_QUADx4(name, op1, op2, 0x02000800) - #define GEN_DFP_SP_T_B_Rc(name, op1, op2) \ _GEN_DFP_LONG(name, op1, op2, 0x00070000) @@ -63,10 +42,6 @@ _GEN_DFP_LONGx2(name, op1, op2, 0x00000000) #define GEN_DFP_Tp_Ap_SH_Rc(name, op1, op2) \ _GEN_DFP_QUADx2(name, op1, op2, 0x00210000) -GEN_DFP_T_A_B_RMC_Rc(dqua, 0x03, 0x00), -GEN_DFP_Tp_Ap_Bp_RMC_Rc(dquaq, 0x03, 0x00), -GEN_DFP_T_A_B_RMC_Rc(drrnd, 0x03, 0x01), -GEN_DFP_Tp_A_Bp_RMC_Rc(drrndq, 0x03, 0x01), GEN_DFP_T_B_Rc(dctdp, 0x02, 0x08), GEN_DFP_Tp_B_Rc(dctqpq, 0x02, 0x08), GEN_DFP_T_B_Rc(drsp, 0x02, 0x18), From c8ef4d1ec0ac395f6e4aac7f6013807db8178547 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:16 -0300 Subject: [PATCH 1272/1334] target/ppc: Move dct{dp,qpq},dr{sp,dpq},dc{f,t}fix[q],dxex[q] to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the following instructions to decodetree: dctdp: DFP Convert To DFP Long dctqpq: DFP Convert To DFP Extended drsp: DFP Round To DFP Short drdpq: DFP Round To DFP Long dcffix: DFP Convert From Fixed dcffixq: DFP Convert From Fixed Quad dctfix: DFP Convert To Fixed dctfixq: DFP Convert To Fixed Quad dxex: DFP Extract Biased Exponent dxexq: DFP Extract Biased Exponent Quad Signed-off-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-15-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 20 +++++------ target/ppc/helper.h | 20 +++++------ target/ppc/insn32.decode | 23 ++++++++++++ target/ppc/translate/dfp-impl.c.inc | 54 ++++++++++++++--------------- target/ppc/translate/dfp-ops.c.inc | 22 ------------ 5 files changed, 69 insertions(+), 70 deletions(-) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index a50a73d3c0..d950d0d3fc 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -885,7 +885,7 @@ static void RINTN_PPs(struct PPC_DFP *dfp) DFP_HELPER_RINT(DRINTN, RINTN_PPs, 64) DFP_HELPER_RINT(DRINTNQ, RINTN_PPs, 128) -void helper_dctdp(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) +void helper_DCTDP(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) { struct PPC_DFP dfp; ppc_vsr_t vb; @@ -901,7 +901,7 @@ void helper_dctdp(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) dfp_set_FPRF_from_FRT(&dfp); } -void helper_dctqpq(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) +void helper_DCTQPQ(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) { struct PPC_DFP dfp; ppc_vsr_t vb; @@ -916,7 +916,7 @@ void helper_dctqpq(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) set_dfp128(t, &dfp.vt); } -void helper_drsp(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) +void helper_DRSP(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) { struct PPC_DFP dfp; uint32_t t_short = 0; @@ -934,7 +934,7 @@ void helper_drsp(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) set_dfp64(t, &vt); } -void helper_drdpq(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) +void helper_DRDPQ(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) { struct PPC_DFP dfp; dfp_prepare_decimal128(&dfp, 0, b, env); @@ -972,8 +972,8 @@ static void CFFIX_PPs(struct PPC_DFP *dfp) dfp_check_for_XX(dfp); } -DFP_HELPER_CFFIX(dcffix, 64) -DFP_HELPER_CFFIX(dcffixq, 128) +DFP_HELPER_CFFIX(DCFFIX, 64) +DFP_HELPER_CFFIX(DCFFIXQ, 128) void helper_DCFFIXQQ(CPUPPCState *env, ppc_fprp_t *t, ppc_avr_t *b) { @@ -1022,8 +1022,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) \ set_dfp64(t, &dfp.vt); \ } -DFP_HELPER_CTFIX(dctfix, 64) -DFP_HELPER_CTFIX(dctfixq, 128) +DFP_HELPER_CTFIX(DCTFIX, 64) +DFP_HELPER_CTFIX(DCTFIXQ, 128) void helper_DCTFIXQQ(CPUPPCState *env, ppc_avr_t *t, ppc_fprp_t *b) { @@ -1233,8 +1233,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) \ } \ } -DFP_HELPER_XEX(dxex, 64) -DFP_HELPER_XEX(dxexq, 128) +DFP_HELPER_XEX(DXEX, 64) +DFP_HELPER_XEX(DXEXQ, 128) static void dfp_set_raw_exp_64(ppc_vsr_t *t, uint64_t raw) { diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 70aebdfeff..b302928a78 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -730,22 +730,22 @@ DEF_HELPER_5(DRINTX, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(DRINTXQ, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(DRINTN, void, env, fprp, fprp, i32, i32) DEF_HELPER_5(DRINTNQ, void, env, fprp, fprp, i32, i32) -DEF_HELPER_3(dctdp, void, env, fprp, fprp) -DEF_HELPER_3(dctqpq, void, env, fprp, fprp) -DEF_HELPER_3(drsp, void, env, fprp, fprp) -DEF_HELPER_3(drdpq, void, env, fprp, fprp) -DEF_HELPER_3(dcffix, void, env, fprp, fprp) -DEF_HELPER_3(dcffixq, void, env, fprp, fprp) +DEF_HELPER_3(DCTDP, void, env, fprp, fprp) +DEF_HELPER_3(DCTQPQ, void, env, fprp, fprp) +DEF_HELPER_3(DRSP, void, env, fprp, fprp) +DEF_HELPER_3(DRDPQ, void, env, fprp, fprp) +DEF_HELPER_3(DCFFIX, void, env, fprp, fprp) +DEF_HELPER_3(DCFFIXQ, void, env, fprp, fprp) DEF_HELPER_3(DCFFIXQQ, void, env, fprp, avr) -DEF_HELPER_3(dctfix, void, env, fprp, fprp) -DEF_HELPER_3(dctfixq, void, env, fprp, fprp) +DEF_HELPER_3(DCTFIX, void, env, fprp, fprp) +DEF_HELPER_3(DCTFIXQ, void, env, fprp, fprp) DEF_HELPER_3(DCTFIXQQ, void, env, avr, fprp) DEF_HELPER_4(ddedpd, void, env, fprp, fprp, i32) DEF_HELPER_4(ddedpdq, void, env, fprp, fprp, i32) DEF_HELPER_4(denbcd, void, env, fprp, fprp, i32) DEF_HELPER_4(denbcdq, void, env, fprp, fprp, i32) -DEF_HELPER_3(dxex, void, env, fprp, fprp) -DEF_HELPER_3(dxexq, void, env, fprp, fprp) +DEF_HELPER_3(DXEX, void, env, fprp, fprp) +DEF_HELPER_3(DXEXQ, void, env, fprp, fprp) DEF_HELPER_4(DIEX, void, env, fprp, fprp, fprp) DEF_HELPER_4(DIEXQ, void, env, fprp, fprp, fprp) DEF_HELPER_4(dscri, void, env, fprp, fprp, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index f7c8d50bd9..033bae2b32 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -54,6 +54,15 @@ @X_tp_a_bp_rc ...... ....0 ra:5 ....0 .......... rc:1 &X_rc rt=%x_frtp rb=%x_frbp +&X_tb_rc rt rb rc:bool +@X_tb_rc ...... rt:5 ..... rb:5 .......... rc:1 &X_tb_rc + +@X_tbp_rc ...... ....0 ..... ....0 .......... rc:1 &X_tb_rc rt=%x_frtp rb=%x_frbp + +@X_tp_b_rc ...... ....0 ..... rb:5 .......... rc:1 &X_tb_rc rt=%x_frtp + +@X_t_bp_rc ...... rt:5 ..... ....0 .......... rc:1 &X_tb_rc rb=%x_frbp + &X_bi rt bi @X_bi ...... rt:5 bi:5 ----- .......... - &X_bi @@ -272,11 +281,25 @@ DRINTNQ 111111 ..... ---- . ..... .. 11100011 . @Z23_tbp ### Decimal Floating-Point Conversion Instructions +DCTDP 111011 ..... ----- ..... 0100000010 . @X_tb_rc +DCTQPQ 111111 ..... ----- ..... 0100000010 . @X_tp_b_rc + +DRSP 111011 ..... ----- ..... 1100000010 . @X_tb_rc +DRDPQ 111111 ..... ----- ..... 1100000010 . @X_tbp_rc + +DCFFIX 111011 ..... ----- ..... 1100100010 . @X_tb_rc +DCFFIXQ 111111 ..... ----- ..... 1100100010 . @X_tp_b_rc DCFFIXQQ 111111 ..... 00000 ..... 1111100010 - @X_frtp_vrb + +DCTFIX 111011 ..... ----- ..... 0100100010 . @X_tb_rc +DCTFIXQ 111111 ..... ----- ..... 0100100010 . @X_t_bp_rc DCTFIXQQ 111111 ..... 00001 ..... 1111100010 - @X_vrt_frbp ### Decimal Floating-Point Format Instructions +DXEX 111011 ..... ----- ..... 0101100010 . @X_tb_rc +DXEXQ 111111 ..... ----- ..... 0101100010 . @X_t_bp_rc + DIEX 111011 ..... ..... ..... 1101100010 . @X_rc DIEXQ 111111 ..... ..... ..... 1101100010 . @X_tp_a_bp_rc diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index 30d65ffb46..736292584c 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -106,23 +106,22 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ return true; \ } -#define GEN_DFP_T_B_Rc(name) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr rt, rb; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - rt = gen_fprp_ptr(rD(ctx->opcode)); \ - rb = gen_fprp_ptr(rB(ctx->opcode)); \ - gen_helper_##name(cpu_env, rt, rb); \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(rb); \ - } +#define TRANS_DFP_T_B_Rc(NAME) \ +static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ +{ \ + TCGv_ptr rt, rb; \ + REQUIRE_INSNS_FLAGS2(ctx, DFP); \ + REQUIRE_FPU(ctx); \ + rt = gen_fprp_ptr(a->rt); \ + rb = gen_fprp_ptr(a->rb); \ + gen_helper_##NAME(cpu_env, rt, rb); \ + if (unlikely(a->rc)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(rb); \ + return true; \ +} #define GEN_DFP_T_FPR_I32_Rc(name, fprfld, i32fld) \ static void gen_##name(DisasContext *ctx) \ @@ -177,20 +176,20 @@ TRANS_DFP_T_B_U32_U32_Rc(DRINTX, r, rmc) TRANS_DFP_T_B_U32_U32_Rc(DRINTXQ, r, rmc) TRANS_DFP_T_B_U32_U32_Rc(DRINTN, r, rmc) TRANS_DFP_T_B_U32_U32_Rc(DRINTNQ, r, rmc) -GEN_DFP_T_B_Rc(dctdp) -GEN_DFP_T_B_Rc(dctqpq) -GEN_DFP_T_B_Rc(drsp) -GEN_DFP_T_B_Rc(drdpq) -GEN_DFP_T_B_Rc(dcffix) -GEN_DFP_T_B_Rc(dcffixq) -GEN_DFP_T_B_Rc(dctfix) -GEN_DFP_T_B_Rc(dctfixq) +TRANS_DFP_T_B_Rc(DCTDP) +TRANS_DFP_T_B_Rc(DCTQPQ) +TRANS_DFP_T_B_Rc(DRSP) +TRANS_DFP_T_B_Rc(DRDPQ) +TRANS_DFP_T_B_Rc(DCFFIX) +TRANS_DFP_T_B_Rc(DCFFIXQ) +TRANS_DFP_T_B_Rc(DCTFIX) +TRANS_DFP_T_B_Rc(DCTFIXQ) GEN_DFP_T_FPR_I32_Rc(ddedpd, rB, SP) GEN_DFP_T_FPR_I32_Rc(ddedpdq, rB, SP) GEN_DFP_T_FPR_I32_Rc(denbcd, rB, SP) GEN_DFP_T_FPR_I32_Rc(denbcdq, rB, SP) -GEN_DFP_T_B_Rc(dxex) -GEN_DFP_T_B_Rc(dxexq) +TRANS_DFP_T_B_Rc(DXEX) +TRANS_DFP_T_B_Rc(DXEXQ) TRANS_DFP_T_A_B_Rc(DIEX) TRANS_DFP_T_A_B_Rc(DIEXQ) GEN_DFP_T_FPR_I32_Rc(dscli, rA, DCM) @@ -198,7 +197,6 @@ GEN_DFP_T_FPR_I32_Rc(dscliq, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscri, rA, DCM) GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) -#undef GEN_DFP_T_B_Rc #undef GEN_DFP_T_FPR_I32_Rc static bool trans_DCFFIXQQ(DisasContext *ctx, arg_DCFFIXQQ *a) diff --git a/target/ppc/translate/dfp-ops.c.inc b/target/ppc/translate/dfp-ops.c.inc index 3e0dfae796..e29c4b2194 100644 --- a/target/ppc/translate/dfp-ops.c.inc +++ b/target/ppc/translate/dfp-ops.c.inc @@ -12,18 +12,6 @@ GEN_HANDLER_E(name, 0x3F, op1, op2, mask, PPC_NONE, PPC2_DFP) GEN_HANDLER_E(name, 0x3F, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ GEN_HANDLER_E(name, 0x3F, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) -#define GEN_DFP_T_B_Rc(name, op1, op2) \ -_GEN_DFP_LONG(name, op1, op2, 0x001F0000) - -#define GEN_DFP_Tp_Bp_Rc(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x003F0800) - -#define GEN_DFP_Tp_B_Rc(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x003F0000) - -#define GEN_DFP_T_Bp_Rc(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x001F0800) - #define GEN_DFP_SP_T_B_Rc(name, op1, op2) \ _GEN_DFP_LONG(name, op1, op2, 0x00070000) @@ -42,20 +30,10 @@ _GEN_DFP_LONGx2(name, op1, op2, 0x00000000) #define GEN_DFP_Tp_Ap_SH_Rc(name, op1, op2) \ _GEN_DFP_QUADx2(name, op1, op2, 0x00210000) -GEN_DFP_T_B_Rc(dctdp, 0x02, 0x08), -GEN_DFP_Tp_B_Rc(dctqpq, 0x02, 0x08), -GEN_DFP_T_B_Rc(drsp, 0x02, 0x18), -GEN_DFP_Tp_Bp_Rc(drdpq, 0x02, 0x18), -GEN_DFP_T_B_Rc(dcffix, 0x02, 0x19), -GEN_DFP_Tp_B_Rc(dcffixq, 0x02, 0x19), -GEN_DFP_T_B_Rc(dctfix, 0x02, 0x09), -GEN_DFP_T_Bp_Rc(dctfixq, 0x02, 0x09), GEN_DFP_SP_T_B_Rc(ddedpd, 0x02, 0x0a), GEN_DFP_SP_Tp_Bp_Rc(ddedpdq, 0x02, 0x0a), GEN_DFP_S_T_B_Rc(denbcd, 0x02, 0x1a), GEN_DFP_S_Tp_Bp_Rc(denbcdq, 0x02, 0x1a), -GEN_DFP_T_B_Rc(dxex, 0x02, 0x0b), -GEN_DFP_T_Bp_Rc(dxexq, 0x02, 0x0b), GEN_DFP_T_A_SH_Rc(dscli, 0x02, 0x02), GEN_DFP_Tp_Ap_SH_Rc(dscliq, 0x02, 0x02), GEN_DFP_T_A_SH_Rc(dscri, 0x02, 0x03), From a23297479cc07812526fb6d9d213392dba555145 Mon Sep 17 00:00:00 2001 From: Luis Pires Date: Fri, 29 Oct 2021 16:24:17 -0300 Subject: [PATCH 1273/1334] target/ppc: Move ddedpd[q],denbcd[q],dscli[q],dscri[q] to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the following instructions to decodetree: ddedpd: DFP Decode DPD To BCD ddedpdq: DFP Decode DPD To BCD Quad denbcd: DFP Encode BCD To DPD denbcdq: DFP Encode BCD To DPD Quad dscli: DFP Shift Significand Left Immediate dscliq: DFP Shift Significand Left Immediate Quad dscri: DFP Shift Significand Right Immediate dscriq: DFP Shift Significand Right Immediate Quad Also deleted dfp-ops.c.inc, now that all PPC DFP instructions were moved to decodetree. Signed-off-by: Luis Pires Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211029192417.400707-16-luis.pires@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/dfp_helper.c | 16 ++++----- target/ppc/helper.h | 16 ++++----- target/ppc/insn32.decode | 28 +++++++++++++++ target/ppc/translate.c | 2 -- target/ppc/translate/dfp-impl.c.inc | 53 +++++++++++++---------------- target/ppc/translate/dfp-ops.c.inc | 40 ---------------------- 6 files changed, 68 insertions(+), 87 deletions(-) delete mode 100644 target/ppc/translate/dfp-ops.c.inc diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index d950d0d3fc..0d01ac3de0 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -1131,8 +1131,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b, \ set_dfp##size(t, &dfp.vt); \ } -DFP_HELPER_DEDPD(ddedpd, 64) -DFP_HELPER_DEDPD(ddedpdq, 128) +DFP_HELPER_DEDPD(DDEDPD, 64) +DFP_HELPER_DEDPD(DDEDPDQ, 128) static inline uint8_t dfp_get_bcd_digit_64(ppc_vsr_t *t, unsigned n) { @@ -1199,8 +1199,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b, \ set_dfp##size(t, &dfp.vt); \ } -DFP_HELPER_ENBCD(denbcd, 64) -DFP_HELPER_ENBCD(denbcdq, 128) +DFP_HELPER_ENBCD(DENBCD, 64) +DFP_HELPER_ENBCD(DENBCDQ, 128) #define DFP_HELPER_XEX(op, size) \ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) \ @@ -1387,7 +1387,7 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a, \ set_dfp##size(t, &dfp.vt); \ } -DFP_HELPER_SHIFT(dscli, 64, 1) -DFP_HELPER_SHIFT(dscliq, 128, 1) -DFP_HELPER_SHIFT(dscri, 64, 0) -DFP_HELPER_SHIFT(dscriq, 128, 0) +DFP_HELPER_SHIFT(DSCLI, 64, 1) +DFP_HELPER_SHIFT(DSCLIQ, 128, 1) +DFP_HELPER_SHIFT(DSCRI, 64, 0) +DFP_HELPER_SHIFT(DSCRIQ, 128, 0) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index b302928a78..72e66c5fe8 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -740,18 +740,18 @@ DEF_HELPER_3(DCFFIXQQ, void, env, fprp, avr) DEF_HELPER_3(DCTFIX, void, env, fprp, fprp) DEF_HELPER_3(DCTFIXQ, void, env, fprp, fprp) DEF_HELPER_3(DCTFIXQQ, void, env, avr, fprp) -DEF_HELPER_4(ddedpd, void, env, fprp, fprp, i32) -DEF_HELPER_4(ddedpdq, void, env, fprp, fprp, i32) -DEF_HELPER_4(denbcd, void, env, fprp, fprp, i32) -DEF_HELPER_4(denbcdq, void, env, fprp, fprp, i32) +DEF_HELPER_4(DDEDPD, void, env, fprp, fprp, i32) +DEF_HELPER_4(DDEDPDQ, void, env, fprp, fprp, i32) +DEF_HELPER_4(DENBCD, void, env, fprp, fprp, i32) +DEF_HELPER_4(DENBCDQ, void, env, fprp, fprp, i32) DEF_HELPER_3(DXEX, void, env, fprp, fprp) DEF_HELPER_3(DXEXQ, void, env, fprp, fprp) DEF_HELPER_4(DIEX, void, env, fprp, fprp, fprp) DEF_HELPER_4(DIEXQ, void, env, fprp, fprp, fprp) -DEF_HELPER_4(dscri, void, env, fprp, fprp, i32) -DEF_HELPER_4(dscriq, void, env, fprp, fprp, i32) -DEF_HELPER_4(dscli, void, env, fprp, fprp, i32) -DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32) +DEF_HELPER_4(DSCRI, void, env, fprp, fprp, i32) +DEF_HELPER_4(DSCRIQ, void, env, fprp, fprp, i32) +DEF_HELPER_4(DSCLI, void, env, fprp, fprp, i32) +DEF_HELPER_4(DSCLIQ, void, env, fprp, fprp, i32) DEF_HELPER_1(tbegin, void, env) DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 033bae2b32..65075f0d03 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -81,6 +81,16 @@ &X_bfl bf l:bool ra rb @X_bfl ...... bf:3 - l:1 ra:5 rb:5 ..........- &X_bfl +&X_tb_sp_rc rt rb sp rc:bool +@X_tb_sp_rc ...... rt:5 sp:2 ... rb:5 .......... rc:1 &X_tb_sp_rc + +@X_tbp_sp_rc ...... ....0 sp:2 ... ....0 .......... rc:1 &X_tb_sp_rc rt=%x_frtp rb=%x_frbp + +&X_tb_s_rc rt rb s:bool rc:bool +@X_tb_s_rc ...... rt:5 s:1 .... rb:5 .......... rc:1 &X_tb_s_rc + +@X_tbp_s_rc ...... ....0 s:1 .... ....0 .......... rc:1 &X_tb_s_rc rt=%x_frtp rb=%x_frbp + &X_frtp_vrb frtp vrb @X_frtp_vrb ...... ....0 ..... vrb:5 .......... . &X_frtp_vrb frtp=%x_frtp @@ -93,6 +103,12 @@ %z22_frap 17:4 !function=times_2 @Z22_bf_frap ...... bf:3 .. ....0 dm:6 ......... . &Z22_bf_fra fra=%z22_frap +&Z22_ta_sh_rc rt ra sh rc:bool +@Z22_ta_sh_rc ...... rt:5 ra:5 sh:6 ......... rc:1 &Z22_ta_sh_rc + +%z22_frtp 22:4 !function=times_2 +@Z22_tap_sh_rc ...... ....0 ....0 sh:6 ......... rc:1 &Z22_ta_sh_rc rt=%z22_frtp ra=%z22_frap + &Z23_tab frt fra frb rmc rc:bool @Z23_tab ...... frt:5 fra:5 frb:5 rmc:2 ........ rc:1 &Z23_tab @@ -297,12 +313,24 @@ DCTFIXQQ 111111 ..... 00001 ..... 1111100010 - @X_vrt_frbp ### Decimal Floating-Point Format Instructions +DDEDPD 111011 ..... .. --- ..... 0101000010 . @X_tb_sp_rc +DDEDPDQ 111111 ..... .. --- ..... 0101000010 . @X_tbp_sp_rc + +DENBCD 111011 ..... . ---- ..... 1101000010 . @X_tb_s_rc +DENBCDQ 111111 ..... . ---- ..... 1101000010 . @X_tbp_s_rc + DXEX 111011 ..... ----- ..... 0101100010 . @X_tb_rc DXEXQ 111111 ..... ----- ..... 0101100010 . @X_t_bp_rc DIEX 111011 ..... ..... ..... 1101100010 . @X_rc DIEXQ 111111 ..... ..... ..... 1101100010 . @X_tp_a_bp_rc +DSCLI 111011 ..... ..... ...... 001000010 . @Z22_ta_sh_rc +DSCLIQ 111111 ..... ..... ...... 001000010 . @Z22_tap_sh_rc + +DSCRI 111011 ..... ..... ...... 001100010 . @Z22_ta_sh_rc +DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc + ## Vector Bit Manipulation Instruction VCFUGED 000100 ..... ..... ..... 10101001101 @VX diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 486339d402..659859ff5f 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -8079,8 +8079,6 @@ GEN_HANDLER2_E(trechkpt, "trechkpt", 0x1F, 0x0E, 0x1F, 0x03FFF800, \ #include "translate/vsx-ops.c.inc" -#include "translate/dfp-ops.c.inc" - #include "translate/spe-ops.c.inc" }; diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index 736292584c..f9f1d58d44 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -123,25 +123,22 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ return true; \ } -#define GEN_DFP_T_FPR_I32_Rc(name, fprfld, i32fld) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr rt, rs; \ - TCGv_i32 i32; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - rt = gen_fprp_ptr(rD(ctx->opcode)); \ - rs = gen_fprp_ptr(fprfld(ctx->opcode)); \ - i32 = tcg_const_i32(i32fld(ctx->opcode)); \ - gen_helper_##name(cpu_env, rt, rs, i32); \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(rs); \ - tcg_temp_free_i32(i32); \ +#define TRANS_DFP_T_FPR_I32_Rc(NAME, FPRFLD, I32FLD) \ +static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ +{ \ + TCGv_ptr rt, rx; \ + REQUIRE_INSNS_FLAGS2(ctx, DFP); \ + REQUIRE_FPU(ctx); \ + rt = gen_fprp_ptr(a->rt); \ + rx = gen_fprp_ptr(a->FPRFLD); \ + gen_helper_##NAME(cpu_env, rt, rx, \ + tcg_constant_i32(a->I32FLD)); \ + if (unlikely(a->rc)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(rx); \ + return true; \ } TRANS_DFP_T_A_B_Rc(DADD) @@ -184,20 +181,18 @@ TRANS_DFP_T_B_Rc(DCFFIX) TRANS_DFP_T_B_Rc(DCFFIXQ) TRANS_DFP_T_B_Rc(DCTFIX) TRANS_DFP_T_B_Rc(DCTFIXQ) -GEN_DFP_T_FPR_I32_Rc(ddedpd, rB, SP) -GEN_DFP_T_FPR_I32_Rc(ddedpdq, rB, SP) -GEN_DFP_T_FPR_I32_Rc(denbcd, rB, SP) -GEN_DFP_T_FPR_I32_Rc(denbcdq, rB, SP) +TRANS_DFP_T_FPR_I32_Rc(DDEDPD, rb, sp) +TRANS_DFP_T_FPR_I32_Rc(DDEDPDQ, rb, sp) +TRANS_DFP_T_FPR_I32_Rc(DENBCD, rb, s) +TRANS_DFP_T_FPR_I32_Rc(DENBCDQ, rb, s) TRANS_DFP_T_B_Rc(DXEX) TRANS_DFP_T_B_Rc(DXEXQ) TRANS_DFP_T_A_B_Rc(DIEX) TRANS_DFP_T_A_B_Rc(DIEXQ) -GEN_DFP_T_FPR_I32_Rc(dscli, rA, DCM) -GEN_DFP_T_FPR_I32_Rc(dscliq, rA, DCM) -GEN_DFP_T_FPR_I32_Rc(dscri, rA, DCM) -GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) - -#undef GEN_DFP_T_FPR_I32_Rc +TRANS_DFP_T_FPR_I32_Rc(DSCLI, ra, sh) +TRANS_DFP_T_FPR_I32_Rc(DSCLIQ, ra, sh) +TRANS_DFP_T_FPR_I32_Rc(DSCRI, ra, sh) +TRANS_DFP_T_FPR_I32_Rc(DSCRIQ, ra, sh) static bool trans_DCFFIXQQ(DisasContext *ctx, arg_DCFFIXQQ *a) { diff --git a/target/ppc/translate/dfp-ops.c.inc b/target/ppc/translate/dfp-ops.c.inc deleted file mode 100644 index e29c4b2194..0000000000 --- a/target/ppc/translate/dfp-ops.c.inc +++ /dev/null @@ -1,40 +0,0 @@ -#define _GEN_DFP_LONG(name, op1, op2, mask) \ -GEN_HANDLER_E(name, 0x3B, op1, op2, mask, PPC_NONE, PPC2_DFP) - -#define _GEN_DFP_LONGx2(name, op1, op2, mask) \ -GEN_HANDLER_E(name, 0x3B, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ -GEN_HANDLER_E(name, 0x3B, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) - -#define _GEN_DFP_QUAD(name, op1, op2, mask) \ -GEN_HANDLER_E(name, 0x3F, op1, op2, mask, PPC_NONE, PPC2_DFP) - -#define _GEN_DFP_QUADx2(name, op1, op2, mask) \ -GEN_HANDLER_E(name, 0x3F, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ -GEN_HANDLER_E(name, 0x3F, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) - -#define GEN_DFP_SP_T_B_Rc(name, op1, op2) \ -_GEN_DFP_LONG(name, op1, op2, 0x00070000) - -#define GEN_DFP_SP_Tp_Bp_Rc(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x00270800) - -#define GEN_DFP_S_T_B_Rc(name, op1, op2) \ -_GEN_DFP_LONG(name, op1, op2, 0x000F0000) - -#define GEN_DFP_S_Tp_Bp_Rc(name, op1, op2) \ -_GEN_DFP_QUAD(name, op1, op2, 0x002F0800) - -#define GEN_DFP_T_A_SH_Rc(name, op1, op2) \ -_GEN_DFP_LONGx2(name, op1, op2, 0x00000000) - -#define GEN_DFP_Tp_Ap_SH_Rc(name, op1, op2) \ -_GEN_DFP_QUADx2(name, op1, op2, 0x00210000) - -GEN_DFP_SP_T_B_Rc(ddedpd, 0x02, 0x0a), -GEN_DFP_SP_Tp_Bp_Rc(ddedpdq, 0x02, 0x0a), -GEN_DFP_S_T_B_Rc(denbcd, 0x02, 0x1a), -GEN_DFP_S_Tp_Bp_Rc(denbcdq, 0x02, 0x1a), -GEN_DFP_T_A_SH_Rc(dscli, 0x02, 0x02), -GEN_DFP_Tp_Ap_SH_Rc(dscliq, 0x02, 0x02), -GEN_DFP_T_A_SH_Rc(dscri, 0x02, 0x03), -GEN_DFP_Tp_Ap_SH_Rc(dscriq, 0x02, 0x03), From 957c52aed5efcf036affb30907514262643f556a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 2 Nov 2021 17:29:05 +0100 Subject: [PATCH 1274/1334] ppc/pnv: Fix check on block device before updating drive contents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test is wrong and the backend can never updated. It could have led to a QEMU crash but since the firmware deactivates flash access if a valid layout is not detected, it went unnoticed. Reported-by: Coverity CID 1465223 Fixes: 35dde5766211 ("ppc/pnv: Add a PNOR model") Signed-off-by: Cédric Le Goater Message-Id: <20211102162905.762078-1-clg@kaod.org> Signed-off-by: David Gibson --- hw/ppc/pnv_pnor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 5ef1cf2afb..83ecccca28 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -36,7 +36,7 @@ static void pnv_pnor_update(PnvPnor *s, int offset, int size) int offset_end; int ret; - if (s->blk) { + if (!s->blk || !blk_is_writable(s->blk)) { return; } From 88adcbf2800c92fb55bf6ecfbaed8d1d21445bea Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 1 Nov 2021 16:08:31 +0100 Subject: [PATCH 1275/1334] ppc/pegasos2: Suppress warning when qtest enabled Suggested-by: Peter Maydell Signed-off-by: BALATON Zoltan Message-Id: <20211101151023.F0D02748F5A@zero.eik.bme.hu> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- hw/ppc/pegasos2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index e427ac2fe0..298e6b93e2 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -23,6 +23,7 @@ #include "hw/qdev-properties.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" +#include "sysemu/qtest.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" @@ -199,7 +200,7 @@ static void pegasos2_init(MachineState *machine) if (!pm->vof) { warn_report("Option -kernel may be ineffective with -bios."); } - } else if (pm->vof) { + } else if (pm->vof && !qtest_enabled()) { warn_report("Using Virtual OpenFirmware but no -kernel option."); } From 6e0bbc4048225cca44f6f060ccd4e286f2a06d61 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:36:55 -0300 Subject: [PATCH 1276/1334] target/ppc: Move vcfuged to vmx-impl.c.inc There's no reason to keep vector-impl.c.inc separate from vmx-impl.c.inc. Additionally, let GVec handle the multiple calls to helper_cfuged for us. Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-2-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/helper.h | 2 +- target/ppc/int_helper.c | 2 +- target/ppc/translate.c | 1 - target/ppc/translate/fixedpoint-impl.c.inc | 2 +- target/ppc/translate/vector-impl.c.inc | 48 ---------------------- target/ppc/translate/vmx-impl.c.inc | 16 ++++++++ 6 files changed, 19 insertions(+), 52 deletions(-) delete mode 100644 target/ppc/translate/vector-impl.c.inc diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 72e66c5fe8..401575b935 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -46,7 +46,7 @@ DEF_HELPER_4(divwe, tl, env, tl, tl, i32) DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_3(sraw, tl, env, tl, tl) -DEF_HELPER_FLAGS_2(cfuged, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(CFUGED, TCG_CALL_NO_RWG_SE, i64, i64, i64) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(PDEPD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(PEXTD, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 913d76be6e..f03c864e48 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -324,7 +324,7 @@ target_ulong helper_popcntb(target_ulong val) } #endif -uint64_t helper_cfuged(uint64_t src, uint64_t mask) +uint64_t helper_CFUGED(uint64_t src, uint64_t mask) { /* * Instead of processing the mask bit-by-bit from the most significant to diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 659859ff5f..fc9d35a7a8 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7407,7 +7407,6 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/vmx-impl.c.inc" #include "translate/vsx-impl.c.inc" -#include "translate/vector-impl.c.inc" #include "translate/dfp-impl.c.inc" diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 220b099fcd..fa519c2d3e 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -407,7 +407,7 @@ static bool trans_CFUGED(DisasContext *ctx, arg_X *a) REQUIRE_64BIT(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA310); #if defined(TARGET_PPC64) - gen_helper_cfuged(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + gen_helper_CFUGED(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); #else qemu_build_not_reached(); #endif diff --git a/target/ppc/translate/vector-impl.c.inc b/target/ppc/translate/vector-impl.c.inc deleted file mode 100644 index 197e903337..0000000000 --- a/target/ppc/translate/vector-impl.c.inc +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Power ISA decode for Vector Facility instructions - * - * Copyright (c) 2021 Instituto de Pesquisas Eldorado (eldorado.org.br) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -static bool trans_VCFUGED(DisasContext *ctx, arg_VX *a) -{ - TCGv_i64 tgt, src, mask; - - REQUIRE_INSNS_FLAGS2(ctx, ISA310); - REQUIRE_VECTOR(ctx); - - tgt = tcg_temp_new_i64(); - src = tcg_temp_new_i64(); - mask = tcg_temp_new_i64(); - - /* centrifuge lower double word */ - get_cpu_vsrl(src, a->vra + 32); - get_cpu_vsrl(mask, a->vrb + 32); - gen_helper_cfuged(tgt, src, mask); - set_cpu_vsrl(a->vrt + 32, tgt); - - /* centrifuge higher double word */ - get_cpu_vsrh(src, a->vra + 32); - get_cpu_vsrh(mask, a->vrb + 32); - gen_helper_cfuged(tgt, src, mask); - set_cpu_vsrh(a->vrt + 32, tgt); - - tcg_temp_free_i64(tgt); - tcg_temp_free_i64(src); - tcg_temp_free_i64(mask); - - return true; -} diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 92b9527aff..e36c66589c 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1559,6 +1559,22 @@ GEN_VXFORM3(vpermxor, 22, 0xFF) GEN_VXFORM_DUAL(vsldoi, PPC_ALTIVEC, PPC_NONE, vpermxor, PPC_NONE, PPC2_ALTIVEC_207) +static bool trans_VCFUGED(DisasContext *ctx, arg_VX *a) +{ + static const GVecGen3 g = { + .fni8 = gen_helper_CFUGED, + .vece = MO_64, + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &g); + + return true; +} + #undef GEN_VR_LDX #undef GEN_VR_STX #undef GEN_VR_LVE From a2c975e119af289d8611df1d8649685b928cfd71 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:36:56 -0300 Subject: [PATCH 1277/1334] target/ppc: Implement vclzdm/vctzdm instructions The signature of do_cntzdm is changed to allow reuse as GVecGen3i.fni8. The method is also moved out of #ifdef TARGET_PPC64, as PowerISA doesn't say vclzdm and vctzdm are 64-bit only. Reviewed-by: Richard Henderson Signed-off-by: Luis Pires Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-3-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 2 ++ target/ppc/translate/fixedpoint-impl.c.inc | 4 +-- target/ppc/translate/vmx-impl.c.inc | 32 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 65075f0d03..6ce06b231d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -334,3 +334,5 @@ DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc ## Vector Bit Manipulation Instruction VCFUGED 000100 ..... ..... ..... 10101001101 @VX +VCLZDM 000100 ..... ..... ..... 11110000100 @VX +VCTZDM 000100 ..... ..... ..... 11111000100 @VX diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index fa519c2d3e..e093562e2a 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -414,8 +414,7 @@ static bool trans_CFUGED(DisasContext *ctx, arg_X *a) return true; } -#if defined(TARGET_PPC64) -static void do_cntzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask, bool trail) +static void do_cntzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask, int64_t trail) { TCGv_i64 tmp; TCGLabel *l1; @@ -444,7 +443,6 @@ static void do_cntzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask, bool trail) tcg_gen_mov_i64(dst, tmp); } -#endif static bool trans_CNTLZDM(DisasContext *ctx, arg_X *a) { diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index e36c66589c..6da8a9123f 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1575,6 +1575,38 @@ static bool trans_VCFUGED(DisasContext *ctx, arg_VX *a) return true; } +static bool trans_VCLZDM(DisasContext *ctx, arg_VX *a) +{ + static const GVecGen3i g = { + .fni8 = do_cntzdm, + .vece = MO_64, + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3i(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, false, &g); + + return true; +} + +static bool trans_VCTZDM(DisasContext *ctx, arg_VX *a) +{ + static const GVecGen3i g = { + .fni8 = do_cntzdm, + .vece = MO_64, + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3i(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, true, &g); + + return true; +} + #undef GEN_VR_LDX #undef GEN_VR_STX #undef GEN_VR_LVE From 00a16569ebb0123fe1a7d820c61d6d9be28705ac Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:36:57 -0300 Subject: [PATCH 1278/1334] target/ppc: Implement vpdepd/vpextd instruction pdepd and pextd helpers are moved out of #ifdef (TARGET_PPC64) to allow them to be reused as GVecGen3.fni8. Reviewed-by: Richard Henderson Signed-off-by: Luis Pires Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-4-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/helper.h | 2 +- target/ppc/insn32.decode | 2 ++ target/ppc/int_helper.c | 2 -- target/ppc/translate/vmx-impl.c.inc | 32 +++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 401575b935..0e99f8095c 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -47,9 +47,9 @@ DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_3(sraw, tl, env, tl, tl) DEF_HELPER_FLAGS_2(CFUGED, TCG_CALL_NO_RWG_SE, i64, i64, i64) -#if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(PDEPD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(PEXTD, TCG_CALL_NO_RWG_SE, i64, i64, i64) +#if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 6ce06b231d..4666c06f55 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -336,3 +336,5 @@ DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc VCFUGED 000100 ..... ..... ..... 10101001101 @VX VCLZDM 000100 ..... ..... ..... 11110000100 @VX VCTZDM 000100 ..... ..... ..... 11111000100 @VX +VPDEPD 000100 ..... ..... ..... 10111001101 @VX +VPEXTD 000100 ..... ..... ..... 10110001101 @VX diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index f03c864e48..42541736f1 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -386,7 +386,6 @@ uint64_t helper_CFUGED(uint64_t src, uint64_t mask) return left | (right >> n); } -#if defined(TARGET_PPC64) uint64_t helper_PDEPD(uint64_t src, uint64_t mask) { int i, o; @@ -422,7 +421,6 @@ uint64_t helper_PEXTD(uint64_t src, uint64_t mask) return result; } -#endif /*****************************************************************************/ /* PowerPC 601 specific instructions (POWER bridge) */ diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 6da8a9123f..cddb3848ab 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1607,6 +1607,38 @@ static bool trans_VCTZDM(DisasContext *ctx, arg_VX *a) return true; } +static bool trans_VPDEPD(DisasContext *ctx, arg_VX *a) +{ + static const GVecGen3 g = { + .fni8 = gen_helper_PDEPD, + .vece = MO_64, + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &g); + + return true; +} + +static bool trans_VPEXTD(DisasContext *ctx, arg_VX *a) +{ + static const GVecGen3 g = { + .fni8 = gen_helper_PEXTD, + .vece = MO_64, + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &g); + + return true; +} + #undef GEN_VR_LDX #undef GEN_VR_STX #undef GEN_VR_LVE From 2c716b4da5251aa8428bd3a9a4a5cc6a6c2c5c89 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:36:58 -0300 Subject: [PATCH 1279/1334] target/ppc: Implement vsldbi/vsrdbi instructions Reviewed-by: Richard Henderson Suggested-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-5-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 8 ++++ target/ppc/translate/vmx-impl.c.inc | 66 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 4666c06f55..257b11113d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -38,6 +38,9 @@ %dx_d 6:s10 16:5 0:1 @DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d +&VN vrt vra vrb sh +@VN ...... vrt:5 vra:5 vrb:5 .. sh:3 ...... &VN + &VX vrt vra vrb @VX ...... vrt:5 vra:5 vrb:5 .......... . &VX @@ -338,3 +341,8 @@ VCLZDM 000100 ..... ..... ..... 11110000100 @VX VCTZDM 000100 ..... ..... ..... 11111000100 @VX VPDEPD 000100 ..... ..... ..... 10111001101 @VX VPEXTD 000100 ..... ..... ..... 10110001101 @VX + +## Vector Permute and Formatting Instruction + +VSLDBI 000100 ..... ..... ..... 00 ... 010110 @VN +VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index cddb3848ab..6edffd5637 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1257,6 +1257,72 @@ static void gen_vsldoi(DisasContext *ctx) tcg_temp_free_i32(sh); } +static bool trans_VSLDBI(DisasContext *ctx, arg_VN *a) +{ + TCGv_i64 t0, t1, t2; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + get_avr64(t0, a->vra, true); + get_avr64(t1, a->vra, false); + + if (a->sh != 0) { + t2 = tcg_temp_new_i64(); + + get_avr64(t2, a->vrb, true); + + tcg_gen_extract2_i64(t0, t1, t0, 64 - a->sh); + tcg_gen_extract2_i64(t1, t2, t1, 64 - a->sh); + + tcg_temp_free_i64(t2); + } + + set_avr64(a->vrt, t0, true); + set_avr64(a->vrt, t1, false); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + + return true; +} + +static bool trans_VSRDBI(DisasContext *ctx, arg_VN *a) +{ + TCGv_i64 t2, t1, t0; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + get_avr64(t0, a->vrb, false); + get_avr64(t1, a->vrb, true); + + if (a->sh != 0) { + t2 = tcg_temp_new_i64(); + + get_avr64(t2, a->vra, false); + + tcg_gen_extract2_i64(t0, t0, t1, a->sh); + tcg_gen_extract2_i64(t1, t1, t2, a->sh); + + tcg_temp_free_i64(t2); + } + + set_avr64(a->vrt, t0, false); + set_avr64(a->vrt, t1, true); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + + return true; +} + #define GEN_VAFORM_PAIRED(name0, name1, opc2) \ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ { \ From 2cc12af399d6f8abb8433ef7f7e75ece177d4d39 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:36:59 -0300 Subject: [PATCH 1280/1334] target/ppc: Implement Vector Insert from GPR using GPR index insns Implements the following PowerISA v3.1 instructions: vinsblx: Vector Insert Byte from GPR using GPR-specified Left-Index vinshlx: Vector Insert Halfword from GPR using GPR-specified Left-Index vinswlx: Vector Insert Word from GPR using GPR-specified Left-Index vinsdlx: Vector Insert Doubleword from GPR using GPR-specified Left-Index vinsbrx: Vector Insert Byte from GPR using GPR-specified Right-Index vinshrx: Vector Insert Halfword from GPR using GPR-specified Right-Index vinswrx: Vector Insert Word from GPR using GPR-specified Right-Index vinsdrx: Vector Insert Doubleword from GPR using GPR-specified Right-Index The helpers and do_vinsx receive i64 to allow code sharing with the future implementation of Vector Insert from VSR using GPR Index. Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-6-matheus.ferst@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/helper.h | 4 +++ target/ppc/insn32.decode | 9 ++++++ target/ppc/int_helper.c | 30 +++++++++++++++++ target/ppc/translate/vmx-impl.c.inc | 50 +++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 0e99f8095c..80f88ce78b 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -228,6 +228,10 @@ DEF_HELPER_3(vinsertb, void, avr, avr, i32) DEF_HELPER_3(vinserth, void, avr, avr, i32) DEF_HELPER_3(vinsertw, void, avr, avr, i32) DEF_HELPER_3(vinsertd, void, avr, avr, i32) +DEF_HELPER_4(VINSBLX, void, env, avr, i64, tl) +DEF_HELPER_4(VINSHLX, void, env, avr, i64, tl) +DEF_HELPER_4(VINSWLX, void, env, avr, i64, tl) +DEF_HELPER_4(VINSDLX, void, env, avr, i64, tl) DEF_HELPER_2(vextsb2w, void, avr, avr) DEF_HELPER_2(vextsh2w, void, avr, avr) DEF_HELPER_2(vextsb2d, void, avr, avr) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 257b11113d..b794424496 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -344,5 +344,14 @@ VPEXTD 000100 ..... ..... ..... 10110001101 @VX ## Vector Permute and Formatting Instruction +VINSBLX 000100 ..... ..... ..... 01000001111 @VX +VINSBRX 000100 ..... ..... ..... 01100001111 @VX +VINSHLX 000100 ..... ..... ..... 01001001111 @VX +VINSHRX 000100 ..... ..... ..... 01101001111 @VX +VINSWLX 000100 ..... ..... ..... 01010001111 @VX +VINSWRX 000100 ..... ..... ..... 01110001111 @VX +VINSDLX 000100 ..... ..... ..... 01011001111 @VX +VINSDRX 000100 ..... ..... ..... 01111001111 @VX + VSLDBI 000100 ..... ..... ..... 00 ... 010110 @VN VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 42541736f1..80b7f8814f 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -1632,6 +1632,36 @@ VINSERT(h, u16) VINSERT(w, u32) VINSERT(d, u64) #undef VINSERT + +#if defined(HOST_WORDS_BIGENDIAN) +#define ELEM_ADDR(VEC, IDX, SIZE) (&(VEC)->u8[IDX]) +#else +#define ELEM_ADDR(VEC, IDX, SIZE) (&(VEC)->u8[15 - (IDX)] - (SIZE) + 1) +#endif + +#define VINSX(SUFFIX, TYPE) \ +void glue(glue(helper_VINS, SUFFIX), LX)(CPUPPCState *env, ppc_avr_t *t, \ + uint64_t val, target_ulong index) \ +{ \ + const int maxidx = ARRAY_SIZE(t->u8) - sizeof(TYPE); \ + target_long idx = index; \ + \ + if (idx < 0 || idx > maxidx) { \ + idx = idx < 0 ? sizeof(TYPE) - idx : idx; \ + qemu_log_mask(LOG_GUEST_ERROR, \ + "Invalid index for Vector Insert Element after 0x" TARGET_FMT_lx \ + ", RA = " TARGET_FMT_ld " > %d\n", env->nip, idx, maxidx); \ + } else { \ + TYPE src = val; \ + memcpy(ELEM_ADDR(t, idx, sizeof(TYPE)), &src, sizeof(TYPE)); \ + } \ +} +VINSX(B, uint8_t) +VINSX(H, uint16_t) +VINSX(W, uint32_t) +VINSX(D, uint64_t) +#undef ELEM_ADDR +#undef VINSX #if defined(HOST_WORDS_BIGENDIAN) #define VEXTRACT(suffix, element) \ void helper_vextract##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 6edffd5637..21af60c616 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1238,6 +1238,56 @@ GEN_VXFORM_DUAL(vspltish, PPC_ALTIVEC, PPC_NONE, GEN_VXFORM_DUAL(vspltisw, PPC_ALTIVEC, PPC_NONE, vinsertw, PPC_NONE, PPC2_ISA300); +static bool do_vinsx(DisasContext *ctx, int vrt, int size, bool right, TCGv ra, + TCGv_i64 rb, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) +{ + TCGv_ptr t; + TCGv idx; + + t = gen_avr_ptr(vrt); + idx = tcg_temp_new(); + + tcg_gen_andi_tl(idx, ra, 0xF); + if (right) { + tcg_gen_subfi_tl(idx, 16 - size, idx); + } + + gen_helper(cpu_env, t, rb, idx); + + tcg_temp_free_ptr(t); + tcg_temp_free(idx); + + return true; +} + +static bool do_vinsx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) +{ + bool ok; + TCGv_i64 val; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + val = tcg_temp_new_i64(); + tcg_gen_extu_tl_i64(val, cpu_gpr[a->vrb]); + + ok = do_vinsx(ctx, a->vrt, size, right, cpu_gpr[a->vra], val, gen_helper); + + tcg_temp_free_i64(val); + return ok; +} + +TRANS(VINSBLX, do_vinsx_VX, 1, false, gen_helper_VINSBLX) +TRANS(VINSHLX, do_vinsx_VX, 2, false, gen_helper_VINSHLX) +TRANS(VINSWLX, do_vinsx_VX, 4, false, gen_helper_VINSWLX) +TRANS(VINSDLX, do_vinsx_VX, 8, false, gen_helper_VINSDLX) + +TRANS(VINSBRX, do_vinsx_VX, 1, true, gen_helper_VINSBLX) +TRANS(VINSHRX, do_vinsx_VX, 2, true, gen_helper_VINSHLX) +TRANS(VINSWRX, do_vinsx_VX, 4, true, gen_helper_VINSWLX) +TRANS(VINSDRX, do_vinsx_VX, 8, true, gen_helper_VINSDLX) + static void gen_vsldoi(DisasContext *ctx) { TCGv_ptr ra, rb, rd; From 23832ae6d53a25e3a56f103fcba55ead17e8e0cf Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:37:00 -0300 Subject: [PATCH 1281/1334] target/ppc: Implement Vector Insert Word from GPR using Immediate insns Implements the following PowerISA v3.1 instructions: vinsw: Vector Insert Word from GPR using immediate-specified index vinsd: Vector Insert Doubleword from GPR using immediate-specified index Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-7-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 6 +++++ target/ppc/translate/vmx-impl.c.inc | 37 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index b794424496..e1f76aac34 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -44,6 +44,9 @@ &VX vrt vra vrb @VX ...... vrt:5 vra:5 vrb:5 .......... . &VX +&VX_uim4 vrt uim vrb +@VX_uim4 ...... vrt:5 . uim:4 vrb:5 ........... &VX_uim4 + &X rt ra rb @X ...... rt:5 ra:5 rb:5 .......... . &X @@ -353,5 +356,8 @@ VINSWRX 000100 ..... ..... ..... 01110001111 @VX VINSDLX 000100 ..... ..... ..... 01011001111 @VX VINSDRX 000100 ..... ..... ..... 01111001111 @VX +VINSW 000100 ..... - .... ..... 00011001111 @VX_uim4 +VINSD 000100 ..... - .... ..... 00111001111 @VX_uim4 + VSLDBI 000100 ..... ..... ..... 00 ... 010110 @VN VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 21af60c616..9642cfa037 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1278,6 +1278,40 @@ static bool do_vinsx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, return ok; } +static bool do_vins_VX_uim4(DisasContext *ctx, arg_VX_uim4 *a, int size, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) +{ + bool ok; + TCGv_i64 val; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + if (a->uim > (16 - size)) { + /* + * PowerISA v3.1 says that the resulting value is undefined in this + * case, so just log a guest error and leave VRT unchanged. The + * real hardware would do a partial insert, e.g. if VRT is zeroed and + * RB is 0x12345678, executing "vinsw VRT,RB,14" results in + * VRT = 0x0000...00001234, but we don't bother to reproduce this + * behavior as software shouldn't rely on it. + */ + qemu_log_mask(LOG_GUEST_ERROR, "Invalid index for VINS* at" + " 0x" TARGET_FMT_lx ", UIM = %d > %d\n", ctx->cia, a->uim, + 16 - size); + return true; + } + + val = tcg_temp_new_i64(); + tcg_gen_extu_tl_i64(val, cpu_gpr[a->vrb]); + + ok = do_vinsx(ctx, a->vrt, size, false, tcg_constant_tl(a->uim), val, + gen_helper); + + tcg_temp_free_i64(val); + return ok; +} + TRANS(VINSBLX, do_vinsx_VX, 1, false, gen_helper_VINSBLX) TRANS(VINSHLX, do_vinsx_VX, 2, false, gen_helper_VINSHLX) TRANS(VINSWLX, do_vinsx_VX, 4, false, gen_helper_VINSWLX) @@ -1288,6 +1322,9 @@ TRANS(VINSHRX, do_vinsx_VX, 2, true, gen_helper_VINSHLX) TRANS(VINSWRX, do_vinsx_VX, 4, true, gen_helper_VINSWLX) TRANS(VINSDRX, do_vinsx_VX, 8, true, gen_helper_VINSDLX) +TRANS(VINSW, do_vins_VX_uim4, 4, gen_helper_VINSWLX) +TRANS(VINSD, do_vins_VX_uim4, 8, gen_helper_VINSDLX) + static void gen_vsldoi(DisasContext *ctx) { TCGv_ptr ra, rb, rd; From 2c9f79584107313e880995ffd2b36f6d28b7bc2e Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:37:01 -0300 Subject: [PATCH 1282/1334] target/ppc: Implement Vector Insert from VSR using GPR index insns Implements the following PowerISA v3.1 instructions: vinsbvlx: Vector Insert Byte from VSR using GPR-specified Left-Index vinshvlx: Vector Insert Halfword from VSR using GPR-specified Left-Index vinswvlx: Vector Insert Word from VSR using GPR-specified Left-Index vinsbvrx: Vector Insert Byte from VSR using GPR-specified Right-Index vinshvrx: Vector Insert Halfword from VSR using GPR-specified Right-Index vinswvrx: Vector Insert Word from VSR using GPR-specified Right-Index Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-8-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 7 +++++++ target/ppc/translate/vmx-impl.c.inc | 32 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index e1f76aac34..de410abf7d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -359,5 +359,12 @@ VINSDRX 000100 ..... ..... ..... 01111001111 @VX VINSW 000100 ..... - .... ..... 00011001111 @VX_uim4 VINSD 000100 ..... - .... ..... 00111001111 @VX_uim4 +VINSBVLX 000100 ..... ..... ..... 00000001111 @VX +VINSBVRX 000100 ..... ..... ..... 00100001111 @VX +VINSHVLX 000100 ..... ..... ..... 00001001111 @VX +VINSHVRX 000100 ..... ..... ..... 00101001111 @VX +VINSWVLX 000100 ..... ..... ..... 00010001111 @VX +VINSWVRX 000100 ..... ..... ..... 00110001111 @VX + VSLDBI 000100 ..... ..... ..... 00 ... 010110 @VN VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 9642cfa037..46d6890242 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1260,6 +1260,20 @@ static bool do_vinsx(DisasContext *ctx, int vrt, int size, bool right, TCGv ra, return true; } +static bool do_vinsvx(DisasContext *ctx, int vrt, int size, bool right, TCGv ra, + int vrb, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) +{ + bool ok; + TCGv_i64 val; + + val = tcg_temp_new_i64(); + get_avr64(val, vrb, true); + ok = do_vinsx(ctx, vrt, size, right, ra, val, gen_helper); + + tcg_temp_free_i64(val); + return ok; +} + static bool do_vinsx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) { @@ -1278,6 +1292,16 @@ static bool do_vinsx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, return ok; } +static bool do_vinsvx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + return do_vinsvx(ctx, a->vrt, size, right, cpu_gpr[a->vra], a->vrb, + gen_helper); +} + static bool do_vins_VX_uim4(DisasContext *ctx, arg_VX_uim4 *a, int size, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) { @@ -1325,6 +1349,14 @@ TRANS(VINSDRX, do_vinsx_VX, 8, true, gen_helper_VINSDLX) TRANS(VINSW, do_vins_VX_uim4, 4, gen_helper_VINSWLX) TRANS(VINSD, do_vins_VX_uim4, 8, gen_helper_VINSDLX) +TRANS(VINSBVLX, do_vinsvx_VX, 1, false, gen_helper_VINSBLX) +TRANS(VINSHVLX, do_vinsvx_VX, 2, false, gen_helper_VINSHLX) +TRANS(VINSWVLX, do_vinsvx_VX, 4, false, gen_helper_VINSWLX) + +TRANS(VINSBVRX, do_vinsvx_VX, 1, true, gen_helper_VINSBLX) +TRANS(VINSHVRX, do_vinsvx_VX, 2, true, gen_helper_VINSHLX) +TRANS(VINSWVRX, do_vinsvx_VX, 4, true, gen_helper_VINSWLX) + static void gen_vsldoi(DisasContext *ctx) { TCGv_ptr ra, rb, rd; From b422c2cb52c7c94d34dfc78d439aa2e653af9337 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:37:02 -0300 Subject: [PATCH 1283/1334] target/ppc: Move vinsertb/vinserth/vinsertw/vinsertd to decodetree Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-9-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/helper.h | 4 ---- target/ppc/insn32.decode | 5 +++++ target/ppc/int_helper.c | 21 ------------------- target/ppc/translate/vmx-impl.c.inc | 32 ++++++++++++++++++++--------- target/ppc/translate/vmx-ops.c.inc | 10 +++------ 5 files changed, 30 insertions(+), 42 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 80f88ce78b..356495f392 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -224,10 +224,6 @@ DEF_HELPER_3(vextractub, void, avr, avr, i32) DEF_HELPER_3(vextractuh, void, avr, avr, i32) DEF_HELPER_3(vextractuw, void, avr, avr, i32) DEF_HELPER_3(vextractd, void, avr, avr, i32) -DEF_HELPER_3(vinsertb, void, avr, avr, i32) -DEF_HELPER_3(vinserth, void, avr, avr, i32) -DEF_HELPER_3(vinsertw, void, avr, avr, i32) -DEF_HELPER_3(vinsertd, void, avr, avr, i32) DEF_HELPER_4(VINSBLX, void, env, avr, i64, tl) DEF_HELPER_4(VINSHLX, void, env, avr, i64, tl) DEF_HELPER_4(VINSWLX, void, env, avr, i64, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index de410abf7d..2eb7fb4e92 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -347,6 +347,11 @@ VPEXTD 000100 ..... ..... ..... 10110001101 @VX ## Vector Permute and Formatting Instruction +VINSERTB 000100 ..... - .... ..... 01100001101 @VX_uim4 +VINSERTH 000100 ..... - .... ..... 01101001101 @VX_uim4 +VINSERTW 000100 ..... - .... ..... 01110001101 @VX_uim4 +VINSERTD 000100 ..... - .... ..... 01111001101 @VX_uim4 + VINSBLX 000100 ..... ..... ..... 01000001111 @VX VINSBRX 000100 ..... ..... ..... 01100001111 @VX VINSHLX 000100 ..... ..... ..... 01001001111 @VX diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 80b7f8814f..6f9479fd53 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -1612,27 +1612,6 @@ void helper_vslo(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) #endif } -#if defined(HOST_WORDS_BIGENDIAN) -#define VINSERT(suffix, element) \ - void helper_vinsert##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ - { \ - memmove(&r->u8[index], &b->u8[8 - sizeof(r->element[0])], \ - sizeof(r->element[0])); \ - } -#else -#define VINSERT(suffix, element) \ - void helper_vinsert##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ - { \ - uint32_t d = (16 - index) - sizeof(r->element[0]); \ - memmove(&r->u8[d], &b->u8[8], sizeof(r->element[0])); \ - } -#endif -VINSERT(b, u8) -VINSERT(h, u16) -VINSERT(w, u32) -VINSERT(d, u64) -#undef VINSERT - #if defined(HOST_WORDS_BIGENDIAN) #define ELEM_ADDR(VEC, IDX, SIZE) (&(VEC)->u8[IDX]) #else diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 46d6890242..6fd18690df 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1217,10 +1217,6 @@ GEN_VXFORM_UIMM_SPLAT(vextractub, 6, 8, 15); GEN_VXFORM_UIMM_SPLAT(vextractuh, 6, 9, 14); GEN_VXFORM_UIMM_SPLAT(vextractuw, 6, 10, 12); GEN_VXFORM_UIMM_SPLAT(vextractd, 6, 11, 8); -GEN_VXFORM_UIMM_SPLAT(vinsertb, 6, 12, 15); -GEN_VXFORM_UIMM_SPLAT(vinserth, 6, 13, 14); -GEN_VXFORM_UIMM_SPLAT(vinsertw, 6, 14, 12); -GEN_VXFORM_UIMM_SPLAT(vinsertd, 6, 15, 8); GEN_VXFORM_UIMM_ENV(vcfux, 5, 12); GEN_VXFORM_UIMM_ENV(vcfsx, 5, 13); GEN_VXFORM_UIMM_ENV(vctuxs, 5, 14); @@ -1231,12 +1227,6 @@ GEN_VXFORM_DUAL(vsplth, PPC_ALTIVEC, PPC_NONE, vextractuh, PPC_NONE, PPC2_ISA300); GEN_VXFORM_DUAL(vspltw, PPC_ALTIVEC, PPC_NONE, vextractuw, PPC_NONE, PPC2_ISA300); -GEN_VXFORM_DUAL(vspltisb, PPC_ALTIVEC, PPC_NONE, - vinsertb, PPC_NONE, PPC2_ISA300); -GEN_VXFORM_DUAL(vspltish, PPC_ALTIVEC, PPC_NONE, - vinserth, PPC_NONE, PPC2_ISA300); -GEN_VXFORM_DUAL(vspltisw, PPC_ALTIVEC, PPC_NONE, - vinsertw, PPC_NONE, PPC2_ISA300); static bool do_vinsx(DisasContext *ctx, int vrt, int size, bool right, TCGv ra, TCGv_i64 rb, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) @@ -1336,6 +1326,23 @@ static bool do_vins_VX_uim4(DisasContext *ctx, arg_VX_uim4 *a, int size, return ok; } +static bool do_vinsert_VX_uim4(DisasContext *ctx, arg_VX_uim4 *a, int size, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VECTOR(ctx); + + if (a->uim > (16 - size)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid index for VINSERT* at" + " 0x" TARGET_FMT_lx ", UIM = %d > %d\n", ctx->cia, a->uim, + 16 - size); + return true; + } + + return do_vinsvx(ctx, a->vrt, size, false, tcg_constant_tl(a->uim), a->vrb, + gen_helper); +} + TRANS(VINSBLX, do_vinsx_VX, 1, false, gen_helper_VINSBLX) TRANS(VINSHLX, do_vinsx_VX, 2, false, gen_helper_VINSHLX) TRANS(VINSWLX, do_vinsx_VX, 4, false, gen_helper_VINSWLX) @@ -1357,6 +1364,11 @@ TRANS(VINSBVRX, do_vinsvx_VX, 1, true, gen_helper_VINSBLX) TRANS(VINSHVRX, do_vinsvx_VX, 2, true, gen_helper_VINSHLX) TRANS(VINSWVRX, do_vinsvx_VX, 4, true, gen_helper_VINSWLX) +TRANS(VINSERTB, do_vinsert_VX_uim4, 1, gen_helper_VINSBLX) +TRANS(VINSERTH, do_vinsert_VX_uim4, 2, gen_helper_VINSHLX) +TRANS(VINSERTW, do_vinsert_VX_uim4, 4, gen_helper_VINSWLX) +TRANS(VINSERTD, do_vinsert_VX_uim4, 8, gen_helper_VINSDLX) + static void gen_vsldoi(DisasContext *ctx) { TCGv_ptr ra, rb, rd; diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index f3f4855111..25ee715b43 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -225,13 +225,9 @@ GEN_VXFORM_DUAL_INV(vsplth, vextractuh, 6, 9, 0x00000000, 0x100000, GEN_VXFORM_DUAL_INV(vspltw, vextractuw, 6, 10, 0x00000000, 0x100000, PPC_ALTIVEC), GEN_VXFORM_300_EXT(vextractd, 6, 11, 0x100000), -GEN_VXFORM_DUAL_INV(vspltisb, vinsertb, 6, 12, 0x00000000, 0x100000, - PPC_ALTIVEC), -GEN_VXFORM_DUAL_INV(vspltish, vinserth, 6, 13, 0x00000000, 0x100000, - PPC_ALTIVEC), -GEN_VXFORM_DUAL_INV(vspltisw, vinsertw, 6, 14, 0x00000000, 0x100000, - PPC_ALTIVEC), -GEN_VXFORM_300_EXT(vinsertd, 6, 15, 0x100000), +GEN_VXFORM(vspltisb, 6, 12), +GEN_VXFORM(vspltish, 6, 13), +GEN_VXFORM(vspltisw, 6, 14), GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06), GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07), GEN_VXFORM_300_EO(vextsb2w, 0x01, 0x18, 0x10), From 28110b72a804e0cd8d5b4a14408ac7c38baa3f2c Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:37:03 -0300 Subject: [PATCH 1284/1334] target/ppc: Implement Vector Extract Double to VSR using GPR index insns Implement the following PowerISA v3.1 instructions: vextdubvlx: Vector Extract Double Unsigned Byte to VSR using GPR-specified Left-Index vextduhvlx: Vector Extract Double Unsigned Halfword to VSR using GPR-specified Left-Index vextduwvlx: Vector Extract Double Unsigned Word to VSR using GPR-specified Left-Index vextddvlx: Vector Extract Double Doubleword to VSR using GPR-specified Left-Index vextdubvrx: Vector Extract Double Unsigned Byte to VSR using GPR-specified Right-Index vextduhvrx: Vector Extract Double Unsigned Halfword to VSR using GPR-specified Right-Index vextduwvrx: Vector Extract Double Unsigned Word to VSR using GPR-specified Right-Index vextddvrx: Vector Extract Double Doubleword to VSR using GPR-specified Right-Index Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Signed-off-by: Luis Pires Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-10-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/helper.h | 4 +++ target/ppc/insn32.decode | 12 +++++++++ target/ppc/int_helper.c | 39 +++++++++++++++++++++++++++++ target/ppc/translate/vmx-impl.c.inc | 37 +++++++++++++++++++++++++++ 4 files changed, 92 insertions(+) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 356495f392..7ff1d055c4 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -334,6 +334,10 @@ DEF_HELPER_2(vextuwlx, tl, tl, avr) DEF_HELPER_2(vextubrx, tl, tl, avr) DEF_HELPER_2(vextuhrx, tl, tl, avr) DEF_HELPER_2(vextuwrx, tl, tl, avr) +DEF_HELPER_5(VEXTDUBVLX, void, env, avr, avr, avr, tl) +DEF_HELPER_5(VEXTDUHVLX, void, env, avr, avr, avr, tl) +DEF_HELPER_5(VEXTDUWVLX, void, env, avr, avr, avr, tl) +DEF_HELPER_5(VEXTDDVLX, void, env, avr, avr, avr, tl) DEF_HELPER_2(vsbox, void, avr, avr) DEF_HELPER_3(vcipher, void, avr, avr, avr) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 2eb7fb4e92..e438177b32 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -38,6 +38,9 @@ %dx_d 6:s10 16:5 0:1 @DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d +&VA vrt vra vrb rc +@VA ...... vrt:5 vra:5 vrb:5 rc:5 ...... &VA + &VN vrt vra vrb sh @VN ...... vrt:5 vra:5 vrb:5 .. sh:3 ...... &VN @@ -347,6 +350,15 @@ VPEXTD 000100 ..... ..... ..... 10110001101 @VX ## Vector Permute and Formatting Instruction +VEXTDUBVLX 000100 ..... ..... ..... ..... 011000 @VA +VEXTDUBVRX 000100 ..... ..... ..... ..... 011001 @VA +VEXTDUHVLX 000100 ..... ..... ..... ..... 011010 @VA +VEXTDUHVRX 000100 ..... ..... ..... ..... 011011 @VA +VEXTDUWVLX 000100 ..... ..... ..... ..... 011100 @VA +VEXTDUWVRX 000100 ..... ..... ..... ..... 011101 @VA +VEXTDDVLX 000100 ..... ..... ..... ..... 011110 @VA +VEXTDDVRX 000100 ..... ..... ..... ..... 011111 @VA + VINSERTB 000100 ..... - .... ..... 01100001101 @VX_uim4 VINSERTH 000100 ..... - .... ..... 01101001101 @VX_uim4 VINSERTW 000100 ..... - .... ..... 01110001101 @VX_uim4 diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 6f9479fd53..b7861776c2 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -1642,6 +1642,45 @@ VINSX(D, uint64_t) #undef ELEM_ADDR #undef VINSX #if defined(HOST_WORDS_BIGENDIAN) +#define VEXTDVLX(NAME, SIZE) \ +void helper_##NAME(CPUPPCState *env, ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, \ + target_ulong index) \ +{ \ + const target_long idx = index; \ + ppc_avr_t tmp[2] = { *a, *b }; \ + memset(t, 0, sizeof(*t)); \ + if (idx >= 0 && idx + SIZE <= sizeof(tmp)) { \ + memcpy(&t->u8[ARRAY_SIZE(t->u8) / 2 - SIZE], (void *)tmp + idx, SIZE); \ + } else { \ + qemu_log_mask(LOG_GUEST_ERROR, "Invalid index for " #NAME " after 0x" \ + TARGET_FMT_lx ", RC = " TARGET_FMT_ld " > %d\n", \ + env->nip, idx < 0 ? SIZE - idx : idx, 32 - SIZE); \ + } \ +} +#else +#define VEXTDVLX(NAME, SIZE) \ +void helper_##NAME(CPUPPCState *env, ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, \ + target_ulong index) \ +{ \ + const target_long idx = index; \ + ppc_avr_t tmp[2] = { *b, *a }; \ + memset(t, 0, sizeof(*t)); \ + if (idx >= 0 && idx + SIZE <= sizeof(tmp)) { \ + memcpy(&t->u8[ARRAY_SIZE(t->u8) / 2], \ + (void *)tmp + sizeof(tmp) - SIZE - idx, SIZE); \ + } else { \ + qemu_log_mask(LOG_GUEST_ERROR, "Invalid index for " #NAME " after 0x" \ + TARGET_FMT_lx ", RC = " TARGET_FMT_ld " > %d\n", \ + env->nip, idx < 0 ? SIZE - idx : idx, 32 - SIZE); \ + } \ +} +#endif +VEXTDVLX(VEXTDUBVLX, 1) +VEXTDVLX(VEXTDUHVLX, 2) +VEXTDVLX(VEXTDUWVLX, 4) +VEXTDVLX(VEXTDDVLX, 8) +#undef VEXTDVLX +#if defined(HOST_WORDS_BIGENDIAN) #define VEXTRACT(suffix, element) \ void helper_vextract##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ { \ diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 6fd18690df..8eb8d3a067 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1228,6 +1228,43 @@ GEN_VXFORM_DUAL(vsplth, PPC_ALTIVEC, PPC_NONE, GEN_VXFORM_DUAL(vspltw, PPC_ALTIVEC, PPC_NONE, vextractuw, PPC_NONE, PPC2_ISA300); +static bool do_vextdx(DisasContext *ctx, arg_VA *a, int size, bool right, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv)) +{ + TCGv_ptr vrt, vra, vrb; + TCGv rc; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + vrt = gen_avr_ptr(a->vrt); + vra = gen_avr_ptr(a->vra); + vrb = gen_avr_ptr(a->vrb); + rc = tcg_temp_new(); + + tcg_gen_andi_tl(rc, cpu_gpr[a->rc], 0x1F); + if (right) { + tcg_gen_subfi_tl(rc, 32 - size, rc); + } + gen_helper(cpu_env, vrt, vra, vrb, rc); + + tcg_temp_free_ptr(vrt); + tcg_temp_free_ptr(vra); + tcg_temp_free_ptr(vrb); + tcg_temp_free(rc); + return true; +} + +TRANS(VEXTDUBVLX, do_vextdx, 1, false, gen_helper_VEXTDUBVLX) +TRANS(VEXTDUHVLX, do_vextdx, 2, false, gen_helper_VEXTDUHVLX) +TRANS(VEXTDUWVLX, do_vextdx, 4, false, gen_helper_VEXTDUWVLX) +TRANS(VEXTDDVLX, do_vextdx, 8, false, gen_helper_VEXTDDVLX) + +TRANS(VEXTDUBVRX, do_vextdx, 1, true, gen_helper_VEXTDUBVLX) +TRANS(VEXTDUHVRX, do_vextdx, 2, true, gen_helper_VEXTDUHVLX) +TRANS(VEXTDUWVRX, do_vextdx, 4, true, gen_helper_VEXTDUWVLX) +TRANS(VEXTDDVRX, do_vextdx, 8, true, gen_helper_VEXTDDVLX) + static bool do_vinsx(DisasContext *ctx, int vrt, int size, bool right, TCGv ra, TCGv_i64 rb, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) { From 8226cb2d9c4c309664ed3f224c29aae536475443 Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Thu, 4 Nov 2021 09:37:04 -0300 Subject: [PATCH 1285/1334] target/ppc: Introduce REQUIRE_VSX macro Introduce the macro to centralize checking if the VSX facility is enabled and handle it correctly. Reviewed-by: Richard Henderson Signed-off-by: Bruno Larsen (billionai) Signed-off-by: Luis Pires Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-11-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/translate.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index fc9d35a7a8..e88b613093 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7354,6 +7354,14 @@ static int times_16(DisasContext *ctx, int x) } \ } while (0) +#define REQUIRE_VSX(CTX) \ + do { \ + if (unlikely(!(CTX)->vsx_enabled)) { \ + gen_exception((CTX), POWERPC_EXCP_VSXU); \ + return true; \ + } \ + } while (0) + #define REQUIRE_FPU(ctx) \ do { \ if (unlikely(!(ctx)->fpu_enabled)) { \ From c2aecae10826c356b043b93b8458825c856c8ba8 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:37:05 -0300 Subject: [PATCH 1286/1334] target/ppc: receive high/low as argument in get/set_cpu_vsr Changes get_cpu_vsr to receive a new argument indicating whether the high or low part of the register is being accessed. This change improves consistency between the interfaces used to access Vector and VSX registers and helps to handle endianness in some cases. Reviewed-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-12-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/translate/vsx-impl.c.inc | 317 +++++++++++++--------------- 1 file changed, 146 insertions(+), 171 deletions(-) diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 57a7f73bba..d923c6a090 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1,23 +1,13 @@ /*** VSX extension ***/ -static inline void get_cpu_vsrh(TCGv_i64 dst, int n) +static inline void get_cpu_vsr(TCGv_i64 dst, int n, bool high) { - tcg_gen_ld_i64(dst, cpu_env, vsr64_offset(n, true)); + tcg_gen_ld_i64(dst, cpu_env, vsr64_offset(n, high)); } -static inline void get_cpu_vsrl(TCGv_i64 dst, int n) +static inline void set_cpu_vsr(int n, TCGv_i64 src, bool high) { - tcg_gen_ld_i64(dst, cpu_env, vsr64_offset(n, false)); -} - -static inline void set_cpu_vsrh(int n, TCGv_i64 src) -{ - tcg_gen_st_i64(src, cpu_env, vsr64_offset(n, true)); -} - -static inline void set_cpu_vsrl(int n, TCGv_i64 src) -{ - tcg_gen_st_i64(src, cpu_env, vsr64_offset(n, false)); + tcg_gen_st_i64(src, cpu_env, vsr64_offset(n, high)); } static inline TCGv_ptr gen_vsr_ptr(int reg) @@ -41,7 +31,7 @@ static void gen_##name(DisasContext *ctx) \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ gen_qemu_##operation(ctx, t0, EA); \ - set_cpu_vsrh(xT(ctx->opcode), t0); \ + set_cpu_vsr(xT(ctx->opcode), t0, true); \ /* NOTE: cpu_vsrl is undefined */ \ tcg_temp_free(EA); \ tcg_temp_free_i64(t0); \ @@ -67,10 +57,10 @@ static void gen_lxvd2x(DisasContext *ctx) EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsrh(xT(ctx->opcode), t0); + set_cpu_vsr(xT(ctx->opcode), t0, true); tcg_gen_addi_tl(EA, EA, 8); gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsrl(xT(ctx->opcode), t0); + set_cpu_vsr(xT(ctx->opcode), t0, false); tcg_temp_free(EA); tcg_temp_free_i64(t0); } @@ -109,8 +99,8 @@ static void gen_lxvw4x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEQ); } - set_cpu_vsrh(xT(ctx->opcode), xth); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xth, true); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free(EA); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -233,8 +223,8 @@ static void gen_lxvh8x(DisasContext *ctx) if (ctx->le_mode) { gen_bswap16x8(xth, xtl, xth, xtl); } - set_cpu_vsrh(xT(ctx->opcode), xth); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xth, true); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free(EA); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -258,8 +248,8 @@ static void gen_lxvb16x(DisasContext *ctx) tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEQ); - set_cpu_vsrh(xT(ctx->opcode), xth); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xth, true); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free(EA); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -301,16 +291,16 @@ static void gen_##name(DisasContext *ctx) \ } \ if (ctx->le_mode) { \ tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \ - set_cpu_vsrl(xt, xtl); \ + set_cpu_vsr(xt, xtl, false); \ tcg_gen_addi_tl(EA, EA, 8); \ tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \ - set_cpu_vsrh(xt, xth); \ + set_cpu_vsr(xt, xth, true); \ } else { \ tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \ - set_cpu_vsrh(xt, xth); \ + set_cpu_vsr(xt, xth, true); \ tcg_gen_addi_tl(EA, EA, 8); \ tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \ - set_cpu_vsrl(xt, xtl); \ + set_cpu_vsr(xt, xtl, false); \ } \ tcg_temp_free(EA); \ tcg_temp_free_i64(xth); \ @@ -347,8 +337,8 @@ static void gen_##name(DisasContext *ctx) \ } \ xth = tcg_temp_new_i64(); \ xtl = tcg_temp_new_i64(); \ - get_cpu_vsrh(xth, xt); \ - get_cpu_vsrl(xtl, xt); \ + get_cpu_vsr(xth, xt, true); \ + get_cpu_vsr(xtl, xt, false); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ if (indexed) { \ @@ -421,7 +411,7 @@ static void gen_##name(DisasContext *ctx) \ EA = tcg_temp_new(); \ gen_addr_imm_index(ctx, EA, 0x03); \ gen_qemu_##operation(ctx, xth, EA); \ - set_cpu_vsrh(rD(ctx->opcode) + 32, xth); \ + set_cpu_vsr(rD(ctx->opcode) + 32, xth, true); \ /* NOTE: cpu_vsrl is undefined */ \ tcg_temp_free(EA); \ tcg_temp_free_i64(xth); \ @@ -443,7 +433,7 @@ static void gen_##name(DisasContext *ctx) \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ - get_cpu_vsrh(t0, xS(ctx->opcode)); \ + get_cpu_vsr(t0, xS(ctx->opcode), true); \ gen_qemu_##operation(ctx, t0, EA); \ tcg_temp_free(EA); \ tcg_temp_free_i64(t0); \ @@ -468,10 +458,10 @@ static void gen_stxvd2x(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); - get_cpu_vsrh(t0, xS(ctx->opcode)); + get_cpu_vsr(t0, xS(ctx->opcode), true); gen_qemu_st64_i64(ctx, t0, EA); tcg_gen_addi_tl(EA, EA, 8); - get_cpu_vsrl(t0, xS(ctx->opcode)); + get_cpu_vsr(t0, xS(ctx->opcode), false); gen_qemu_st64_i64(ctx, t0, EA); tcg_temp_free(EA); tcg_temp_free_i64(t0); @@ -489,8 +479,8 @@ static void gen_stxvw4x(DisasContext *ctx) } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsrh(xsh, xS(ctx->opcode)); - get_cpu_vsrl(xsl, xS(ctx->opcode)); + get_cpu_vsr(xsh, xS(ctx->opcode), true); + get_cpu_vsr(xsl, xS(ctx->opcode), false); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -529,8 +519,8 @@ static void gen_stxvh8x(DisasContext *ctx) } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsrh(xsh, xS(ctx->opcode)); - get_cpu_vsrl(xsl, xS(ctx->opcode)); + get_cpu_vsr(xsh, xS(ctx->opcode), true); + get_cpu_vsr(xsl, xS(ctx->opcode), false); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -566,8 +556,8 @@ static void gen_stxvb16x(DisasContext *ctx) } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsrh(xsh, xS(ctx->opcode)); - get_cpu_vsrl(xsl, xS(ctx->opcode)); + get_cpu_vsr(xsh, xS(ctx->opcode), true); + get_cpu_vsr(xsl, xS(ctx->opcode), false); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -590,7 +580,7 @@ static void gen_##name(DisasContext *ctx) \ return; \ } \ xth = tcg_temp_new_i64(); \ - get_cpu_vsrh(xth, rD(ctx->opcode) + 32); \ + get_cpu_vsr(xth, rD(ctx->opcode) + 32, true); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_imm_index(ctx, EA, 0x03); \ @@ -618,7 +608,7 @@ static void gen_mfvsrwz(DisasContext *ctx) } TCGv_i64 tmp = tcg_temp_new_i64(); TCGv_i64 xsh = tcg_temp_new_i64(); - get_cpu_vsrh(xsh, xS(ctx->opcode)); + get_cpu_vsr(xsh, xS(ctx->opcode), true); tcg_gen_ext32u_i64(tmp, xsh); tcg_gen_trunc_i64_tl(cpu_gpr[rA(ctx->opcode)], tmp); tcg_temp_free_i64(tmp); @@ -642,7 +632,7 @@ static void gen_mtvsrwa(DisasContext *ctx) TCGv_i64 xsh = tcg_temp_new_i64(); tcg_gen_extu_tl_i64(tmp, cpu_gpr[rA(ctx->opcode)]); tcg_gen_ext32s_i64(xsh, tmp); - set_cpu_vsrh(xT(ctx->opcode), xsh); + set_cpu_vsr(xT(ctx->opcode), xsh, true); tcg_temp_free_i64(tmp); tcg_temp_free_i64(xsh); } @@ -664,7 +654,7 @@ static void gen_mtvsrwz(DisasContext *ctx) TCGv_i64 xsh = tcg_temp_new_i64(); tcg_gen_extu_tl_i64(tmp, cpu_gpr[rA(ctx->opcode)]); tcg_gen_ext32u_i64(xsh, tmp); - set_cpu_vsrh(xT(ctx->opcode), xsh); + set_cpu_vsr(xT(ctx->opcode), xsh, true); tcg_temp_free_i64(tmp); tcg_temp_free_i64(xsh); } @@ -685,7 +675,7 @@ static void gen_mfvsrd(DisasContext *ctx) } } t0 = tcg_temp_new_i64(); - get_cpu_vsrh(t0, xS(ctx->opcode)); + get_cpu_vsr(t0, xS(ctx->opcode), true); tcg_gen_mov_i64(cpu_gpr[rA(ctx->opcode)], t0); tcg_temp_free_i64(t0); } @@ -706,7 +696,7 @@ static void gen_mtvsrd(DisasContext *ctx) } t0 = tcg_temp_new_i64(); tcg_gen_mov_i64(t0, cpu_gpr[rA(ctx->opcode)]); - set_cpu_vsrh(xT(ctx->opcode), t0); + set_cpu_vsr(xT(ctx->opcode), t0, true); tcg_temp_free_i64(t0); } @@ -725,7 +715,7 @@ static void gen_mfvsrld(DisasContext *ctx) } } t0 = tcg_temp_new_i64(); - get_cpu_vsrl(t0, xS(ctx->opcode)); + get_cpu_vsr(t0, xS(ctx->opcode), false); tcg_gen_mov_i64(cpu_gpr[rA(ctx->opcode)], t0); tcg_temp_free_i64(t0); } @@ -751,10 +741,10 @@ static void gen_mtvsrdd(DisasContext *ctx) } else { tcg_gen_mov_i64(t0, cpu_gpr[rA(ctx->opcode)]); } - set_cpu_vsrh(xT(ctx->opcode), t0); + set_cpu_vsr(xT(ctx->opcode), t0, true); tcg_gen_mov_i64(t0, cpu_gpr[rB(ctx->opcode)]); - set_cpu_vsrl(xT(ctx->opcode), t0); + set_cpu_vsr(xT(ctx->opcode), t0, false); tcg_temp_free_i64(t0); } @@ -776,8 +766,8 @@ static void gen_mtvsrws(DisasContext *ctx) t0 = tcg_temp_new_i64(); tcg_gen_deposit_i64(t0, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 32, 32); - set_cpu_vsrl(xT(ctx->opcode), t0); - set_cpu_vsrh(xT(ctx->opcode), t0); + set_cpu_vsr(xT(ctx->opcode), t0, false); + set_cpu_vsr(xT(ctx->opcode), t0, true); tcg_temp_free_i64(t0); } @@ -797,33 +787,25 @@ static void gen_xxpermdi(DisasContext *ctx) if (unlikely((xT(ctx->opcode) == xA(ctx->opcode)) || (xT(ctx->opcode) == xB(ctx->opcode)))) { - if ((DM(ctx->opcode) & 2) == 0) { - get_cpu_vsrh(xh, xA(ctx->opcode)); - } else { - get_cpu_vsrl(xh, xA(ctx->opcode)); - } - if ((DM(ctx->opcode) & 1) == 0) { - get_cpu_vsrh(xl, xB(ctx->opcode)); - } else { - get_cpu_vsrl(xl, xB(ctx->opcode)); - } + get_cpu_vsr(xh, xA(ctx->opcode), (DM(ctx->opcode) & 2) == 0); + get_cpu_vsr(xl, xB(ctx->opcode), (DM(ctx->opcode) & 1) == 0); - set_cpu_vsrh(xT(ctx->opcode), xh); - set_cpu_vsrl(xT(ctx->opcode), xl); + set_cpu_vsr(xT(ctx->opcode), xh, true); + set_cpu_vsr(xT(ctx->opcode), xl, false); } else { if ((DM(ctx->opcode) & 2) == 0) { - get_cpu_vsrh(xh, xA(ctx->opcode)); - set_cpu_vsrh(xT(ctx->opcode), xh); + get_cpu_vsr(xh, xA(ctx->opcode), true); + set_cpu_vsr(xT(ctx->opcode), xh, true); } else { - get_cpu_vsrl(xh, xA(ctx->opcode)); - set_cpu_vsrh(xT(ctx->opcode), xh); + get_cpu_vsr(xh, xA(ctx->opcode), false); + set_cpu_vsr(xT(ctx->opcode), xh, true); } if ((DM(ctx->opcode) & 1) == 0) { - get_cpu_vsrh(xl, xB(ctx->opcode)); - set_cpu_vsrl(xT(ctx->opcode), xl); + get_cpu_vsr(xl, xB(ctx->opcode), true); + set_cpu_vsr(xT(ctx->opcode), xl, false); } else { - get_cpu_vsrl(xl, xB(ctx->opcode)); - set_cpu_vsrl(xT(ctx->opcode), xl); + get_cpu_vsr(xl, xB(ctx->opcode), false); + set_cpu_vsr(xT(ctx->opcode), xl, false); } } tcg_temp_free_i64(xh); @@ -847,7 +829,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ } \ xb = tcg_temp_new_i64(); \ sgm = tcg_temp_new_i64(); \ - get_cpu_vsrh(xb, xB(ctx->opcode)); \ + get_cpu_vsr(xb, xB(ctx->opcode), true); \ tcg_gen_movi_i64(sgm, sgn_mask); \ switch (op) { \ case OP_ABS: { \ @@ -864,7 +846,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ } \ case OP_CPSGN: { \ TCGv_i64 xa = tcg_temp_new_i64(); \ - get_cpu_vsrh(xa, xA(ctx->opcode)); \ + get_cpu_vsr(xa, xA(ctx->opcode), true); \ tcg_gen_and_i64(xa, xa, sgm); \ tcg_gen_andc_i64(xb, xb, sgm); \ tcg_gen_or_i64(xb, xb, xa); \ @@ -872,7 +854,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ break; \ } \ } \ - set_cpu_vsrh(xT(ctx->opcode), xb); \ + set_cpu_vsr(xT(ctx->opcode), xb, true); \ tcg_temp_free_i64(xb); \ tcg_temp_free_i64(sgm); \ } @@ -898,8 +880,8 @@ static void glue(gen_, name)(DisasContext *ctx) \ xbl = tcg_temp_new_i64(); \ sgm = tcg_temp_new_i64(); \ tmp = tcg_temp_new_i64(); \ - get_cpu_vsrh(xbh, xb); \ - get_cpu_vsrl(xbl, xb); \ + get_cpu_vsr(xbh, xb, true); \ + get_cpu_vsr(xbl, xb, false); \ tcg_gen_movi_i64(sgm, sgn_mask); \ switch (op) { \ case OP_ABS: \ @@ -914,15 +896,15 @@ static void glue(gen_, name)(DisasContext *ctx) \ case OP_CPSGN: \ xah = tcg_temp_new_i64(); \ xa = rA(ctx->opcode) + 32; \ - get_cpu_vsrh(tmp, xa); \ + get_cpu_vsr(tmp, xa, true); \ tcg_gen_and_i64(xah, tmp, sgm); \ tcg_gen_andc_i64(xbh, xbh, sgm); \ tcg_gen_or_i64(xbh, xbh, xah); \ tcg_temp_free_i64(xah); \ break; \ } \ - set_cpu_vsrh(xt, xbh); \ - set_cpu_vsrl(xt, xbl); \ + set_cpu_vsr(xt, xbh, true); \ + set_cpu_vsr(xt, xbl, false); \ tcg_temp_free_i64(xbl); \ tcg_temp_free_i64(xbh); \ tcg_temp_free_i64(sgm); \ @@ -945,8 +927,8 @@ static void glue(gen_, name)(DisasContext *ctx) \ xbh = tcg_temp_new_i64(); \ xbl = tcg_temp_new_i64(); \ sgm = tcg_temp_new_i64(); \ - get_cpu_vsrh(xbh, xB(ctx->opcode)); \ - get_cpu_vsrl(xbl, xB(ctx->opcode)); \ + get_cpu_vsr(xbh, xB(ctx->opcode), true); \ + get_cpu_vsr(xbl, xB(ctx->opcode), false); \ tcg_gen_movi_i64(sgm, sgn_mask); \ switch (op) { \ case OP_ABS: { \ @@ -967,8 +949,8 @@ static void glue(gen_, name)(DisasContext *ctx) \ case OP_CPSGN: { \ TCGv_i64 xah = tcg_temp_new_i64(); \ TCGv_i64 xal = tcg_temp_new_i64(); \ - get_cpu_vsrh(xah, xA(ctx->opcode)); \ - get_cpu_vsrl(xal, xA(ctx->opcode)); \ + get_cpu_vsr(xah, xA(ctx->opcode), true); \ + get_cpu_vsr(xal, xA(ctx->opcode), false); \ tcg_gen_and_i64(xah, xah, sgm); \ tcg_gen_and_i64(xal, xal, sgm); \ tcg_gen_andc_i64(xbh, xbh, sgm); \ @@ -980,8 +962,8 @@ static void glue(gen_, name)(DisasContext *ctx) \ break; \ } \ } \ - set_cpu_vsrh(xT(ctx->opcode), xbh); \ - set_cpu_vsrl(xT(ctx->opcode), xbl); \ + set_cpu_vsr(xT(ctx->opcode), xbh, true); \ + set_cpu_vsr(xT(ctx->opcode), xbl, false); \ tcg_temp_free_i64(xbh); \ tcg_temp_free_i64(xbl); \ tcg_temp_free_i64(sgm); \ @@ -1193,9 +1175,9 @@ static void gen_##name(DisasContext *ctx) \ } \ t0 = tcg_temp_new_i64(); \ t1 = tcg_temp_new_i64(); \ - get_cpu_vsrh(t0, xB(ctx->opcode)); \ + get_cpu_vsr(t0, xB(ctx->opcode), true); \ gen_helper_##name(t1, cpu_env, t0); \ - set_cpu_vsrh(xT(ctx->opcode), t1); \ + set_cpu_vsr(xT(ctx->opcode), t1, true); \ tcg_temp_free_i64(t0); \ tcg_temp_free_i64(t1); \ } @@ -1390,13 +1372,13 @@ static void gen_xxbrd(DisasContext *ctx) xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); tcg_gen_bswap64_i64(xth, xbh); tcg_gen_bswap64_i64(xtl, xbl); - set_cpu_vsrh(xT(ctx->opcode), xth); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xth, true); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -1419,12 +1401,12 @@ static void gen_xxbrh(DisasContext *ctx) xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); gen_bswap16x8(xth, xtl, xbh, xbl); - set_cpu_vsrh(xT(ctx->opcode), xth); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xth, true); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -1448,15 +1430,15 @@ static void gen_xxbrq(DisasContext *ctx) xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); t0 = tcg_temp_new_i64(); tcg_gen_bswap64_i64(t0, xbl); tcg_gen_bswap64_i64(xtl, xbh); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_gen_mov_i64(xth, t0); - set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsr(xT(ctx->opcode), xth, true); tcg_temp_free_i64(t0); tcg_temp_free_i64(xth); @@ -1480,12 +1462,12 @@ static void gen_xxbrw(DisasContext *ctx) xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); gen_bswap32x4(xth, xtl, xbh, xbl); - set_cpu_vsrh(xT(ctx->opcode), xth); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xth, true); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -1527,23 +1509,16 @@ static void glue(gen_, name)(DisasContext *ctx) \ b0 = tcg_temp_new_i64(); \ b1 = tcg_temp_new_i64(); \ tmp = tcg_temp_new_i64(); \ - if (high) { \ - get_cpu_vsrh(a0, xA(ctx->opcode)); \ - get_cpu_vsrh(a1, xA(ctx->opcode)); \ - get_cpu_vsrh(b0, xB(ctx->opcode)); \ - get_cpu_vsrh(b1, xB(ctx->opcode)); \ - } else { \ - get_cpu_vsrl(a0, xA(ctx->opcode)); \ - get_cpu_vsrl(a1, xA(ctx->opcode)); \ - get_cpu_vsrl(b0, xB(ctx->opcode)); \ - get_cpu_vsrl(b1, xB(ctx->opcode)); \ - } \ + get_cpu_vsr(a0, xA(ctx->opcode), high); \ + get_cpu_vsr(a1, xA(ctx->opcode), high); \ + get_cpu_vsr(b0, xB(ctx->opcode), high); \ + get_cpu_vsr(b1, xB(ctx->opcode), high); \ tcg_gen_shri_i64(a0, a0, 32); \ tcg_gen_shri_i64(b0, b0, 32); \ tcg_gen_deposit_i64(tmp, b0, a0, 32, 32); \ - set_cpu_vsrh(xT(ctx->opcode), tmp); \ + set_cpu_vsr(xT(ctx->opcode), tmp, true); \ tcg_gen_deposit_i64(tmp, b1, a1, 32, 32); \ - set_cpu_vsrl(xT(ctx->opcode), tmp); \ + set_cpu_vsr(xT(ctx->opcode), tmp, false); \ tcg_temp_free_i64(a0); \ tcg_temp_free_i64(a1); \ tcg_temp_free_i64(b0); \ @@ -1624,40 +1599,40 @@ static void gen_xxsldwi(DisasContext *ctx) switch (SHW(ctx->opcode)) { case 0: { - get_cpu_vsrh(xth, xA(ctx->opcode)); - get_cpu_vsrl(xtl, xA(ctx->opcode)); + get_cpu_vsr(xth, xA(ctx->opcode), true); + get_cpu_vsr(xtl, xA(ctx->opcode), false); break; } case 1: { TCGv_i64 t0 = tcg_temp_new_i64(); - get_cpu_vsrh(xth, xA(ctx->opcode)); + get_cpu_vsr(xth, xA(ctx->opcode), true); tcg_gen_shli_i64(xth, xth, 32); - get_cpu_vsrl(t0, xA(ctx->opcode)); + get_cpu_vsr(t0, xA(ctx->opcode), false); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xth, xth, t0); - get_cpu_vsrl(xtl, xA(ctx->opcode)); + get_cpu_vsr(xtl, xA(ctx->opcode), false); tcg_gen_shli_i64(xtl, xtl, 32); - get_cpu_vsrh(t0, xB(ctx->opcode)); + get_cpu_vsr(t0, xB(ctx->opcode), true); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xtl, xtl, t0); tcg_temp_free_i64(t0); break; } case 2: { - get_cpu_vsrl(xth, xA(ctx->opcode)); - get_cpu_vsrh(xtl, xB(ctx->opcode)); + get_cpu_vsr(xth, xA(ctx->opcode), false); + get_cpu_vsr(xtl, xB(ctx->opcode), true); break; } case 3: { TCGv_i64 t0 = tcg_temp_new_i64(); - get_cpu_vsrl(xth, xA(ctx->opcode)); + get_cpu_vsr(xth, xA(ctx->opcode), false); tcg_gen_shli_i64(xth, xth, 32); - get_cpu_vsrh(t0, xB(ctx->opcode)); + get_cpu_vsr(t0, xB(ctx->opcode), true); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xth, xth, t0); - get_cpu_vsrh(xtl, xB(ctx->opcode)); + get_cpu_vsr(xtl, xB(ctx->opcode), true); tcg_gen_shli_i64(xtl, xtl, 32); - get_cpu_vsrl(t0, xB(ctx->opcode)); + get_cpu_vsr(t0, xB(ctx->opcode), false); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xtl, xtl, t0); tcg_temp_free_i64(t0); @@ -1665,8 +1640,8 @@ static void gen_xxsldwi(DisasContext *ctx) } } - set_cpu_vsrh(xT(ctx->opcode), xth); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xth, true); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -1694,8 +1669,8 @@ static void gen_##name(DisasContext *ctx) \ */ \ if (uimm > 15) { \ tcg_gen_movi_i64(t1, 0); \ - set_cpu_vsrh(xT(ctx->opcode), t1); \ - set_cpu_vsrl(xT(ctx->opcode), t1); \ + set_cpu_vsr(xT(ctx->opcode), t1, true); \ + set_cpu_vsr(xT(ctx->opcode), t1, false); \ return; \ } \ tcg_gen_movi_i32(t0, uimm); \ @@ -1719,7 +1694,7 @@ static void gen_xsxexpdp(DisasContext *ctx) return; } t0 = tcg_temp_new_i64(); - get_cpu_vsrh(t0, xB(ctx->opcode)); + get_cpu_vsr(t0, xB(ctx->opcode), true); tcg_gen_extract_i64(rt, t0, 52, 11); tcg_temp_free_i64(t0); } @@ -1737,12 +1712,12 @@ static void gen_xsxexpqp(DisasContext *ctx) xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, rB(ctx->opcode) + 32); + get_cpu_vsr(xbh, rB(ctx->opcode) + 32, true); tcg_gen_extract_i64(xth, xbh, 48, 15); - set_cpu_vsrh(rD(ctx->opcode) + 32, xth); + set_cpu_vsr(rD(ctx->opcode) + 32, xth, true); tcg_gen_movi_i64(xtl, 0); - set_cpu_vsrl(rD(ctx->opcode) + 32, xtl); + set_cpu_vsr(rD(ctx->opcode) + 32, xtl, false); tcg_temp_free_i64(xbh); tcg_temp_free_i64(xth); @@ -1766,7 +1741,7 @@ static void gen_xsiexpdp(DisasContext *ctx) tcg_gen_andi_i64(t0, rb, 0x7FF); tcg_gen_shli_i64(t0, t0, 52); tcg_gen_or_i64(xth, xth, t0); - set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsr(xT(ctx->opcode), xth, true); /* dword[1] is undefined */ tcg_temp_free_i64(t0); tcg_temp_free_i64(xth); @@ -1789,19 +1764,19 @@ static void gen_xsiexpqp(DisasContext *ctx) xtl = tcg_temp_new_i64(); xah = tcg_temp_new_i64(); xal = tcg_temp_new_i64(); - get_cpu_vsrh(xah, rA(ctx->opcode) + 32); - get_cpu_vsrl(xal, rA(ctx->opcode) + 32); + get_cpu_vsr(xah, rA(ctx->opcode) + 32, true); + get_cpu_vsr(xal, rA(ctx->opcode) + 32, false); xbh = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, rB(ctx->opcode) + 32); + get_cpu_vsr(xbh, rB(ctx->opcode) + 32, true); t0 = tcg_temp_new_i64(); tcg_gen_andi_i64(xth, xah, 0x8000FFFFFFFFFFFF); tcg_gen_andi_i64(t0, xbh, 0x7FFF); tcg_gen_shli_i64(t0, t0, 48); tcg_gen_or_i64(xth, xth, t0); - set_cpu_vsrh(rD(ctx->opcode) + 32, xth); + set_cpu_vsr(rD(ctx->opcode) + 32, xth, true); tcg_gen_mov_i64(xtl, xal); - set_cpu_vsrl(rD(ctx->opcode) + 32, xtl); + set_cpu_vsr(rD(ctx->opcode) + 32, xtl, false); tcg_temp_free_i64(t0); tcg_temp_free_i64(xth); @@ -1826,12 +1801,12 @@ static void gen_xsxsigdp(DisasContext *ctx) zr = tcg_const_i64(0); nan = tcg_const_i64(2047); - get_cpu_vsrh(t1, xB(ctx->opcode)); + get_cpu_vsr(t1, xB(ctx->opcode), true); tcg_gen_extract_i64(exp, t1, 52, 11); tcg_gen_movi_i64(t0, 0x0010000000000000); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); - get_cpu_vsrh(t1, xB(ctx->opcode)); + get_cpu_vsr(t1, xB(ctx->opcode), true); tcg_gen_deposit_i64(rt, t0, t1, 0, 52); tcg_temp_free_i64(t0); @@ -1857,8 +1832,8 @@ static void gen_xsxsigqp(DisasContext *ctx) xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, rB(ctx->opcode) + 32); - get_cpu_vsrl(xbl, rB(ctx->opcode) + 32); + get_cpu_vsr(xbh, rB(ctx->opcode) + 32, true); + get_cpu_vsr(xbl, rB(ctx->opcode) + 32, false); exp = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); zr = tcg_const_i64(0); @@ -1869,9 +1844,9 @@ static void gen_xsxsigqp(DisasContext *ctx) tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); tcg_gen_deposit_i64(xth, t0, xbh, 0, 48); - set_cpu_vsrh(rD(ctx->opcode) + 32, xth); + set_cpu_vsr(rD(ctx->opcode) + 32, xth, true); tcg_gen_mov_i64(xtl, xbl); - set_cpu_vsrl(rD(ctx->opcode) + 32, xtl); + set_cpu_vsr(rD(ctx->opcode) + 32, xtl, false); tcg_temp_free_i64(t0); tcg_temp_free_i64(exp); @@ -1904,22 +1879,22 @@ static void gen_xviexpsp(DisasContext *ctx) xal = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xah, xA(ctx->opcode)); - get_cpu_vsrl(xal, xA(ctx->opcode)); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xah, xA(ctx->opcode), true); + get_cpu_vsr(xal, xA(ctx->opcode), false); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); t0 = tcg_temp_new_i64(); tcg_gen_andi_i64(xth, xah, 0x807FFFFF807FFFFF); tcg_gen_andi_i64(t0, xbh, 0xFF000000FF); tcg_gen_shli_i64(t0, t0, 23); tcg_gen_or_i64(xth, xth, t0); - set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsr(xT(ctx->opcode), xth, true); tcg_gen_andi_i64(xtl, xal, 0x807FFFFF807FFFFF); tcg_gen_andi_i64(t0, xbl, 0xFF000000FF); tcg_gen_shli_i64(t0, t0, 23); tcg_gen_or_i64(xtl, xtl, t0); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(t0); tcg_temp_free_i64(xth); @@ -1949,16 +1924,16 @@ static void gen_xviexpdp(DisasContext *ctx) xal = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xah, xA(ctx->opcode)); - get_cpu_vsrl(xal, xA(ctx->opcode)); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xah, xA(ctx->opcode), true); + get_cpu_vsr(xal, xA(ctx->opcode), false); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); tcg_gen_deposit_i64(xth, xah, xbh, 52, 11); - set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsr(xT(ctx->opcode), xth, true); tcg_gen_deposit_i64(xtl, xal, xbl, 52, 11); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -1983,15 +1958,15 @@ static void gen_xvxexpsp(DisasContext *ctx) xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); tcg_gen_shri_i64(xth, xbh, 23); tcg_gen_andi_i64(xth, xth, 0xFF000000FF); - set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsr(xT(ctx->opcode), xth, true); tcg_gen_shri_i64(xtl, xbl, 23); tcg_gen_andi_i64(xtl, xtl, 0xFF000000FF); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -2014,13 +1989,13 @@ static void gen_xvxexpdp(DisasContext *ctx) xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); tcg_gen_extract_i64(xth, xbh, 52, 11); - set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsr(xT(ctx->opcode), xth, true); tcg_gen_extract_i64(xtl, xbl, 52, 11); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -2046,8 +2021,8 @@ static void gen_xvxsigdp(DisasContext *ctx) xtl = tcg_temp_new_i64(); xbh = tcg_temp_new_i64(); xbl = tcg_temp_new_i64(); - get_cpu_vsrh(xbh, xB(ctx->opcode)); - get_cpu_vsrl(xbl, xB(ctx->opcode)); + get_cpu_vsr(xbh, xB(ctx->opcode), true); + get_cpu_vsr(xbl, xB(ctx->opcode), false); exp = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); zr = tcg_const_i64(0); @@ -2058,14 +2033,14 @@ static void gen_xvxsigdp(DisasContext *ctx) tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); tcg_gen_deposit_i64(xth, t0, xbh, 0, 52); - set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsr(xT(ctx->opcode), xth, true); tcg_gen_extract_i64(exp, xbl, 52, 11); tcg_gen_movi_i64(t0, 0x0010000000000000); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); tcg_gen_deposit_i64(xtl, t0, xbl, 0, 52); - set_cpu_vsrl(xT(ctx->opcode), xtl); + set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_temp_free_i64(t0); tcg_temp_free_i64(exp); From 72b70d5c3cdca7eaa7be448a1dc91af52167b9ab Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Thu, 4 Nov 2021 09:37:06 -0300 Subject: [PATCH 1287/1334] target/ppc: moved stxv and lxv from legacy to decodtree Moved stxv and lxv implementation from the legacy system to decodetree. Reviewed-by: Richard Henderson Signed-off-by: Luis Pires Signed-off-by: Lucas Mateus Castro (alqotel) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-13-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 8 +++++ target/ppc/translate.c | 17 ++-------- target/ppc/translate/vsx-impl.c.inc | 51 +++++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index e438177b32..296d6d6c5a 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -28,6 +28,9 @@ %dq_rtp 22:4 !function=times_2 @DQ_rtp ...... ....0 ra:5 ............ .... &D rt=%dq_rtp si=%dq_si +%dq_rt_tsx 3:1 21:5 +@DQ_TSX ...... ..... ra:5 ............ .... &D si=%dq_si rt=%dq_rt_tsx + %ds_si 2:s14 !function=times_4 @DS ...... rt:5 ra:5 .............. .. &D si=%ds_si @@ -385,3 +388,8 @@ VINSWVRX 000100 ..... ..... ..... 00110001111 @VX VSLDBI 000100 ..... ..... ..... 00 ... 010110 @VN VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN + +# VSX Load/Store Instructions + +LXV 111101 ..... ..... ............ . 001 @DQ_TSX +STXV 111101 ..... ..... ............ . 101 @DQ_TSX diff --git a/target/ppc/translate.c b/target/ppc/translate.c index e88b613093..9960df6e18 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7446,20 +7446,7 @@ static void gen_dform39(DisasContext *ctx) /* handles stfdp, lxv, stxsd, stxssp lxvx */ static void gen_dform3D(DisasContext *ctx) { - if ((ctx->opcode & 3) == 1) { /* DQ-FORM */ - switch (ctx->opcode & 0x7) { - case 1: /* lxv */ - if (ctx->insns_flags2 & PPC2_ISA300) { - return gen_lxv(ctx); - } - break; - case 5: /* stxv */ - if (ctx->insns_flags2 & PPC2_ISA300) { - return gen_stxv(ctx); - } - break; - } - } else { /* DS-FORM */ + if ((ctx->opcode & 3) != 1) { /* DS-FORM */ switch (ctx->opcode & 0x3) { case 0: /* stfdp */ if (ctx->insns_flags2 & PPC2_ISA205) { @@ -7584,7 +7571,7 @@ GEN_HANDLER2_E(extswsli1, "extswsli", 0x1F, 0x1B, 0x1B, 0x00000000, #endif /* handles lfdp, lxsd, lxssp */ GEN_HANDLER_E(dform39, 0x39, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205), -/* handles stfdp, lxv, stxsd, stxssp, stxv */ +/* handles stfdp, stxsd, stxssp */ GEN_HANDLER_E(dform3D, 0x3D, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205), GEN_HANDLER(lmw, 0x2E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(stmw, 0x2F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index d923c6a090..9da66b5348 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -307,7 +307,6 @@ static void gen_##name(DisasContext *ctx) \ tcg_temp_free_i64(xtl); \ } -VSX_VECTOR_LOAD(lxv, ld_i64, 0) VSX_VECTOR_LOAD(lxvx, ld_i64, 1) #define VSX_VECTOR_STORE(name, op, indexed) \ @@ -360,7 +359,6 @@ static void gen_##name(DisasContext *ctx) \ tcg_temp_free_i64(xtl); \ } -VSX_VECTOR_STORE(stxv, st_i64, 0) VSX_VECTOR_STORE(stxvx, st_i64, 1) #ifdef TARGET_PPC64 @@ -2052,6 +2050,55 @@ static void gen_xvxsigdp(DisasContext *ctx) tcg_temp_free_i64(xbl); } +static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, + int rt, bool store) +{ + TCGv ea; + TCGv_i64 xt; + MemOp mop; + + xt = tcg_temp_new_i64(); + + mop = DEF_MEMOP(MO_Q); + + gen_set_access_type(ctx, ACCESS_INT); + ea = do_ea_calc(ctx, ra, displ); + + if (store) { + get_cpu_vsr(xt, rt, !ctx->le_mode); + tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + gen_addr_add(ctx, ea, ea, 8); + get_cpu_vsr(xt, rt, ctx->le_mode); + tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + } else { + tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); + set_cpu_vsr(rt, xt, !ctx->le_mode); + gen_addr_add(ctx, ea, ea, 8); + tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); + set_cpu_vsr(rt, xt, ctx->le_mode); + } + + tcg_temp_free(ea); + tcg_temp_free_i64(xt); + return true; +} + +static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + + if (a->rt >= 32) { + REQUIRE_VSX(ctx); + } else { + REQUIRE_VECTOR(ctx); + } + + return do_lstxv(ctx, a->ra, tcg_constant_tl(a->si), a->rt, store); +} + +TRANS(STXV, do_lstxv_D, true) +TRANS(LXV, do_lstxv_D, false) + #undef GEN_XX2FORM #undef GEN_XX3FORM #undef GEN_XX2IFORM From 70426b5bb738c3d8b70d6f7484c2a9b91739ac54 Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Thu, 4 Nov 2021 09:37:07 -0300 Subject: [PATCH 1288/1334] target/ppc: moved stxvx and lxvx from legacy to decodtree Moved stxvx and lxvx implementation from the legacy system to decodetree. Reviewed-by: Richard Henderson Signed-off-by: Lucas Mateus Castro (alqotel) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-14-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 5 ++ target/ppc/translate/vsx-impl.c.inc | 121 ++++------------------------ target/ppc/translate/vsx-ops.c.inc | 2 - 3 files changed, 20 insertions(+), 108 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 296d6d6c5a..3ce26b2e6e 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -103,6 +103,9 @@ @X_tbp_s_rc ...... ....0 s:1 .... ....0 .......... rc:1 &X_tb_s_rc rt=%x_frtp rb=%x_frbp +%x_rt_tsx 0:1 21:5 +@X_TSX ...... ..... ra:5 rb:5 .......... . &X rt=%x_rt_tsx + &X_frtp_vrb frtp vrb @X_frtp_vrb ...... ....0 ..... vrb:5 .......... . &X_frtp_vrb frtp=%x_frtp @@ -393,3 +396,5 @@ VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN LXV 111101 ..... ..... ............ . 001 @DQ_TSX STXV 111101 ..... ..... ............ . 101 @DQ_TSX +LXVX 011111 ..... ..... ..... 0100 - 01100 . @X_TSX +STXVX 011111 ..... ..... ..... 0110001100 . @X_TSX diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 9da66b5348..1973bb18f3 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -255,112 +255,6 @@ static void gen_lxvb16x(DisasContext *ctx) tcg_temp_free_i64(xtl); } -#define VSX_VECTOR_LOAD(name, op, indexed) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - int xt; \ - TCGv EA; \ - TCGv_i64 xth; \ - TCGv_i64 xtl; \ - \ - if (indexed) { \ - xt = xT(ctx->opcode); \ - } else { \ - xt = DQxT(ctx->opcode); \ - } \ - \ - if (xt < 32) { \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - } else { \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - } \ - xth = tcg_temp_new_i64(); \ - xtl = tcg_temp_new_i64(); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - if (indexed) { \ - gen_addr_reg_index(ctx, EA); \ - } else { \ - gen_addr_imm_index(ctx, EA, 0x0F); \ - } \ - if (ctx->le_mode) { \ - tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \ - set_cpu_vsr(xt, xtl, false); \ - tcg_gen_addi_tl(EA, EA, 8); \ - tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \ - set_cpu_vsr(xt, xth, true); \ - } else { \ - tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \ - set_cpu_vsr(xt, xth, true); \ - tcg_gen_addi_tl(EA, EA, 8); \ - tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \ - set_cpu_vsr(xt, xtl, false); \ - } \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(xth); \ - tcg_temp_free_i64(xtl); \ -} - -VSX_VECTOR_LOAD(lxvx, ld_i64, 1) - -#define VSX_VECTOR_STORE(name, op, indexed) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - int xt; \ - TCGv EA; \ - TCGv_i64 xth; \ - TCGv_i64 xtl; \ - \ - if (indexed) { \ - xt = xT(ctx->opcode); \ - } else { \ - xt = DQxT(ctx->opcode); \ - } \ - \ - if (xt < 32) { \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - } else { \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - } \ - xth = tcg_temp_new_i64(); \ - xtl = tcg_temp_new_i64(); \ - get_cpu_vsr(xth, xt, true); \ - get_cpu_vsr(xtl, xt, false); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - if (indexed) { \ - gen_addr_reg_index(ctx, EA); \ - } else { \ - gen_addr_imm_index(ctx, EA, 0x0F); \ - } \ - if (ctx->le_mode) { \ - tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \ - tcg_gen_addi_tl(EA, EA, 8); \ - tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \ - } else { \ - tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \ - tcg_gen_addi_tl(EA, EA, 8); \ - tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \ - } \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(xth); \ - tcg_temp_free_i64(xtl); \ -} - -VSX_VECTOR_STORE(stxvx, st_i64, 1) - #ifdef TARGET_PPC64 #define VSX_VECTOR_LOAD_STORE_LENGTH(name) \ static void gen_##name(DisasContext *ctx) \ @@ -2096,8 +1990,23 @@ static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store) return do_lstxv(ctx, a->ra, tcg_constant_tl(a->si), a->rt, store); } +static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + + if (a->rt >= 32) { + REQUIRE_VSX(ctx); + } else { + REQUIRE_VECTOR(ctx); + } + + return do_lstxv(ctx, a->ra, cpu_gpr[a->rb], a->rt, store); +} + TRANS(STXV, do_lstxv_D, true) TRANS(LXV, do_lstxv_D, false) +TRANS(STXVX, do_lstxv_X, true) +TRANS(LXVX, do_lstxv_X, false) #undef GEN_XX2FORM #undef GEN_XX3FORM diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index 1d41beef26..b94f3fa4e0 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -10,7 +10,6 @@ GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvh8x, 0x1F, 0x0C, 0x19, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(lxvb16x, 0x1F, 0x0C, 0x1B, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvx, 0x1F, 0x0C, 0x08, 0x00000040, PPC_NONE, PPC2_ISA300), #if defined(TARGET_PPC64) GEN_HANDLER_E(lxvl, 0x1F, 0x0D, 0x08, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(lxvll, 0x1F, 0x0D, 0x09, 0, PPC_NONE, PPC2_ISA300), @@ -25,7 +24,6 @@ GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(stxvb16x, 0x1F, 0x0C, 0x1F, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxvx, 0x1F, 0x0C, 0x0C, 0, PPC_NONE, PPC2_ISA300), #if defined(TARGET_PPC64) GEN_HANDLER_E(stxvl, 0x1F, 0x0D, 0x0C, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(stxvll, 0x1F, 0x0D, 0x0D, 0, PPC_NONE, PPC2_ISA300), From 96fa2632472c5cde4d64c579647a0fbf0e6617da Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Thu, 4 Nov 2021 09:37:08 -0300 Subject: [PATCH 1289/1334] target/ppc: added the instructions LXVP and STXVP Implemented the instructions lxvp and stxvp using decodetree Reviewed-by: Richard Henderson Signed-off-by: Luis Pires Signed-off-by: Lucas Mateus Castro (alqotel) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-15-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 5 +++ target/ppc/translate/vsx-impl.c.inc | 55 ++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 3ce26b2e6e..c252dec02f 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -31,6 +31,9 @@ %dq_rt_tsx 3:1 21:5 @DQ_TSX ...... ..... ra:5 ............ .... &D si=%dq_si rt=%dq_rt_tsx +%rt_tsxp 21:1 22:4 !function=times_2 +@DQ_TSXP ...... ..... ra:5 ............ .... &D si=%dq_si rt=%rt_tsxp + %ds_si 2:s14 !function=times_4 @DS ...... rt:5 ra:5 .............. .. &D si=%ds_si @@ -396,5 +399,7 @@ VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN LXV 111101 ..... ..... ............ . 001 @DQ_TSX STXV 111101 ..... ..... ............ . 101 @DQ_TSX +LXVP 000110 ..... ..... ............ 0000 @DQ_TSXP +STXVP 000110 ..... ..... ............ 0001 @DQ_TSXP LXVX 011111 ..... ..... ..... 0100 - 01100 . @X_TSX STXVX 011111 ..... ..... ..... 0110001100 . @X_TSX diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 1973bb18f3..05bf6ea40c 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1945,11 +1945,12 @@ static void gen_xvxsigdp(DisasContext *ctx) } static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, - int rt, bool store) + int rt, bool store, bool paired) { TCGv ea; TCGv_i64 xt; MemOp mop; + int rt1, rt2; xt = tcg_temp_new_i64(); @@ -1958,18 +1959,42 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, gen_set_access_type(ctx, ACCESS_INT); ea = do_ea_calc(ctx, ra, displ); + if (paired && ctx->le_mode) { + rt1 = rt + 1; + rt2 = rt; + } else { + rt1 = rt; + rt2 = rt + 1; + } + if (store) { - get_cpu_vsr(xt, rt, !ctx->le_mode); + get_cpu_vsr(xt, rt1, !ctx->le_mode); tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt, ctx->le_mode); + get_cpu_vsr(xt, rt1, ctx->le_mode); tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + if (paired) { + gen_addr_add(ctx, ea, ea, 8); + get_cpu_vsr(xt, rt2, !ctx->le_mode); + tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + gen_addr_add(ctx, ea, ea, 8); + get_cpu_vsr(xt, rt2, ctx->le_mode); + tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + } } else { tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt, xt, !ctx->le_mode); + set_cpu_vsr(rt1, xt, !ctx->le_mode); gen_addr_add(ctx, ea, ea, 8); tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt, xt, ctx->le_mode); + set_cpu_vsr(rt1, xt, ctx->le_mode); + if (paired) { + gen_addr_add(ctx, ea, ea, 8); + tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); + set_cpu_vsr(rt2, xt, !ctx->le_mode); + gen_addr_add(ctx, ea, ea, 8); + tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); + set_cpu_vsr(rt2, xt, ctx->le_mode); + } } tcg_temp_free(ea); @@ -1977,17 +2002,21 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, return true; } -static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store) +static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store, bool paired) { - REQUIRE_INSNS_FLAGS2(ctx, ISA300); + if (paired) { + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + } else { + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + } - if (a->rt >= 32) { + if (paired || a->rt >= 32) { REQUIRE_VSX(ctx); } else { REQUIRE_VECTOR(ctx); } - return do_lstxv(ctx, a->ra, tcg_constant_tl(a->si), a->rt, store); + return do_lstxv(ctx, a->ra, tcg_constant_tl(a->si), a->rt, store, paired); } static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store) @@ -2000,11 +2029,13 @@ static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store) REQUIRE_VECTOR(ctx); } - return do_lstxv(ctx, a->ra, cpu_gpr[a->rb], a->rt, store); + return do_lstxv(ctx, a->ra, cpu_gpr[a->rb], a->rt, store, false); } -TRANS(STXV, do_lstxv_D, true) -TRANS(LXV, do_lstxv_D, false) +TRANS(STXV, do_lstxv_D, true, false) +TRANS(LXV, do_lstxv_D, false, false) +TRANS(STXVP, do_lstxv_D, true, true) +TRANS(LXVP, do_lstxv_D, false, true) TRANS(STXVX, do_lstxv_X, true) TRANS(LXVX, do_lstxv_X, false) From 226ce506b1d3fa121128e22d5f3c0a3c438a388c Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Thu, 4 Nov 2021 09:37:09 -0300 Subject: [PATCH 1290/1334] target/ppc: added the instructions LXVPX and STXVPX Implemented the instructions lxvpx and stxvpx using decodetree Reviewed-by: Richard Henderson Signed-off-by: Lucas Mateus Castro (alqotel) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-16-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 3 +++ target/ppc/translate/vsx-impl.c.inc | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index c252dec02f..e4508631b0 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -108,6 +108,7 @@ %x_rt_tsx 0:1 21:5 @X_TSX ...... ..... ra:5 rb:5 .......... . &X rt=%x_rt_tsx +@X_TSXP ...... ..... ra:5 rb:5 .......... . &X rt=%rt_tsxp &X_frtp_vrb frtp vrb @X_frtp_vrb ...... ....0 ..... vrb:5 .......... . &X_frtp_vrb frtp=%x_frtp @@ -403,3 +404,5 @@ LXVP 000110 ..... ..... ............ 0000 @DQ_TSXP STXVP 000110 ..... ..... ............ 0001 @DQ_TSXP LXVX 011111 ..... ..... ..... 0100 - 01100 . @X_TSX STXVX 011111 ..... ..... ..... 0110001100 . @X_TSX +LXVPX 011111 ..... ..... ..... 0101001101 - @X_TSXP +STXVPX 011111 ..... ..... ..... 0111001101 - @X_TSXP diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 05bf6ea40c..c66505ac71 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2019,25 +2019,31 @@ static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store, bool paired) return do_lstxv(ctx, a->ra, tcg_constant_tl(a->si), a->rt, store, paired); } -static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store) +static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store, bool paired) { - REQUIRE_INSNS_FLAGS2(ctx, ISA300); + if (paired) { + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + } else { + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + } - if (a->rt >= 32) { + if (paired || a->rt >= 32) { REQUIRE_VSX(ctx); } else { REQUIRE_VECTOR(ctx); } - return do_lstxv(ctx, a->ra, cpu_gpr[a->rb], a->rt, store, false); + return do_lstxv(ctx, a->ra, cpu_gpr[a->rb], a->rt, store, paired); } TRANS(STXV, do_lstxv_D, true, false) TRANS(LXV, do_lstxv_D, false, false) TRANS(STXVP, do_lstxv_D, true, true) TRANS(LXVP, do_lstxv_D, false, true) -TRANS(STXVX, do_lstxv_X, true) -TRANS(LXVX, do_lstxv_X, false) +TRANS(STXVX, do_lstxv_X, true, false) +TRANS(LXVX, do_lstxv_X, false, false) +TRANS(STXVPX, do_lstxv_X, true, true) +TRANS(LXVPX, do_lstxv_X, false, true) #undef GEN_XX2FORM #undef GEN_XX3FORM From 5301d0219c7b281542d5bdf0f84817703c62b464 Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Thu, 4 Nov 2021 09:37:10 -0300 Subject: [PATCH 1291/1334] target/ppc: added the instructions PLXV and PSTXV Implemented the instructions plxv and pstxv using decodetree Reviewed-by: Richard Henderson Signed-off-by: Lucas Mateus Castro (alqotel) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-17-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn64.decode | 10 ++++++++++ target/ppc/translate/vsx-impl.c.inc | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index 48756cd4ca..093439b370 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -23,6 +23,9 @@ @PLS_D ...... .. ... r:1 .. .................. \ ...... rt:5 ra:5 ................ \ &PLS_D si=%pls_si +@8LS_D_TSX ...... .. . .. r:1 .. .................. \ + ..... rt:6 ra:5 ................ \ + &PLS_D si=%pls_si ### Fixed-Point Load Instructions @@ -137,3 +140,10 @@ PSTFD 000001 10 0--.-- .................. \ PNOP ................................ \ -------------------------------- @PNOP } + +### VSX instructions + +PLXV 000001 00 0--.-- .................. \ + 11001 ...... ..... ................ @8LS_D_TSX +PSTXV 000001 00 0--.-- .................. \ + 11011 ...... ..... ................ @8LS_D_TSX diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index c66505ac71..1972127a22 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2019,6 +2019,20 @@ static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store, bool paired) return do_lstxv(ctx, a->ra, tcg_constant_tl(a->si), a->rt, store, paired); } +static bool do_lstxv_PLS_D(DisasContext *ctx, arg_PLS_D *a, + bool store, bool paired) +{ + arg_D d; + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + if (!resolve_PLS_D(ctx, &d, a)) { + return true; + } + + return do_lstxv(ctx, d.ra, tcg_constant_tl(d.si), d.rt, store, paired); +} + static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store, bool paired) { if (paired) { @@ -2044,6 +2058,8 @@ TRANS(STXVX, do_lstxv_X, true, false) TRANS(LXVX, do_lstxv_X, false, false) TRANS(STXVPX, do_lstxv_X, true, true) TRANS(LXVPX, do_lstxv_X, false, true) +TRANS64(PSTXV, do_lstxv_PLS_D, true, false) +TRANS64(PLXV, do_lstxv_PLS_D, false, false) #undef GEN_XX2FORM #undef GEN_XX3FORM From dcbf48316f98a71e03ccdb1cd2d8709a90062078 Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Thu, 4 Nov 2021 09:37:11 -0300 Subject: [PATCH 1292/1334] target/ppc: added the instructions PLXVP and PSTXVP Implemented the instructions plxvp and pstxvp using decodetree Reviewed-by: Richard Henderson Signed-off-by: Lucas Mateus Castro (alqotel) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-18-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn64.decode | 9 +++++++++ target/ppc/translate/vsx-impl.c.inc | 2 ++ 2 files changed, 11 insertions(+) diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index 093439b370..880ac3edc7 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -27,6 +27,11 @@ ..... rt:6 ra:5 ................ \ &PLS_D si=%pls_si +%rt_tsxp 21:1 22:4 !function=times_2 +@8LS_D_TSXP ...... .. . .. r:1 .. .................. \ + ...... ..... ra:5 ................ \ + &PLS_D si=%pls_si rt=%rt_tsxp + ### Fixed-Point Load Instructions PLBZ 000001 10 0--.-- .................. \ @@ -147,3 +152,7 @@ PLXV 000001 00 0--.-- .................. \ 11001 ...... ..... ................ @8LS_D_TSX PSTXV 000001 00 0--.-- .................. \ 11011 ...... ..... ................ @8LS_D_TSX +PLXVP 000001 00 0--.-- .................. \ + 111010 ..... ..... ................ @8LS_D_TSXP +PSTXVP 000001 00 0--.-- .................. \ + 111110 ..... ..... ................ @8LS_D_TSXP diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 1972127a22..6c60e29cca 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2060,6 +2060,8 @@ TRANS(STXVPX, do_lstxv_X, true, true) TRANS(LXVPX, do_lstxv_X, false, true) TRANS64(PSTXV, do_lstxv_PLS_D, true, false) TRANS64(PLXV, do_lstxv_PLS_D, false, false) +TRANS64(PSTXVP, do_lstxv_PLS_D, true, true) +TRANS64(PLXVP, do_lstxv_PLS_D, false, true) #undef GEN_XX2FORM #undef GEN_XX3FORM From 30dfca8d8f83c936565c080a5e52de0bba86651d Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Thu, 4 Nov 2021 09:37:12 -0300 Subject: [PATCH 1293/1334] target/ppc: moved XXSPLTW to using decodetree Changed the function that handles XXSPLTW emulation to using decodetree, but still using the same logic. Reviewed-by: Richard Henderson Signed-off-by: Bruno Larsen (billionai) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-19-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 9 +++++++++ target/ppc/translate/vsx-impl.c.inc | 17 ++++++----------- target/ppc/translate/vsx-ops.c.inc | 1 - 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index e4508631b0..5d425ec076 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -116,6 +116,11 @@ &X_vrt_frbp vrt frbp @X_vrt_frbp ...... vrt:5 ..... ....0 .......... . &X_vrt_frbp frbp=%x_frbp +&XX2 xt xb uim:uint8_t +%xx2_xt 0:1 21:5 +%xx2_xb 1:1 11:5 +@XX2 ...... ..... ... uim:2 ..... ......... .. &XX2 xt=%xx2_xt xb=%xx2_xb + &Z22_bf_fra bf fra dm @Z22_bf_fra ...... bf:3 .. fra:5 dm:6 ......... . &Z22_bf_fra @@ -406,3 +411,7 @@ LXVX 011111 ..... ..... ..... 0100 - 01100 . @X_TSX STXVX 011111 ..... ..... ..... 0110001100 . @X_TSX LXVPX 011111 ..... ..... ..... 0101001101 - @X_TSXP STXVPX 011111 ..... ..... ..... 0111001101 - @X_TSXP + +## VSX splat instruction + +XXSPLTW 111100 ..... ---.. ..... 010100100 . . @XX2 diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 6c60e29cca..ce8796d139 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1436,26 +1436,21 @@ static void gen_xxsel(DisasContext *ctx) vsr_full_offset(rb), vsr_full_offset(ra), 16, 16); } -static void gen_xxspltw(DisasContext *ctx) +static bool trans_XXSPLTW(DisasContext *ctx, arg_XX2 *a) { - int rt = xT(ctx->opcode); - int rb = xB(ctx->opcode); - int uim = UIM(ctx->opcode); int tofs, bofs; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + REQUIRE_VSX(ctx); - tofs = vsr_full_offset(rt); - bofs = vsr_full_offset(rb); - bofs += uim << MO_32; + tofs = vsr_full_offset(a->xt); + bofs = vsr_full_offset(a->xb); + bofs += a->uim << MO_32; #ifndef HOST_WORDS_BIG_ENDIAN bofs ^= 8 | 4; #endif tcg_gen_gvec_dup_mem(MO_32, tofs, bofs, 16, 16); + return true; } #define pattern(x) (((x) & 0xff) * (~(uint64_t)0 / 0xff)) diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index b94f3fa4e0..b669b64d35 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -348,7 +348,6 @@ GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), GEN_XX3FORM(xxperm, 0x08, 0x03, PPC2_ISA300), GEN_XX3FORM(xxpermr, 0x08, 0x07, PPC2_ISA300), -GEN_XX2FORM(xxspltw, 0x08, 0x0A, PPC2_VSX), GEN_XX1FORM(xxspltib, 0x08, 0x0B, PPC2_ISA300), GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00), GEN_XX2FORM_EXT(xxextractuw, 0x0A, 0x0A, PPC2_ISA300), From 6166fced10f635b789807df23af70037031ea56d Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Thu, 4 Nov 2021 09:37:13 -0300 Subject: [PATCH 1294/1334] target/ppc: moved XXSPLTIB to using decodetree Changed the function that handles XXSPLTIB emulation to using decodetree, but still use the same logic as before Reviewed-by: Richard Henderson Signed-off-by: Bruno Larsen (billionai) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-20-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 5 +++++ target/ppc/translate/vsx-impl.c.inc | 20 ++++++-------------- target/ppc/translate/vsx-ops.c.inc | 1 - 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 5d425ec076..fd73946122 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -96,6 +96,10 @@ &X_bfl bf l:bool ra rb @X_bfl ...... bf:3 - l:1 ra:5 rb:5 ..........- &X_bfl +%x_xt 0:1 21:5 +&X_imm8 xt imm:uint8_t +@X_imm8 ...... ..... .. imm:8 .......... . &X_imm8 xt=%x_xt + &X_tb_sp_rc rt rb sp rc:bool @X_tb_sp_rc ...... rt:5 sp:2 ... rb:5 .......... rc:1 &X_tb_sp_rc @@ -414,4 +418,5 @@ STXVPX 011111 ..... ..... ..... 0111001101 - @X_TSXP ## VSX splat instruction +XXSPLTIB 111100 ..... 00 ........ 0101101000 . @X_imm8 XXSPLTW 111100 ..... ---.. ..... 010100100 . . @XX2 diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index ce8796d139..ad25a0daf0 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1455,23 +1455,15 @@ static bool trans_XXSPLTW(DisasContext *ctx, arg_XX2 *a) #define pattern(x) (((x) & 0xff) * (~(uint64_t)0 / 0xff)) -static void gen_xxspltib(DisasContext *ctx) +static bool trans_XXSPLTIB(DisasContext *ctx, arg_X_imm8 *a) { - uint8_t uim8 = IMM8(ctx->opcode); - int rt = xT(ctx->opcode); - - if (rt < 32) { - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + if (a->xt < 32) { + REQUIRE_VSX(ctx); } else { - if (unlikely(!ctx->altivec_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VPU); - return; - } + REQUIRE_VECTOR(ctx); } - tcg_gen_gvec_dup_imm(MO_8, vsr_full_offset(rt), 16, 16, uim8); + tcg_gen_gvec_dup_imm(MO_8, vsr_full_offset(a->xt), 16, 16, a->imm); + return true; } static void gen_xxsldwi(DisasContext *ctx) diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index b669b64d35..152d1e5c3b 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -348,7 +348,6 @@ GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), GEN_XX3FORM(xxperm, 0x08, 0x03, PPC2_ISA300), GEN_XX3FORM(xxpermr, 0x08, 0x07, PPC2_ISA300), -GEN_XX1FORM(xxspltib, 0x08, 0x0B, PPC2_ISA300), GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00), GEN_XX2FORM_EXT(xxextractuw, 0x0A, 0x0A, PPC2_ISA300), GEN_XX2FORM_EXT(xxinsertw, 0x0A, 0x0B, PPC2_ISA300), From aa4592fab7cc28ef230ecab5c7e5b4d3936917ce Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Thu, 4 Nov 2021 09:37:14 -0300 Subject: [PATCH 1295/1334] target/ppc: implemented XXSPLTI32DX Implemented XXSPLTI32DX emulation using decodetree Reviewed-by: Richard Henderson Signed-off-by: Bruno Larsen (billionai) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-21-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn64.decode | 11 +++++++++++ target/ppc/translate/vsx-impl.c.inc | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index 880ac3edc7..134bc60c57 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -32,6 +32,14 @@ ...... ..... ra:5 ................ \ &PLS_D si=%pls_si rt=%rt_tsxp +# Format 8RR:D +%8rr_si 32:s16 0:16 +%8rr_xt 16:1 21:5 +&8RR_D_IX xt ix si +@8RR_D_IX ...... .. .... .. .. ................ \ + ...... ..... ... ix:1 . ................ \ + &8RR_D_IX si=%8rr_si xt=%8rr_xt + ### Fixed-Point Load Instructions PLBZ 000001 10 0--.-- .................. \ @@ -156,3 +164,6 @@ PLXVP 000001 00 0--.-- .................. \ 111010 ..... ..... ................ @8LS_D_TSXP PSTXVP 000001 00 0--.-- .................. \ 111110 ..... ..... ................ @8LS_D_TSXP + +XXSPLTI32DX 000001 01 0000 -- -- ................ \ + 100000 ..... 000 .. ................ @8RR_D_IX diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index ad25a0daf0..360593a9ab 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1466,6 +1466,23 @@ static bool trans_XXSPLTIB(DisasContext *ctx, arg_X_imm8 *a) return true; } +static bool trans_XXSPLTI32DX(DisasContext *ctx, arg_8RR_D_IX *a) +{ + TCGv_i32 imm; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + imm = tcg_constant_i32(a->si); + + tcg_gen_st_i32(imm, cpu_env, + offsetof(CPUPPCState, vsr[a->xt].VsrW(0 + a->ix))); + tcg_gen_st_i32(imm, cpu_env, + offsetof(CPUPPCState, vsr[a->xt].VsrW(2 + a->ix))); + + return true; +} + static void gen_xxsldwi(DisasContext *ctx) { TCGv_i64 xth, xtl; From ec10f73eb92c0f72891808e58d4d47932e39797b Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Thu, 4 Nov 2021 09:37:15 -0300 Subject: [PATCH 1296/1334] target/ppc: Implemented XXSPLTIW using decodetree Implemented the XXSPLTIW instruction, using decodetree. Reviewed-by: Richard Henderson Signed-off-by: Bruno Larsen (billionai) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-22-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn64.decode | 6 ++++++ target/ppc/translate/vsx-impl.c.inc | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index 134bc60c57..bd71f616cc 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -39,6 +39,10 @@ @8RR_D_IX ...... .. .... .. .. ................ \ ...... ..... ... ix:1 . ................ \ &8RR_D_IX si=%8rr_si xt=%8rr_xt +&8RR_D xt si:int32_t +@8RR_D ...... .. .... .. .. ................ \ + ...... ..... .... . ................ \ + &8RR_D si=%8rr_si xt=%8rr_xt ### Fixed-Point Load Instructions @@ -165,5 +169,7 @@ PLXVP 000001 00 0--.-- .................. \ PSTXVP 000001 00 0--.-- .................. \ 111110 ..... ..... ................ @8LS_D_TSXP +XXSPLTIW 000001 01 0000 -- -- ................ \ + 100000 ..... 0011 . ................ @8RR_D XXSPLTI32DX 000001 01 0000 -- -- ................ \ 100000 ..... 000 .. ................ @8RR_D_IX diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 360593a9ab..7116141a6a 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1466,6 +1466,16 @@ static bool trans_XXSPLTIB(DisasContext *ctx, arg_X_imm8 *a) return true; } +static bool trans_XXSPLTIW(DisasContext *ctx, arg_8RR_D *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + tcg_gen_gvec_dup_imm(MO_32, vsr_full_offset(a->xt), 16, 16, a->si); + + return true; +} + static bool trans_XXSPLTI32DX(DisasContext *ctx, arg_8RR_D_IX *a) { TCGv_i32 imm; From 236a628599b2d1307b3f3d095e0c15d7b7524f83 Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Thu, 4 Nov 2021 09:37:16 -0300 Subject: [PATCH 1297/1334] target/ppc: implemented XXSPLTIDP instruction Implemented the instruction XXSPLTIDP using decodetree. Reviewed-by: Richard Henderson Signed-off-by: Bruno Larsen (billionai) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-23-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn64.decode | 2 ++ target/ppc/translate/vsx-impl.c.inc | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index bd71f616cc..20aa2b4615 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -169,6 +169,8 @@ PLXVP 000001 00 0--.-- .................. \ PSTXVP 000001 00 0--.-- .................. \ 111110 ..... ..... ................ @8LS_D_TSXP +XXSPLTIDP 000001 01 0000 -- -- ................ \ + 100000 ..... 0010 . ................ @8RR_D XXSPLTIW 000001 01 0000 -- -- ................ \ 100000 ..... 0011 . ................ @8RR_D XXSPLTI32DX 000001 01 0000 -- -- ................ \ diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 7116141a6a..180d329f1a 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1476,6 +1476,16 @@ static bool trans_XXSPLTIW(DisasContext *ctx, arg_8RR_D *a) return true; } +static bool trans_XXSPLTIDP(DisasContext *ctx, arg_8RR_D *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + tcg_gen_gvec_dup_imm(MO_64, vsr_full_offset(a->xt), 16, 16, + helper_todouble(a->si)); + return true; +} + static bool trans_XXSPLTI32DX(DisasContext *ctx, arg_8RR_D_IX *a) { TCGv_i32 imm; From 788c63998ce6e17b7d7f2e30d28ee5979a6d6cec Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:37:17 -0300 Subject: [PATCH 1298/1334] target/ppc: Implement xxblendvb/xxblendvh/xxblendvw/xxblendvd instructions Reviewed-by: Richard Henderson Signed-off-by: Bruno Larsen (billionai) Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-24-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/helper.h | 4 +++ target/ppc/insn64.decode | 19 ++++++++++ target/ppc/int_helper.c | 15 ++++++++ target/ppc/translate/vsx-impl.c.inc | 55 +++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 7ff1d055c4..627811cefc 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -520,6 +520,10 @@ DEF_HELPER_4(xxpermr, void, env, vsr, vsr, vsr) DEF_HELPER_4(xxextractuw, void, env, vsr, vsr, i32) DEF_HELPER_4(xxinsertw, void, env, vsr, vsr, i32) DEF_HELPER_3(xvxsigsp, void, env, vsr, vsr) +DEF_HELPER_5(XXBLENDVB, void, vsr, vsr, vsr, vsr, i32) +DEF_HELPER_5(XXBLENDVH, void, vsr, vsr, vsr, vsr, i32) +DEF_HELPER_5(XXBLENDVW, void, vsr, vsr, vsr, vsr, i32) +DEF_HELPER_5(XXBLENDVD, void, vsr, vsr, vsr, vsr, i32) DEF_HELPER_2(efscfsi, i32, env, i32) DEF_HELPER_2(efscfui, i32, env, i32) diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index 20aa2b4615..39e610913d 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -44,6 +44,16 @@ ...... ..... .... . ................ \ &8RR_D si=%8rr_si xt=%8rr_xt +# Format XX4 +&XX4 xt xa xb xc +%xx4_xt 0:1 21:5 +%xx4_xa 2:1 16:5 +%xx4_xb 1:1 11:5 +%xx4_xc 3:1 6:5 +@XX4 ........ ........ ........ ........ \ + ...... ..... ..... ..... ..... .. .... \ + &XX4 xt=%xx4_xt xa=%xx4_xa xb=%xx4_xb xc=%xx4_xc + ### Fixed-Point Load Instructions PLBZ 000001 10 0--.-- .................. \ @@ -175,3 +185,12 @@ XXSPLTIW 000001 01 0000 -- -- ................ \ 100000 ..... 0011 . ................ @8RR_D XXSPLTI32DX 000001 01 0000 -- -- ................ \ 100000 ..... 000 .. ................ @8RR_D_IX + +XXBLENDVD 000001 01 0000 -- ------------------ \ + 100001 ..... ..... ..... ..... 11 .... @XX4 +XXBLENDVW 000001 01 0000 -- ------------------ \ + 100001 ..... ..... ..... ..... 10 .... @XX4 +XXBLENDVH 000001 01 0000 -- ------------------ \ + 100001 ..... ..... ..... ..... 01 .... @XX4 +XXBLENDVB 000001 01 0000 -- ------------------ \ + 100001 ..... ..... ..... ..... 00 .... @XX4 diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index b7861776c2..9bc327bcba 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -1737,6 +1737,21 @@ void helper_xxinsertw(CPUPPCState *env, ppc_vsr_t *xt, *xt = t; } +#define XXBLEND(name, sz) \ +void glue(helper_XXBLENDV, name)(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, \ + ppc_avr_t *c, uint32_t desc) \ +{ \ + for (int i = 0; i < ARRAY_SIZE(t->glue(u, sz)); i++) { \ + t->glue(u, sz)[i] = (c->glue(s, sz)[i] >> (sz - 1)) ? \ + b->glue(u, sz)[i] : a->glue(u, sz)[i]; \ + } \ +} +XXBLEND(B, 8) +XXBLEND(H, 16) +XXBLEND(W, 32) +XXBLEND(D, 64) +#undef XXBLEND + #define VEXT_SIGNED(name, element, cast) \ void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ { \ diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 180d329f1a..d1de0da877 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2087,6 +2087,61 @@ TRANS64(PLXV, do_lstxv_PLS_D, false, false) TRANS64(PSTXVP, do_lstxv_PLS_D, true, true) TRANS64(PLXVP, do_lstxv_PLS_D, false, true) +static void gen_xxblendv_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, + TCGv_vec c) +{ + TCGv_vec tmp = tcg_temp_new_vec_matching(c); + tcg_gen_sari_vec(vece, tmp, c, (8 << vece) - 1); + tcg_gen_bitsel_vec(vece, t, tmp, b, a); + tcg_temp_free_vec(tmp); +} + +static bool do_xxblendv(DisasContext *ctx, arg_XX4 *a, unsigned vece) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, 0 + }; + static const GVecGen4 ops[4] = { + { + .fniv = gen_xxblendv_vec, + .fno = gen_helper_XXBLENDVB, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_xxblendv_vec, + .fno = gen_helper_XXBLENDVH, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_xxblendv_vec, + .fno = gen_helper_XXBLENDVW, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_xxblendv_vec, + .fno = gen_helper_XXBLENDVD, + .opt_opc = vecop_list, + .vece = MO_64 + } + }; + + REQUIRE_VSX(ctx); + + tcg_gen_gvec_4(vsr_full_offset(a->xt), vsr_full_offset(a->xa), + vsr_full_offset(a->xb), vsr_full_offset(a->xc), + 16, 16, &ops[vece]); + + return true; +} + +TRANS(XXBLENDVB, do_xxblendv, MO_8) +TRANS(XXBLENDVH, do_xxblendv, MO_16) +TRANS(XXBLENDVW, do_xxblendv, MO_32) +TRANS(XXBLENDVD, do_xxblendv, MO_64) + #undef GEN_XX2FORM #undef GEN_XX3FORM #undef GEN_XX2IFORM From 6e26b85de5f00629e074aef9260a7632a8978f08 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:37:18 -0300 Subject: [PATCH 1299/1334] target/ppc: Implement lxvkq instruction Reviewed-by: Richard Henderson Signed-off-by: Luis Pires Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-25-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/insn32.decode | 7 +++++ target/ppc/translate/vsx-impl.c.inc | 43 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index fd73946122..e135b8aba4 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -100,6 +100,9 @@ &X_imm8 xt imm:uint8_t @X_imm8 ...... ..... .. imm:8 .......... . &X_imm8 xt=%x_xt +&X_uim5 xt uim:uint8_t +@X_uim5 ...... ..... ..... uim:5 .......... . &X_uim5 xt=%x_xt + &X_tb_sp_rc rt rb sp rc:bool @X_tb_sp_rc ...... rt:5 sp:2 ... rb:5 .......... rc:1 &X_tb_sp_rc @@ -420,3 +423,7 @@ STXVPX 011111 ..... ..... ..... 0111001101 - @X_TSXP XXSPLTIB 111100 ..... 00 ........ 0101101000 . @X_imm8 XXSPLTW 111100 ..... ---.. ..... 010100100 . . @XX2 + +## VSX Vector Load Special Value Instruction + +LXVKQ 111100 ..... 11111 ..... 0101101000 . @X_uim5 diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index d1de0da877..c0e38060b4 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1503,6 +1503,49 @@ static bool trans_XXSPLTI32DX(DisasContext *ctx, arg_8RR_D_IX *a) return true; } +static bool trans_LXVKQ(DisasContext *ctx, arg_X_uim5 *a) +{ + static const uint64_t values[32] = { + 0, /* Unspecified */ + 0x3FFF000000000000llu, /* QP +1.0 */ + 0x4000000000000000llu, /* QP +2.0 */ + 0x4000800000000000llu, /* QP +3.0 */ + 0x4001000000000000llu, /* QP +4.0 */ + 0x4001400000000000llu, /* QP +5.0 */ + 0x4001800000000000llu, /* QP +6.0 */ + 0x4001C00000000000llu, /* QP +7.0 */ + 0x7FFF000000000000llu, /* QP +Inf */ + 0x7FFF800000000000llu, /* QP dQNaN */ + 0, /* Unspecified */ + 0, /* Unspecified */ + 0, /* Unspecified */ + 0, /* Unspecified */ + 0, /* Unspecified */ + 0, /* Unspecified */ + 0x8000000000000000llu, /* QP -0.0 */ + 0xBFFF000000000000llu, /* QP -1.0 */ + 0xC000000000000000llu, /* QP -2.0 */ + 0xC000800000000000llu, /* QP -3.0 */ + 0xC001000000000000llu, /* QP -4.0 */ + 0xC001400000000000llu, /* QP -5.0 */ + 0xC001800000000000llu, /* QP -6.0 */ + 0xC001C00000000000llu, /* QP -7.0 */ + 0xFFFF000000000000llu, /* QP -Inf */ + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + if (values[a->uim]) { + set_cpu_vsr(a->xt, tcg_constant_i64(0x0), false); + set_cpu_vsr(a->xt, tcg_constant_i64(values[a->uim]), true); + } else { + gen_invalid(ctx); + } + + return true; +} + static void gen_xxsldwi(DisasContext *ctx) { TCGv_i64 xth, xtl; From ab1e25ad2f2fcc3c3d0f01b24f272a2bc2a42e3e Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 4 Nov 2021 09:37:19 -0300 Subject: [PATCH 1300/1334] target/ppc: cntlzdm/cnttzdm implementation without brcond Suggested-by: Richard Henderson Signed-off-by: Matheus Ferst Message-Id: <20211104123719.323713-26-matheus.ferst@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate/fixedpoint-impl.c.inc | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index e093562e2a..7fecff4579 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -416,32 +416,33 @@ static bool trans_CFUGED(DisasContext *ctx, arg_X *a) static void do_cntzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask, int64_t trail) { - TCGv_i64 tmp; - TCGLabel *l1; + TCGv_i64 t0, t1; - tmp = tcg_temp_local_new_i64(); - l1 = gen_new_label(); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); - tcg_gen_and_i64(tmp, src, mask); + tcg_gen_and_i64(t0, src, mask); if (trail) { - tcg_gen_ctzi_i64(tmp, tmp, 64); + tcg_gen_ctzi_i64(t0, t0, -1); } else { - tcg_gen_clzi_i64(tmp, tmp, 64); + tcg_gen_clzi_i64(t0, t0, -1); } - tcg_gen_brcondi_i64(TCG_COND_EQ, tmp, 0, l1); - - tcg_gen_subfi_i64(tmp, 64, tmp); + tcg_gen_setcondi_i64(TCG_COND_NE, t1, t0, -1); + tcg_gen_andi_i64(t0, t0, 63); + tcg_gen_xori_i64(t0, t0, 63); if (trail) { - tcg_gen_shl_i64(tmp, mask, tmp); + tcg_gen_shl_i64(t0, mask, t0); + tcg_gen_shl_i64(t0, t0, t1); } else { - tcg_gen_shr_i64(tmp, mask, tmp); + tcg_gen_shr_i64(t0, mask, t0); + tcg_gen_shr_i64(t0, t0, t1); } - tcg_gen_ctpop_i64(tmp, tmp); - gen_set_label(l1); + tcg_gen_ctpop_i64(dst, t0); - tcg_gen_mov_i64(dst, tmp); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } static bool trans_CNTLZDM(DisasContext *ctx, arg_X *a) From 14fe3222e5bd74f5075b774416dd193852f1e535 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 5 Nov 2021 14:28:51 +1100 Subject: [PATCH 1301/1334] target/ppc, hw/ppc: Change maintainers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As our day jobs and interests have moved onto other things, Greg and I have been struggling to keep on top of maintainership of target/ppc and associated pieces like the pseries and powernv machine types, with their platform specific devices. We've therefore discussed and plan to transfer maintainership to Cédric Le Goater (primary) and Daniel Henrique Barboza (backup). Cédric and Daniel have been actively contributing to the area for some time, and they're supported in this by their current employer, IBM, who has an obvious interest in the platform. Greg and I do plan to stay around in some capacity for at least the next 6 months, providing reviews and advice to assist the new maintainers into the role. Signed-off-by: David Gibson Reviewed-by: Daniel Henrique Barboza Reviewed-by: Greg Kurz Acked-by: Cédric Le Goater --- MAINTAINERS | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 53b63df407..d3879aa3c1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -262,8 +262,10 @@ F: hw/openrisc/ F: tests/tcg/openrisc/ PowerPC TCG CPUs -M: David Gibson -M: Greg Kurz +M: Cédric Le Goater +M: Daniel Henrique Barboza +R: David Gibson +R: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: target/ppc/ @@ -382,8 +384,10 @@ F: target/mips/kvm* F: target/mips/sysemu/ PPC KVM CPUs -M: David Gibson -M: Greg Kurz +M: Cédric Le Goater +M: Daniel Henrique Barboza +R: David Gibson +R: Greg Kurz S: Maintained F: target/ppc/kvm.c @@ -1321,8 +1325,10 @@ F: include/hw/rtc/m48t59.h F: tests/avocado/ppc_prep_40p.py sPAPR -M: David Gibson -M: Greg Kurz +M: Cédric Le Goater +M: Daniel Henrique Barboza +R: David Gibson +R: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: hw/*/spapr* @@ -1382,6 +1388,8 @@ F: include/hw/pci-host/mv64361.h Virtual Open Firmware (VOF) M: Alexey Kardashevskiy +R: Cédric Le Goater +R: Daniel Henrique Barboza R: David Gibson R: Greg Kurz L: qemu-ppc@nongnu.org From 71e6fae3a994ab5c69e37d6a52a30c840883fbfb Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 5 Nov 2021 23:51:37 +1000 Subject: [PATCH 1302/1334] spapr_numa.c: FORM2 table handle nodes with no distance info A configuration that specifies multiple nodes without distance info results in the non-local points in the FORM2 matrix having a distance of 0. This causes Linux to complain "Invalid distance value range" because a node distance is smaller than the local distance. Fix this by building a simple local / remote fallback for points where distance information is missing. Signed-off-by: Nicholas Piggin Message-Id: <20211105135137.1584840-1-npiggin@gmail.com> Reviewed-by: Daniel Henrique Barboza Signed-off-by: David Gibson --- hw/ppc/spapr_numa.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index 5822938448..56ab2a5fb6 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -546,12 +546,24 @@ static void spapr_numa_FORM2_write_rtas_tables(SpaprMachineState *spapr, * NUMA nodes, but QEMU adds the default NUMA node without * adding the numa_info to retrieve distance info from. */ - if (src == dst) { - distance_table[i++] = NUMA_DISTANCE_MIN; - continue; + distance_table[i] = numa_info[src].distance[dst]; + if (distance_table[i] == 0) { + /* + * In case QEMU adds a default NUMA single node when the user + * did not add any, or where the user did not supply distances, + * the value will be 0 here. Populate the table with a fallback + * simple local / remote distance. + */ + if (src == dst) { + distance_table[i] = NUMA_DISTANCE_MIN; + } else { + distance_table[i] = numa_info[src].distance[dst]; + if (distance_table[i] < NUMA_DISTANCE_MIN) { + distance_table[i] = NUMA_DISTANCE_DEFAULT; + } + } } - - distance_table[i++] = numa_info[src].distance[dst]; + i++; } } From adc903a6c00531022114ffc4efe99ffbe92a22c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyman=20Huang=28=E9=BB=84=E5=8B=87=29?= Date: Mon, 8 Nov 2021 14:51:00 +0800 Subject: [PATCH 1303/1334] docs: fix 'sample-pages' option tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f78d4ed701 has fixed qemu tag, making 'sample-pages' option tag involved by accident, which introduced since 6.1 in commit 7afa08cd8fd. revert this line. Signed-off-by: Hyman Huang(黄勇) Reviewed-by: Markus Armbruster Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- qapi/migration.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/migration.json b/qapi/migration.json index f0aefdab64..bbfd48cf0b 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1796,7 +1796,7 @@ # @calc-time: time in units of second for sample dirty pages # # @sample-pages: page count per GB for sample dirty pages -# the default value is 512 (since 6.2) +# the default value is 512 (since 6.1) # # @mode: mode containing method of calculate dirtyrate includes # 'page-sampling' and 'dirty-ring' (Since 6.2) From a6a83cef9c581d210fd965fc7ac17e388db840dc Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Tue, 9 Nov 2021 11:04:55 +0800 Subject: [PATCH 1304/1334] Reduce the PVM stop time during Checkpoint When flushing memory from ram cache to ram during every checkpoint on secondary VM, we can copy continuous chunks of memory instead of 4096 bytes per time to reduce the time of VM stop during checkpoint. Signed-off-by: Lei Rao Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Lukas Straub Reviewed-by: Juan Quintela Tested-by: Lukas Straub Signed-off-by: Juan Quintela --- migration/ram.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 847af461f2..f48cf4b0a5 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -836,6 +836,41 @@ migration_clear_memory_region_dirty_bitmap_range(RAMBlock *rb, } } +/* + * colo_bitmap_find_diry:find contiguous dirty pages from start + * + * Returns the page offset within memory region of the start of the contiguout + * dirty page + * + * @rs: current RAM state + * @rb: RAMBlock where to search for dirty pages + * @start: page where we start the search + * @num: the number of contiguous dirty pages + */ +static inline +unsigned long colo_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, + unsigned long start, unsigned long *num) +{ + unsigned long size = rb->used_length >> TARGET_PAGE_BITS; + unsigned long *bitmap = rb->bmap; + unsigned long first, next; + + *num = 0; + + if (ramblock_is_ignored(rb)) { + return size; + } + + first = find_next_bit(bitmap, size, start); + if (first >= size) { + return first; + } + next = find_next_zero_bit(bitmap, size, first + 1); + assert(next >= first); + *num = next - first; + return first; +} + static inline bool migration_bitmap_clear_dirty(RAMState *rs, RAMBlock *rb, unsigned long page) @@ -3886,19 +3921,26 @@ void colo_flush_ram_cache(void) block = QLIST_FIRST_RCU(&ram_list.blocks); while (block) { - offset = migration_bitmap_find_dirty(ram_state, block, offset); + unsigned long num = 0; + offset = colo_bitmap_find_dirty(ram_state, block, offset, &num); if (!offset_in_ramblock(block, ((ram_addr_t)offset) << TARGET_PAGE_BITS)) { offset = 0; + num = 0; block = QLIST_NEXT_RCU(block, next); } else { - migration_bitmap_clear_dirty(ram_state, block, offset); + unsigned long i = 0; + + for (i = 0; i < num; i++) { + migration_bitmap_clear_dirty(ram_state, block, offset + i); + } dst_host = block->host + (((ram_addr_t)offset) << TARGET_PAGE_BITS); src_host = block->colo_cache + (((ram_addr_t)offset) << TARGET_PAGE_BITS); - memcpy(dst_host, src_host, TARGET_PAGE_SIZE); + memcpy(dst_host, src_host, TARGET_PAGE_SIZE * num); + offset += num; } } } From 91fe9a8dbd449a2f333aefb82ec8adb1f6424408 Mon Sep 17 00:00:00 2001 From: "Rao, Lei" Date: Tue, 9 Nov 2021 11:04:54 +0800 Subject: [PATCH 1305/1334] Reset the auto-converge counter at every checkpoint. if we don't reset the auto-converge counter, it will continue to run with COLO running, and eventually the system will hang due to the CPU throttle reaching DEFAULT_MIGRATE_MAX_CPU_THROTTLE. Signed-off-by: Lei Rao Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Lukas Straub Tested-by: Lukas Straub Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/colo.c | 4 ++++ migration/ram.c | 9 +++++++++ migration/ram.h | 1 + 3 files changed, 14 insertions(+) diff --git a/migration/colo.c b/migration/colo.c index e3b1f136f4..2415325262 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -459,6 +459,10 @@ static int colo_do_checkpoint_transaction(MigrationState *s, if (ret < 0) { goto out; } + + if (migrate_auto_converge()) { + mig_throttle_counter_reset(); + } /* * Only save VM's live state, which not including device state. * TODO: We may need a timeout mechanism to prevent COLO process diff --git a/migration/ram.c b/migration/ram.c index f48cf4b0a5..863035d235 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -641,6 +641,15 @@ static void mig_throttle_guest_down(uint64_t bytes_dirty_period, } } +void mig_throttle_counter_reset(void) +{ + RAMState *rs = ram_state; + + rs->time_last_bitmap_sync = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + rs->num_dirty_pages_period = 0; + rs->bytes_xfer_prev = ram_counters.transferred; +} + /** * xbzrle_cache_zero_page: insert a zero page in the XBZRLE cache * diff --git a/migration/ram.h b/migration/ram.h index dda1988f3d..c515396a9a 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -50,6 +50,7 @@ bool ramblock_is_ignored(RAMBlock *block); int xbzrle_cache_resize(uint64_t new_size, Error **errp); uint64_t ram_bytes_remaining(void); uint64_t ram_bytes_total(void); +void mig_throttle_counter_reset(void); uint64_t ram_pagesize_summary(void); int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); From de4cf848b19e32fa59ee4dc908de49d5b282152b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 30 Oct 2021 11:49:37 +0200 Subject: [PATCH 1306/1334] hmp: Add shortcut to stop command to match cont MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some commands such as quit or cont have one letter alternatives but stop is missing that. Add stop|s to match cont|c for consistency and convenience. Signed-off-by: BALATON Zoltan Reviewed-by: Daniel P. Berrangé Message-Id: <20211030095225.513D4748F48@zero.eik.bme.hu> Signed-off-by: Laurent Vivier --- hmp-commands.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 3a5aeba3fe..70a9136ac2 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -382,7 +382,7 @@ SRST ERST { - .name = "stop", + .name = "stop|s", .args_type = "", .params = "", .help = "stop emulation", @@ -390,7 +390,7 @@ ERST }, SRST -``stop`` +``stop`` or ``s`` Stop emulation. ERST From 65b4c8c759a5c1a9cf207a3deb05dfdf09277161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 3 Nov 2021 11:53:11 +0100 Subject: [PATCH 1307/1334] hw/m68k: Fix typo in SPDX tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix 'Identifer' -> 'Identifier' typo. Cc: Laurent Vivier Fixes: 8c6df16ff60 ("hw/char: add goldfish-tty") Fixes: 87855593903 ("hw/intc: add goldfish-pic") Fixes: 2fde99ee312 ("m68k: add an interrupt controller") Fixes: 0791bc02b8f ("m68k: add a system controller") Fixes: e1cecdca559 ("m68k: add Virtual M68k Machine") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Laurent Vivier Message-Id: <20211103105311.3399293-1-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- hw/char/goldfish_tty.c | 2 +- hw/intc/goldfish_pic.c | 2 +- hw/intc/m68k_irqc.c | 2 +- hw/m68k/virt.c | 2 +- hw/misc/virt_ctrl.c | 2 +- include/hw/char/goldfish_tty.h | 2 +- include/hw/intc/goldfish_pic.h | 2 +- include/hw/intc/m68k_irqc.h | 2 +- include/hw/misc/virt_ctrl.h | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index 8365a18761..20b77885c1 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * Goldfish TTY * diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c index e3b43a69f1..dfd53275f6 100644 --- a/hw/intc/goldfish_pic.c +++ b/hw/intc/goldfish_pic.c @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * Goldfish PIC * diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 2133d2a698..0c515e4ecb 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * QEMU Motorola 680x0 IRQ Controller * diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 4e8bce5aa6..edc58fbdda 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * QEMU Vitual M68K Machine * diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c index 3552d7a09a..e75d1e7e17 100644 --- a/hw/misc/virt_ctrl.c +++ b/hw/misc/virt_ctrl.c @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * Virt system Controller */ diff --git a/include/hw/char/goldfish_tty.h b/include/hw/char/goldfish_tty.h index b9dd67362a..7503d2fa1e 100644 --- a/include/hw/char/goldfish_tty.h +++ b/include/hw/char/goldfish_tty.h @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * Goldfish TTY * diff --git a/include/hw/intc/goldfish_pic.h b/include/hw/intc/goldfish_pic.h index ad13ab37fc..e9d552f796 100644 --- a/include/hw/intc/goldfish_pic.h +++ b/include/hw/intc/goldfish_pic.h @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * Goldfish PIC * diff --git a/include/hw/intc/m68k_irqc.h b/include/hw/intc/m68k_irqc.h index dbcfcfc2e0..ef91f21812 100644 --- a/include/hw/intc/m68k_irqc.h +++ b/include/hw/intc/m68k_irqc.h @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * QEMU Motorola 680x0 IRQ Controller * diff --git a/include/hw/misc/virt_ctrl.h b/include/hw/misc/virt_ctrl.h index edfadc4695..25a237e518 100644 --- a/include/hw/misc/virt_ctrl.h +++ b/include/hw/misc/virt_ctrl.h @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifer: GPL-2.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later * * Virt system Controller */ From 63a0eb69873f94bdae8a145f70b69ebd2572ffad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Oct 2021 06:32:54 +0200 Subject: [PATCH 1308/1334] .mailmap: Fix more contributor entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These authors have some incorrect author email field. Acked-by: Pan Nengyuan Reviewed-by: Alex Chen Reviewed-by: Hyman Huang Reviewed-by: Haibin Zhang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211027043254.1248097-1-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- .mailmap | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.mailmap b/.mailmap index f029d1c21f..8beb2f95ae 100644 --- a/.mailmap +++ b/.mailmap @@ -69,6 +69,7 @@ Yongbok Kim # git author config, or had utf8/latin1 encoding issues. Aaron Lindsay Alexey Gerasimenko +Alex Chen Alex Ivanov Andreas Färber Bandan Das @@ -99,9 +100,11 @@ Gautham R. Shenoy Gautham R. Shenoy Gonglei (Arei) Guang Wang +Haibin Zhang Hailiang Zhang Hanna Reitz Hervé Poussineau +Hyman Huang Jakub Jermář Jakub Jermář Jean-Christophe Dubois @@ -135,6 +138,7 @@ Nicholas Thomas Nikunj A Dadhania Orit Wasserman Paolo Bonzini +Pan Nengyuan Pavel Dovgaluk Pavel Dovgaluk Pavel Dovgaluk From 1c282da3958ef52bd401ece181280cd7a203253d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 May 2021 12:34:23 +0200 Subject: [PATCH 1309/1334] meson: Fix 'interpretor' typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a typo from commit fa2f7b0b9b7 ("meson: Warn when TCI is selected but TCG backend is available"). Reported-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210521103423.2780345-1-philmd@redhat.com> Signed-off-by: Laurent Vivier --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6bfed294d0..9702fdce6d 100644 --- a/meson.build +++ b/meson.build @@ -340,7 +340,7 @@ if not get_option('tcg').disabled() error('Unsupported CPU @0@, try --enable-tcg-interpreter'.format(cpu)) endif elif get_option('tcg_interpreter') - warning('Use of the TCG interpretor is not recommended on this host') + warning('Use of the TCG interpreter is not recommended on this host') warning('architecture. There is a native TCG execution backend available') warning('which provides substantially better performance and reliability.') warning('It is strongly recommended to remove the --enable-tcg-interpreter') From 4a778dac9ee57fac871ebcd51d447d2273ae5783 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 28 Oct 2021 19:30:14 +0200 Subject: [PATCH 1310/1334] tests/qtest/virtio-net: fix hotplug test case virtio-net-test has an hotplug testcase that is never executed. This is because the testcase is attached to virtio-pci interface rather than to virtio-net-pci. $ QTEST_QEMU_BINARY=./qemu-system-x86_64 tests/qtest/qos-test -l | grep hotplug /x86_64/.../pci-ohci-tests/ohci_pci-test-hotplug /x86_64/.../e1000e/e1000e-tests/hotplug /x86_64/.../virtio-blk-pci/virtio-blk-pci-tests/hotplug /x86_64/.../vhost-user-blk-pci/vhost-user-blk-pci-tests/hotplug /x86_64/.../virtio-rng-pci/virtio-rng-pci-tests/hotplug /x86_64/.../virtio-scsi/virtio-scsi-tests/hotplug /x86_64/.../virtio-serial/virtio-serial-tests/hotplug With this fix: $ QTEST_QEMU_BINARY=./qemu-system-x86_64 tests/qtest/qos-test -l | grep hotplug ... /x86_64/.../vhost-user-blk-pci/vhost-user-blk-pci-tests/hotplug /x86_64/.../virtio-net-pci/virtio-net-pci-tests/hotplug /x86_64/.../virtio-rng-pci/virtio-rng-pci-tests/hotplug ... $ QTEST_QEMU_BINARY=./qemu-system-x86_64 tests/qtest/qos-test -p /x86_64/.../virtio-net-pci-tests/hotplug /x86_64/pc/i440FX-pcihost/pci-bus-pc/pci-bus/virtio-net-pci/virtio-net-pci-tests/hotplug: OK Fixes: 6ae333f91b99 ("qos-test: virtio-net test node") Signed-off-by: Laurent Vivier Acked-by: Thomas Huth Message-Id: <20211028173014.139692-1-lvivier@redhat.com> Signed-off-by: Laurent Vivier --- tests/qtest/virtio-net-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c index a08e2ffe12..8bf74e516c 100644 --- a/tests/qtest/virtio-net-test.c +++ b/tests/qtest/virtio-net-test.c @@ -319,7 +319,7 @@ static void register_virtio_net_test(void) .before = virtio_net_test_setup, }; - qos_add_test("hotplug", "virtio-pci", hotplug, &opts); + qos_add_test("hotplug", "virtio-net-pci", hotplug, &opts); #ifndef _WIN32 qos_add_test("basic", "virtio-net", send_recv_test, &opts); qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts); From 66d96a1901b7d9cfdbc454d407710ca8bfb90947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 5 Nov 2021 15:26:56 +0100 Subject: [PATCH 1311/1334] docs/about/deprecated: Remove empty 'related binaries' section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 497a30dbb06 ("qemu-img: Require -F with -b backing image") removed the content of the "Related binaries" section but forgot to remove the section title. Since it is now empty, remove it too. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Willian Rampazzo Reviewed-by: Yanan Wang Reviewed-by: Joaquin de Andres Message-Id: <20211105142656.145791-1-philmd@redhat.com> Signed-off-by: Laurent Vivier --- docs/about/deprecated.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 56f9ad15ab..5e514fb443 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -370,9 +370,6 @@ The ``I7200`` guest CPU relies on the nanoMIPS ISA, which is deprecated (the ISA has never been upstreamed to a compiler toolchain). Therefore this CPU is also deprecated. -Related binaries ----------------- - Backwards compatibility ----------------------- From 6837f299762679429924242a63f16490862578e3 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 6 Nov 2021 20:41:57 +0100 Subject: [PATCH 1312/1334] hw: m68k: virt: Add compat machine for 6.1 Add the missing machine type for m68k/virt Cc: qemu-stable@nongnu.org Signed-off-by: Laurent Vivier Message-Id: <20211106194158.4068596-2-laurent@vivier.eu> Signed-off-by: Laurent Vivier --- hw/m68k/virt.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 4e8bce5aa6..0d9e3f83c1 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -304,7 +304,14 @@ type_init(virt_machine_register_types) } \ type_init(machvirt_machine_##major##_##minor##_init); -static void virt_machine_6_0_options(MachineClass *mc) +static void virt_machine_6_1_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE(6, 0, true) +DEFINE_VIRT_MACHINE(6, 1, true) + +static void virt_machine_6_0_options(MachineClass *mc) +{ + virt_machine_6_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len); +} +DEFINE_VIRT_MACHINE(6, 0, false) From 6ed25621f2890d8f1c3b9e6f7a0e91c841aea1f8 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 6 Nov 2021 20:41:58 +0100 Subject: [PATCH 1313/1334] hw: m68k: virt: Add compat machine for 6.2 Add the missing machine type for m68k/virt Signed-off-by: Laurent Vivier Message-Id: <20211106194158.4068596-3-laurent@vivier.eu> Signed-off-by: Laurent Vivier --- hw/m68k/virt.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 0d9e3f83c1..978c26e025 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -304,10 +304,17 @@ type_init(virt_machine_register_types) } \ type_init(machvirt_machine_##major##_##minor##_init); -static void virt_machine_6_1_options(MachineClass *mc) +static void virt_machine_6_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE(6, 1, true) +DEFINE_VIRT_MACHINE(6, 2, true) + +static void virt_machine_6_1_options(MachineClass *mc) +{ + virt_machine_6_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); +} +DEFINE_VIRT_MACHINE(6, 1, false) static void virt_machine_6_0_options(MachineClass *mc) { From 5db83c7e90ec6c0b28995b537bb03942edcd4164 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 5 Nov 2021 17:52:54 +0100 Subject: [PATCH 1314/1334] macfb: fix a memory leak (CID 1465231) Rewrite the function using g_string_append_printf() rather than g_strdup_printf()/g_strconcat(). Fixes: df8abbbadf74 ("macfb: add common monitor modes supported by the MacOS toolbox ROM") Cc: mark.cave-ayland@ilande.co.uk Reported-by: Peter Maydell Suggested-by: Peter Maydell Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Reviewed-by: Mark Cave-Ayland Message-Id: <20211105165254.3544369-1-laurent@vivier.eu> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 4b352eb89c..277d3e6633 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -440,21 +440,18 @@ static MacFbMode *macfb_find_mode(MacfbDisplayType display_type, static gchar *macfb_mode_list(void) { - gchar *list = NULL; - gchar *mode; + GString *list = g_string_new(""); MacFbMode *macfb_mode; int i; for (i = 0; i < ARRAY_SIZE(macfb_mode_table); i++) { macfb_mode = &macfb_mode_table[i]; - mode = g_strdup_printf(" %dx%dx%d\n", macfb_mode->width, + g_string_append_printf(list, " %dx%dx%d\n", macfb_mode->width, macfb_mode->height, macfb_mode->depth); - list = g_strconcat(mode, list, NULL); - g_free(mode); } - return list; + return g_string_free(list, FALSE); } @@ -643,7 +640,7 @@ static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) gchar *list; error_setg(errp, "unknown display mode: width %d, height %d, depth %d", s->width, s->height, s->depth); - list = macfb_mode_list(); + list = macfb_mode_list(); error_append_hint(errp, "Available modes:\n%s", list); g_free(list); From 9a599217a44d98ac2ccdd22f8c75a07c03ad45c9 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 4 Nov 2021 09:58:09 +0100 Subject: [PATCH 1315/1334] docs/block-replication: use blockdev-backup We are going to deprecate drive-backup, so don't mention it here. Moreover, blockdev-backup seems more correct in the context. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: John Snow --- docs/block-replication.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/block-replication.txt b/docs/block-replication.txt index 108e9166a8..59eb2b33b3 100644 --- a/docs/block-replication.txt +++ b/docs/block-replication.txt @@ -79,7 +79,7 @@ Primary | || Secondary disk <--------- hidden-disk 5 <--------- || | | || | | || '-------------------------' - || drive-backup sync=none 6 + || blockdev-backup sync=none 6 1) The disk on the primary is represented by a block device with two children, providing replication between a primary disk and the host that @@ -101,7 +101,7 @@ should support bdrv_make_empty() and backing file. that is modified by the primary VM. It should also start as an empty disk, and the driver supports bdrv_make_empty() and backing file. -6) The drive-backup job (sync=none) is run to allow hidden-disk to buffer +6) The blockdev-backup job (sync=none) is run to allow hidden-disk to buffer any state that would otherwise be lost by the speculative write-through of the NBD server into the secondary disk. So before block replication, the primary disk and secondary disk should contain the same data. From 24d6cc1fa17745228694a3b2c94e8a5cbd4fc4d9 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 4 Nov 2021 09:58:10 +0100 Subject: [PATCH 1316/1334] docs/interop/bitmaps: use blockdev-backup We are going to deprecate drive-backup, so use modern interface here. In examples where target image creation is shown, show blockdev-add as well. If target creation omitted, omit blockdev-add as well. Reviewed-by: Kashyap Chamarthy Signed-off-by: Vladimir Sementsov-Ogievskiy --- docs/interop/bitmaps.rst | 285 +++++++++++++++++++++++++++++---------- 1 file changed, 215 insertions(+), 70 deletions(-) diff --git a/docs/interop/bitmaps.rst b/docs/interop/bitmaps.rst index 059ad67929..1de46febdc 100644 --- a/docs/interop/bitmaps.rst +++ b/docs/interop/bitmaps.rst @@ -539,12 +539,11 @@ other partial disk images on top of a base image to reconstruct a full backup from the point in time at which the incremental backup was issued. The "Push Model" here references the fact that QEMU is "pushing" the modified -blocks out to a destination. We will be using the `drive-backup -`_ and `blockdev-backup -`_ QMP commands to create both +blocks out to a destination. We will be using the `blockdev-backup +`_ QMP command to create both full and incremental backups. -Both of these commands are jobs, which have their own QMP API for querying and +The command is a background job, which has its own QMP API for querying and management documented in `Background jobs `_. @@ -557,6 +556,10 @@ create a new incremental backup chain attached to a drive. This example creates a new, full backup of "drive0" and accompanies it with a new, empty bitmap that records writes from this point in time forward. +The target can be created with the help of `blockdev-add +`_ or `blockdev-create +`_ command. + .. note:: Any new writes that happen after this command is issued, even while the backup job runs, will be written locally and not to the backup destination. These writes will be recorded in the bitmap @@ -576,12 +579,11 @@ new, empty bitmap that records writes from this point in time forward. } }, { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive0", - "target": "/path/to/drive0.full.qcow2", - "sync": "full", - "format": "qcow2" + "target": "target0", + "sync": "full" } } ] @@ -664,12 +666,11 @@ use a transaction to reset the bitmap while making a new full backup: } }, { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive0", - "target": "/path/to/drive0.new_full.qcow2", - "sync": "full", - "format": "qcow2" + "target": "target0", + "sync": "full" } } ] @@ -728,19 +729,35 @@ Example: First Incremental Backup $ qemu-img create -f qcow2 drive0.inc0.qcow2 \ -b drive0.full.qcow2 -F qcow2 +#. Add target block node: + + .. code-block:: QMP + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target0", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive0.inc0.qcow2" + } + } + } + + <- { "return": {} } + #. Issue an incremental backup command: .. code-block:: QMP -> { - "execute": "drive-backup", + "execute": "blockdev-backup", "arguments": { "device": "drive0", "bitmap": "bitmap0", - "target": "drive0.inc0.qcow2", - "format": "qcow2", - "sync": "incremental", - "mode": "existing" + "target": "target0", + "sync": "incremental" } } @@ -785,20 +802,36 @@ Example: Second Incremental Backup $ qemu-img create -f qcow2 drive0.inc1.qcow2 \ -b drive0.inc0.qcow2 -F qcow2 +#. Add target block node: + + .. code-block:: QMP + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target0", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive0.inc1.qcow2" + } + } + } + + <- { "return": {} } + #. Issue a new incremental backup command. The only difference here is that we have changed the target image below. .. code-block:: QMP -> { - "execute": "drive-backup", + "execute": "blockdev-backup", "arguments": { "device": "drive0", "bitmap": "bitmap0", - "target": "drive0.inc1.qcow2", - "format": "qcow2", - "sync": "incremental", - "mode": "existing" + "target": "target0", + "sync": "incremental" } } @@ -866,20 +899,36 @@ image: file for you, but you lose control over format options like compatibility and preallocation presets. +#. Add target block node: + + .. code-block:: QMP + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target0", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive0.inc2.qcow2" + } + } + } + + <- { "return": {} } + #. Issue a new incremental backup command. Apart from the new destination image, there is no difference from the last two examples. .. code-block:: QMP -> { - "execute": "drive-backup", + "execute": "blockdev-backup", "arguments": { "device": "drive0", "bitmap": "bitmap0", - "target": "drive0.inc2.qcow2", - "format": "qcow2", - "sync": "incremental", - "mode": "existing" + "target": "target0", + "sync": "incremental" } } @@ -930,6 +979,38 @@ point in time. $ qemu-img create -f qcow2 drive0.full.qcow2 64G $ qemu-img create -f qcow2 drive1.full.qcow2 64G +#. Add target block nodes: + + .. code-block:: QMP + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target0", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive0.full.qcow2" + } + } + } + + <- { "return": {} } + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target1", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive1.full.qcow2" + } + } + } + + <- { "return": {} } + #. Create a full (anchor) backup for each drive, with accompanying bitmaps: .. code-block:: QMP @@ -953,21 +1034,19 @@ point in time. } }, { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive0", - "target": "/path/to/drive0.full.qcow2", - "sync": "full", - "format": "qcow2" + "target": "target0", + "sync": "full" } }, { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive1", - "target": "/path/to/drive1.full.qcow2", - "sync": "full", - "format": "qcow2" + "target": "target1", + "sync": "full" } } ] @@ -1016,6 +1095,38 @@ point in time. $ qemu-img create -f qcow2 drive1.inc0.qcow2 \ -b drive1.full.qcow2 -F qcow2 +#. Add target block nodes: + + .. code-block:: QMP + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target0", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive0.inc0.qcow2" + } + } + } + + <- { "return": {} } + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target1", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive1.inc0.qcow2" + } + } + } + + <- { "return": {} } + #. Issue a multi-drive incremental push backup transaction: .. code-block:: QMP @@ -1025,25 +1136,21 @@ point in time. "arguments": { "actions": [ { - "type": "drive-backup", + "type": "blockev-backup", "data": { "device": "drive0", "bitmap": "bitmap0", - "format": "qcow2", - "mode": "existing", "sync": "incremental", - "target": "drive0.inc0.qcow2" + "target": "target0" } }, { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive1", "bitmap": "bitmap0", - "format": "qcow2", - "mode": "existing", "sync": "incremental", - "target": "drive1.inc0.qcow2" + "target": "target1" } }, ] @@ -1119,19 +1226,35 @@ described above. This example demonstrates the single-job failure case: $ qemu-img create -f qcow2 drive0.inc0.qcow2 \ -b drive0.full.qcow2 -F qcow2 +#. Add target block node: + + .. code-block:: QMP + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target0", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive0.inc0.qcow2" + } + } + } + + <- { "return": {} } + #. Attempt to create an incremental backup via QMP: .. code-block:: QMP -> { - "execute": "drive-backup", + "execute": "blockdev-backup", "arguments": { "device": "drive0", "bitmap": "bitmap0", - "target": "drive0.inc0.qcow2", - "format": "qcow2", - "sync": "incremental", - "mode": "existing" + "target": "target0", + "sync": "incremental" } } @@ -1164,6 +1287,19 @@ described above. This example demonstrates the single-job failure case: "event": "BLOCK_JOB_COMPLETED" } +#. Remove target node: + + .. code-block:: QMP + + -> { + "execute": "blockdev-del", + "arguments": { + "node-name": "target0", + } + } + + <- { "return": {} } + #. Delete the failed image, and re-create it. .. code:: bash @@ -1172,20 +1308,36 @@ described above. This example demonstrates the single-job failure case: $ qemu-img create -f qcow2 drive0.inc0.qcow2 \ -b drive0.full.qcow2 -F qcow2 +#. Add target block node: + + .. code-block:: QMP + + -> { + "execute": "blockdev-add", + "arguments": { + "node-name": "target0", + "driver": "qcow2", + "file": { + "driver": "file", + "filename": "drive0.inc0.qcow2" + } + } + } + + <- { "return": {} } + #. Retry the command after fixing the underlying problem, such as freeing up space on the backup volume: .. code-block:: QMP -> { - "execute": "drive-backup", + "execute": "blockdev-backup", "arguments": { "device": "drive0", "bitmap": "bitmap0", - "target": "drive0.inc0.qcow2", - "format": "qcow2", - "sync": "incremental", - "mode": "existing" + "target": "target0", + "sync": "incremental" } } @@ -1210,7 +1362,8 @@ described above. This example demonstrates the single-job failure case: Example: Partial Transactional Failures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -QMP commands like `drive-backup `_ +QMP commands like `blockdev-backup +`_ conceptually only start a job, and so transactions containing these commands may succeed even if the job it created later fails. This might have surprising interactions with notions of how a "transaction" ought to behave. @@ -1240,25 +1393,21 @@ and one succeeds: "arguments": { "actions": [ { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive0", "bitmap": "bitmap0", - "format": "qcow2", - "mode": "existing", "sync": "incremental", - "target": "drive0.inc0.qcow2" + "target": "target0" } }, { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive1", "bitmap": "bitmap0", - "format": "qcow2", - "mode": "existing", "sync": "incremental", - "target": "drive1.inc0.qcow2" + "target": "target1" } }] } @@ -1375,25 +1524,21 @@ applied: }, "actions": [ { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive0", "bitmap": "bitmap0", - "format": "qcow2", - "mode": "existing", "sync": "incremental", - "target": "drive0.inc0.qcow2" + "target": "target0" } }, { - "type": "drive-backup", + "type": "blockdev-backup", "data": { "device": "drive1", "bitmap": "bitmap0", - "format": "qcow2", - "mode": "existing", "sync": "incremental", - "target": "drive1.inc0.qcow2" + "target": "target1" } }] } From 1084159b31015e003946d199cbfecaec282e0eb2 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 4 Nov 2021 09:58:11 +0100 Subject: [PATCH 1317/1334] qapi: deprecate drive-backup Modern way is using blockdev-add + blockdev-backup, which provides a lot more control on how target is opened. As example of drive-backup problems consider the following: User of drive-backup expects that target will be opened in the same cache and aio mode as source. Corresponding logic is in drive_backup_prepare(), where we take bs->open_flags of source. It works rather bad if source was added by blockdev-add. Assume source is qcow2 image. On blockdev-add we should specify aio and cache options for file child of qcow2 node. What happens next: drive_backup_prepare() looks at bs->open_flags of qcow2 source node. But there no BDRV_O_NOCAHE neither BDRV_O_NATIVE_AIO: BDRV_O_NOCAHE is places in bs->file->bs->open_flags, and BDRV_O_NATIVE_AIO is nowhere, as file-posix parse options and simply set s->use_linux_aio. The documentation is updated in a minimal way, so that drive-backup is noted only as a deprecated command, and blockdev-backup used in most of places. Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake --- docs/about/deprecated.rst | 11 ++++++ docs/interop/live-block-operations.rst | 47 +++++++++++++++++--------- qapi/block-core.json | 5 ++- qapi/transaction.json | 6 +++- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 5e514fb443..600031210d 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -239,6 +239,17 @@ single ``bitmap``, the new ``block-export-add`` uses a list of ``bitmaps``. Member ``values`` in return value elements with meta-type ``enum`` is deprecated. Use ``members`` instead. +``drive-backup`` (since 6.2) +'''''''''''''''''''''''''''' + +Use ``blockdev-backup`` in combination with ``blockdev-add`` instead. +This change primarily separates the creation/opening process of the backup +target with explicit, separate steps. ``blockdev-backup`` uses mostly the +same arguments as ``drive-backup``, except the ``format`` and ``mode`` +options are removed in favor of using explicit ``blockdev-create`` and +``blockdev-add`` calls. See :doc:`/interop/live-block-operations` for +details. + System accelerators ------------------- diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst index 814c29bbe1..39e62c9915 100644 --- a/docs/interop/live-block-operations.rst +++ b/docs/interop/live-block-operations.rst @@ -116,8 +116,8 @@ QEMU block layer supports. (3) ``drive-mirror`` (and ``blockdev-mirror``): Synchronize a running disk to another image. -(4) ``drive-backup`` (and ``blockdev-backup``): Point-in-time (live) copy - of a block device to a destination. +(4) ``blockdev-backup`` (and the deprecated ``drive-backup``): + Point-in-time (live) copy of a block device to a destination. .. _`Interacting with a QEMU instance`: @@ -555,13 +555,14 @@ Currently, there are four different kinds: (3) ``none`` -- Synchronize only the new writes from this point on. - .. note:: In the case of ``drive-backup`` (or ``blockdev-backup``), - the behavior of ``none`` synchronization mode is different. - Normally, a ``backup`` job consists of two parts: Anything - that is overwritten by the guest is first copied out to - the backup, and in the background the whole image is - copied from start to end. With ``sync=none``, it's only - the first part. + .. note:: In the case of ``blockdev-backup`` (or deprecated + ``drive-backup``), the behavior of ``none`` + synchronization mode is different. Normally, a + ``backup`` job consists of two parts: Anything that is + overwritten by the guest is first copied out to the + backup, and in the background the whole image is copied + from start to end. With ``sync=none``, it's only the + first part. (4) ``incremental`` -- Synchronize content that is described by the dirty bitmap @@ -928,19 +929,22 @@ Shutdown the guest, by issuing the ``quit`` QMP command:: } -Live disk backup --- ``drive-backup`` and ``blockdev-backup`` -------------------------------------------------------------- +Live disk backup --- ``blockdev-backup`` and the deprecated``drive-backup`` +--------------------------------------------------------------------------- -The ``drive-backup`` (and its newer equivalent ``blockdev-backup``) allows +The ``blockdev-backup`` (and the deprecated ``drive-backup``) allows you to create a point-in-time snapshot. -In this case, the point-in-time is when you *start* the ``drive-backup`` -(or its newer equivalent ``blockdev-backup``) command. +In this case, the point-in-time is when you *start* the +``blockdev-backup`` (or deprecated ``drive-backup``) command. QMP invocation for ``drive-backup`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note that ``drive-backup`` command is deprecated since QEMU 6.2 and +will be removed in future. + Yet again, starting afresh with our example disk image chain:: [A] <-- [B] <-- [C] <-- [D] @@ -965,11 +969,22 @@ will be issued, indicating the live block device job operation has completed, and no further action is required. +Moving from the deprecated ``drive-backup`` to newer ``blockdev-backup`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``blockdev-backup`` differs from ``drive-backup`` in how you specify +the backup target. With ``blockdev-backup`` you can't specify filename +as a target. Instead you use ``node-name`` of existing block node, +which you may add by ``blockdev-add`` or ``blockdev-create`` commands. +Correspondingly, ``blockdev-backup`` doesn't have ``mode`` and +``format`` arguments which don't apply to an existing block node. See +following sections for details and examples. + + Notes on ``blockdev-backup`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``blockdev-backup`` command is equivalent in functionality to -``drive-backup``, except that it operates at node-level in a Block Driver +The ``blockdev-backup`` command operates at node-level in a Block Driver State (BDS) graph. E.g. the sequence of actions to create a point-in-time backup diff --git a/qapi/block-core.json b/qapi/block-core.json index 33e8507d10..1d3dd9cb48 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1709,6 +1709,9 @@ # The operation can be stopped before it has completed using the # block-job-cancel command. # +# Features: +# @deprecated: This command is deprecated. Use @blockdev-backup instead. +# # Returns: - nothing on success # - If @device is not a valid block device, GenericError # @@ -1724,7 +1727,7 @@ # ## { 'command': 'drive-backup', 'boxed': true, - 'data': 'DriveBackup' } + 'data': 'DriveBackup', 'features': ['deprecated'] } ## # @blockdev-backup: diff --git a/qapi/transaction.json b/qapi/transaction.json index d175b5f863..381a2df782 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -54,6 +54,10 @@ # @blockdev-snapshot-sync: since 1.1 # @drive-backup: Since 1.6 # +# Features: +# @deprecated: Member @drive-backup is deprecated. Use member +# @blockdev-backup instead. +# # Since: 1.1 ## { 'enum': 'TransactionActionKind', @@ -62,7 +66,7 @@ 'block-dirty-bitmap-disable', 'block-dirty-bitmap-merge', 'blockdev-backup', 'blockdev-snapshot', 'blockdev-snapshot-internal-sync', 'blockdev-snapshot-sync', - 'drive-backup' ] } + { 'name': 'drive-backup', 'features': [ 'deprecated' ] } ] } ## # @AbortWrapper: From 0a70bcf18caf7a61d480f8448723c15209d128ef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 9 Nov 2021 18:22:57 +0100 Subject: [PATCH 1318/1334] Update version for v6.2.0-rc0 release Signed-off-by: Richard Henderson --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 0ad6cf7fe6..8de48381d2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.50 +6.1.90 From a0b9c5f75c05c12d30328a305377383652e62e63 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Nov 2021 18:50:14 +0100 Subject: [PATCH 1319/1334] target/i386: sgx: mark device not user creatable The device is created by the machine based on the sgx-epc property. It should not be created by users. Reported-by: Thomas Huth Signed-off-by: Paolo Bonzini --- hw/i386/sgx-epc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index 55e2217eae..e508827e78 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -154,6 +154,7 @@ static void sgx_epc_class_init(ObjectClass *oc, void *data) dc->realize = sgx_epc_realize; dc->unrealize = sgx_epc_unrealize; dc->desc = "SGX EPC section"; + dc->user_creatable = false; device_class_set_props(dc, sgx_epc_properties); mdc->get_addr = sgx_epc_md_get_addr; From 13b86cbd2cbd12397143106bf8a93486397c4b9e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 26 Oct 2021 13:10:22 +0200 Subject: [PATCH 1320/1334] docs/devel/qapi-code-gen: Drop a duplicate paragraph Commit 55ec69f8b1 "docs/devel/qapi-code-gen.txt: Update to new rST backend conventions" accidentally duplicated a paragraph. Drop it. Cc: Peter Maydell Signed-off-by: Markus Armbruster Message-Id: <20211026111023.76937-2-armbru@redhat.com> Reviewed-by: John Snow --- docs/devel/qapi-code-gen.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 38f2d7aad3..b5761f60cd 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1000,12 +1000,6 @@ multiline argument descriptions. A 'Since: x.y.z' tagged section lists the release that introduced the definition. -The text of a section can start on a new line, in -which case it must not be indented at all. It can also start -on the same line as the 'Note:', 'Returns:', etc tag. In this -case if it spans multiple lines then second and subsequent -lines must be indented to match the first. - An 'Example' or 'Examples' section is automatically rendered entirely as literal fixed-width text. In other sections, the text is formatted, and rST markup can be used. From 53e9e547d20e91414e899034cf43720a8bf197be Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 26 Oct 2021 13:10:23 +0200 Subject: [PATCH 1321/1334] docs/devel/qapi-code-gen: Belatedly document feature documentation Commit 6a8c0b5102 "qapi: Add feature flags to struct types" neglected to document how to document feature flags. Make up for that. Cc: Kevin Wolf Signed-off-by: Markus Armbruster Message-Id: <20211026111023.76937-3-armbru@redhat.com> [Editing accident fixed] --- docs/devel/qapi-code-gen.rst | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index b5761f60cd..a3b5473089 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -956,15 +956,16 @@ definition must have documentation. Definition documentation starts with a line naming the definition, followed by an optional overview, a description of each argument (for commands and events), member (for structs and unions), branch (for -alternates), or value (for enums), and finally optional tagged -sections. +alternates), or value (for enums), a description of each feature (if +any), and finally optional tagged sections. -Descriptions of arguments can span multiple lines. The description -text can start on the line following the '\@argname:', in which case it -must not be indented at all. It can also start on the same line as -the '\@argname:'. In this case if it spans multiple lines then second -and subsequent lines must be indented to line up with the first -character of the first line of the description:: +The description of an argument or feature 'name' starts with +'\@name:'. The description text can start on the line following the +'\@name:', in which case it must not be indented at all. It can also +start on the same line as the '\@name:'. In this case if it spans +multiple lines then second and subsequent lines must be indented to +line up with the first character of the first line of the +description:: # @argone: # This is a two line description @@ -986,6 +987,12 @@ The number of spaces between the ':' and the text is not significant. Extensions added after the definition was first released carry a '(since x.y.z)' comment. +The feature descriptions must be preceded by a line "Features:", like +this:: + + # Features: + # @feature: Description text + A tagged section starts with one of the following words: "Note:"/"Notes:", "Since:", "Example"/"Examples", "Returns:", "TODO:". The section ends with the start of a new section. From 8c0bae5a19478db93371570b57164c63392a2d50 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 9 Nov 2021 15:55:59 +0100 Subject: [PATCH 1322/1334] qapi: Belatedly mark unstable QMP parts with feature 'unstable' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The work in merge commit e86e00a2493 lacks special feature flag 'unstable', because it raced with it. Add it where it's missing. Signed-off-by: Markus Armbruster Message-Id: <20211109145559.2122827-1-armbru@redhat.com> Reviewed-by: Damien Hedde Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé --- qapi/machine.json | 54 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/qapi/machine.json b/qapi/machine.json index 17794ef681..067e3f5378 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1417,107 +1417,143 @@ # # Query interrupt statistics # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: interrupt statistics # # Since: 6.2 ## { 'command': 'x-query-irq', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } ## # @x-query-jit: # # Query TCG compiler statistics # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: TCG compiler statistics # # Since: 6.2 ## { 'command': 'x-query-jit', 'returns': 'HumanReadableText', - 'if': 'CONFIG_TCG' } + 'if': 'CONFIG_TCG', + 'features': [ 'unstable' ] } ## # @x-query-numa: # # Query NUMA topology information # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: topology information # # Since: 6.2 ## { 'command': 'x-query-numa', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } ## # @x-query-opcount: # # Query TCG opcode counters # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: TCG opcode counters # # Since: 6.2 ## { 'command': 'x-query-opcount', 'returns': 'HumanReadableText', - 'if': 'CONFIG_TCG' } + 'if': 'CONFIG_TCG', + 'features': [ 'unstable' ] } ## # @x-query-profile: # # Query TCG profiling information # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: profile information # # Since: 6.2 ## { 'command': 'x-query-profile', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } ## # @x-query-ramblock: # # Query system ramblock information # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: system ramblock information # # Since: 6.2 ## { 'command': 'x-query-ramblock', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } ## # @x-query-rdma: # # Query RDMA state # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: RDMA state # # Since: 6.2 ## { 'command': 'x-query-rdma', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } ## # @x-query-roms: # # Query information on the registered ROMS # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: registered ROMs # # Since: 6.2 ## { 'command': 'x-query-roms', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } ## # @x-query-usb: # # Query information on the USB devices # +# Features: +# @unstable: This command is meant for debugging. +# # Returns: USB device information # # Since: 6.2 ## { 'command': 'x-query-usb', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } From 1bf4d3294bd48b702530b131e3344860495425fd Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 19 Oct 2021 10:57:11 +0200 Subject: [PATCH 1323/1334] monitor: Fix find_device_state() for IDs containing slashes Recent commit 6952026120 "monitor: Tidy up find_device_state()" assumed the function's argument is "the device's ID or QOM path" (as documented for device_del). It's actually either an absolute QOM path, or a QOM path relative to /machine/peripheral/. Such a relative path is a device ID when it doesn't contain a slash. When it does, the function now always fails. Broke iotest 200, which uses relative path "vda/virtio-backend". It fails because object_resolve_path_component() resolves just one component, not a relative path. The obvious function to resolve relative paths is object_resolve_path(). It picks a parent automatically. Too much magic, we want to specify the parent. Create new object_resolve_path_at() for that, and use it in find_device_state(). Reported-by: Christian Borntraeger Signed-off-by: Markus Armbruster Message-Id: <20211019085711.86377-1-armbru@redhat.com> Tested-by: Christian Borntraeger Acked-by: Paolo Bonzini --- include/qom/object.h | 12 ++++++++++++ qom/object.c | 11 +++++++++++ softmmu/qdev-monitor.c | 8 +------- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index faae0d841f..fae096f51c 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1543,6 +1543,18 @@ Object *object_resolve_path(const char *path, bool *ambiguous); Object *object_resolve_path_type(const char *path, const char *typename, bool *ambiguous); +/** + * object_resolve_path_at: + * @parent: the object in which to resolve the path + * @path: the path to resolve + * + * This is like object_resolve_path(), except paths not starting with + * a slash are relative to @parent. + * + * Returns: The resolved object or NULL on path lookup failure. + */ +Object *object_resolve_path_at(Object *parent, const char *path); + /** * object_resolve_path_component: * @parent: the object in which to resolve the path diff --git a/qom/object.c b/qom/object.c index 6be710bc40..4f0677cca9 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2144,6 +2144,17 @@ Object *object_resolve_path(const char *path, bool *ambiguous) return object_resolve_path_type(path, TYPE_OBJECT, ambiguous); } +Object *object_resolve_path_at(Object *parent, const char *path) +{ + g_auto(GStrv) parts = g_strsplit(path, "/", 0); + + if (*path == '/') { + return object_resolve_abs_path(object_get_root(), parts + 1, + TYPE_OBJECT); + } + return object_resolve_abs_path(parent, parts, TYPE_OBJECT); +} + typedef struct StringProperty { char *(*get)(Object *, Error **); diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index f8b3a4cd82..b5aaae4b8c 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -871,15 +871,9 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp) static DeviceState *find_device_state(const char *id, Error **errp) { - Object *obj; + Object *obj = object_resolve_path_at(qdev_get_peripheral(), id); DeviceState *dev; - if (id[0] == '/') { - obj = object_resolve_path(id, NULL); - } else { - obj = object_resolve_path_component(qdev_get_peripheral(), id); - } - if (!obj) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", id); From 3620328f787de5190c3c7b0b0041348dc11d796a Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Tue, 9 Nov 2021 16:29:11 -0300 Subject: [PATCH 1324/1334] target/ppc: Fix register update on lf[sd]u[x]/stf[sd]u[x] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These instructions should update the GPR indicated by the field RA instead of RT. This error caused a regression on Mac OS 9 boot and some graphical glitches in OS X. Fixes: a39a106634a9 ("target/ppc: Move load and store floating point instructions to decodetree") Reported-by: Mark Cave-Ayland Tested-by: Mark Cave-Ayland Signed-off-by: Matheus Ferst Signed-off-by: Cédric Le Goater --- target/ppc/translate/fp-impl.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index d1dbb1b96b..c9e05201d9 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -1328,7 +1328,7 @@ static bool do_lsfpsd(DisasContext *ctx, int rt, int ra, TCGv displ, set_fpr(rt, t0); } if (update) { - tcg_gen_mov_tl(cpu_gpr[rt], ea); + tcg_gen_mov_tl(cpu_gpr[ra], ea); } tcg_temp_free_i64(t0); tcg_temp_free(ea); From ef149763a8fcce70b85dfda27cc1222ecf765750 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 9 Nov 2021 19:35:22 +0100 Subject: [PATCH 1325/1334] rcu: Introduce force_rcu notifier The drain_rcu_call() function can be blocked as long as an RCU reader stays in a read-side critical section. This is typically what happens when a TCG vCPU is executing a busy loop. It can deadlock the QEMU monitor as reported in https://gitlab.com/qemu-project/qemu/-/issues/650 . This can be avoided by allowing drain_rcu_call() to enforce an RCU grace period. Since each reader might need to do specific actions to end a read-side critical section, do it with notifiers. Prepare ground for this by adding a notifier list to the RCU reader struct and use it in wait_for_readers() if drain_rcu_call() is in progress. An API is added for readers to register their notifiers. This is largely based on a draft from Paolo Bonzini. Suggested-by: Paolo Bonzini Signed-off-by: Greg Kurz Reviewed-by: Richard Henderson Message-Id: <20211109183523.47726-2-groug@kaod.org> Signed-off-by: Paolo Bonzini --- include/qemu/rcu.h | 15 +++++++++++++++ util/rcu.c | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h index 515d327cf1..e69efbd47f 100644 --- a/include/qemu/rcu.h +++ b/include/qemu/rcu.h @@ -27,6 +27,7 @@ #include "qemu/thread.h" #include "qemu/queue.h" #include "qemu/atomic.h" +#include "qemu/notify.h" #include "qemu/sys_membarrier.h" #ifdef __cplusplus @@ -66,6 +67,13 @@ struct rcu_reader_data { /* Data used for registry, protected by rcu_registry_lock */ QLIST_ENTRY(rcu_reader_data) node; + + /* + * NotifierList used to force an RCU grace period. Accessed under + * rcu_registry_lock. Note that the notifier is called _outside_ + * the thread! + */ + NotifierList force_rcu; }; extern __thread struct rcu_reader_data rcu_reader; @@ -180,6 +188,13 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(RCUReadAuto, rcu_read_auto_unlock) #define RCU_READ_LOCK_GUARD() \ g_autoptr(RCUReadAuto) _rcu_read_auto __attribute__((unused)) = rcu_read_auto_lock() +/* + * Force-RCU notifiers tell readers that they should exit their + * read-side critical section. + */ +void rcu_add_force_rcu_notifier(Notifier *n); +void rcu_remove_force_rcu_notifier(Notifier *n); + #ifdef __cplusplus } #endif diff --git a/util/rcu.c b/util/rcu.c index 13ac0f75cb..c91da9f137 100644 --- a/util/rcu.c +++ b/util/rcu.c @@ -46,6 +46,7 @@ unsigned long rcu_gp_ctr = RCU_GP_LOCKED; QemuEvent rcu_gp_event; +static int in_drain_call_rcu; static QemuMutex rcu_registry_lock; static QemuMutex rcu_sync_lock; @@ -107,6 +108,8 @@ static void wait_for_readers(void) * get some extra futex wakeups. */ qatomic_set(&index->waiting, false); + } else if (qatomic_read(&in_drain_call_rcu)) { + notifier_list_notify(&index->force_rcu, NULL); } } @@ -339,8 +342,10 @@ void drain_call_rcu(void) * assumed. */ + qatomic_inc(&in_drain_call_rcu); call_rcu1(&rcu_drain.rcu, drain_rcu_callback); qemu_event_wait(&rcu_drain.drain_complete_event); + qatomic_dec(&in_drain_call_rcu); if (locked) { qemu_mutex_lock_iothread(); @@ -363,6 +368,20 @@ void rcu_unregister_thread(void) qemu_mutex_unlock(&rcu_registry_lock); } +void rcu_add_force_rcu_notifier(Notifier *n) +{ + qemu_mutex_lock(&rcu_registry_lock); + notifier_list_add(&rcu_reader.force_rcu, n); + qemu_mutex_unlock(&rcu_registry_lock); +} + +void rcu_remove_force_rcu_notifier(Notifier *n) +{ + qemu_mutex_lock(&rcu_registry_lock); + notifier_remove(n); + qemu_mutex_unlock(&rcu_registry_lock); +} + static void rcu_init_complete(void) { QemuThread thread; From dd47a8f654d84f666b235ce8891e17ee76f9be8b Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 9 Nov 2021 19:35:23 +0100 Subject: [PATCH 1326/1334] accel/tcg: Register a force_rcu notifier A TCG vCPU doing a busy loop systematicaly hangs the QEMU monitor if the user passes 'device_add' without argument. This is because drain_cpu_all() which is called from qmp_device_add() cannot return if readers don't exit read-side critical sections. That is typically what busy-looping TCG vCPUs do: int cpu_exec(CPUState *cpu) { [...] rcu_read_lock(); [...] while (!cpu_handle_exception(cpu, &ret)) { // Busy loop keeps vCPU here } [...] rcu_read_unlock(); return ret; } For MTTCG, have all vCPU threads register a force_rcu notifier that will kick them out of the loop using async_run_on_cpu(). The notifier is called with the rcu_registry_lock mutex held, using async_run_on_cpu() ensures there are no deadlocks. For RR, a single thread runs all vCPUs. Just register a single notifier that kicks the current vCPU to the next one. For MTTCG: Suggested-by: Paolo Bonzini For RR: Suggested-by: Richard Henderson Fixes: 7bed89958bfb ("device_core: use drain_call_rcu in in qmp_device_add") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/650 Signed-off-by: Greg Kurz Reviewed-by: Richard Henderson Message-Id: <20211109183523.47726-3-groug@kaod.org> Signed-off-by: Paolo Bonzini --- accel/tcg/tcg-accel-ops-mttcg.c | 26 ++++++++++++++++++++++++++ accel/tcg/tcg-accel-ops-rr.c | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 847d2079d2..29632bd4c0 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -28,6 +28,7 @@ #include "sysemu/tcg.h" #include "sysemu/replay.h" #include "qemu/main-loop.h" +#include "qemu/notify.h" #include "qemu/guest-random.h" #include "exec/exec-all.h" #include "hw/boards.h" @@ -35,6 +36,26 @@ #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" +typedef struct MttcgForceRcuNotifier { + Notifier notifier; + CPUState *cpu; +} MttcgForceRcuNotifier; + +static void do_nothing(CPUState *cpu, run_on_cpu_data d) +{ +} + +static void mttcg_force_rcu(Notifier *notify, void *data) +{ + CPUState *cpu = container_of(notify, MttcgForceRcuNotifier, notifier)->cpu; + + /* + * Called with rcu_registry_lock held, using async_run_on_cpu() ensures + * that there are no deadlocks. + */ + async_run_on_cpu(cpu, do_nothing, RUN_ON_CPU_NULL); +} + /* * In the multi-threaded case each vCPU has its own thread. The TLS * variable current_cpu can be used deep in the code to find the @@ -43,12 +64,16 @@ static void *mttcg_cpu_thread_fn(void *arg) { + MttcgForceRcuNotifier force_rcu; CPUState *cpu = arg; assert(tcg_enabled()); g_assert(!icount_enabled()); rcu_register_thread(); + force_rcu.notifier.notify = mttcg_force_rcu; + force_rcu.cpu = cpu; + rcu_add_force_rcu_notifier(&force_rcu.notifier); tcg_register_thread(); qemu_mutex_lock_iothread(); @@ -100,6 +125,7 @@ static void *mttcg_cpu_thread_fn(void *arg) tcg_cpus_destroy(cpu); qemu_mutex_unlock_iothread(); + rcu_remove_force_rcu_notifier(&force_rcu.notifier); rcu_unregister_thread(); return NULL; } diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index a5fd26190e..bf59f53dbc 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -28,6 +28,7 @@ #include "sysemu/tcg.h" #include "sysemu/replay.h" #include "qemu/main-loop.h" +#include "qemu/notify.h" #include "qemu/guest-random.h" #include "exec/exec-all.h" @@ -133,6 +134,11 @@ static void rr_deal_with_unplugged_cpus(void) } } +static void rr_force_rcu(Notifier *notify, void *data) +{ + rr_kick_next_cpu(); +} + /* * In the single-threaded case each vCPU is simulated in turn. If * there is more than a single vCPU we create a simple timer to kick @@ -143,10 +149,13 @@ static void rr_deal_with_unplugged_cpus(void) static void *rr_cpu_thread_fn(void *arg) { + Notifier force_rcu; CPUState *cpu = arg; assert(tcg_enabled()); rcu_register_thread(); + force_rcu.notify = rr_force_rcu; + rcu_add_force_rcu_notifier(&force_rcu); tcg_register_thread(); qemu_mutex_lock_iothread(); @@ -255,6 +264,7 @@ static void *rr_cpu_thread_fn(void *arg) rr_deal_with_unplugged_cpus(); } + rcu_remove_force_rcu_notifier(&force_rcu); rcu_unregister_thread(); return NULL; } From bd989ed44f847cba20b46a743770c152e188f365 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Nov 2021 13:29:03 +0100 Subject: [PATCH 1327/1334] numa: avoid crash with SGX and "info numa" Add the MEMORY_DEVICE_INFO_KIND_SGX_EPC case, so that enclave memory is included in the output of "info numa" instead of crashing the monitor. Fixes: a7c565a941 ("sgx-epc: Add the fill_device_info() callback support", 2021-09-30) Signed-off-by: Paolo Bonzini --- hw/core/numa.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/core/numa.c b/hw/core/numa.c index 510d096a88..e6050b2273 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -756,6 +756,7 @@ static void numa_stat_memory_devices(NumaNodeMem node_mem[]) PCDIMMDeviceInfo *pcdimm_info; VirtioPMEMDeviceInfo *vpi; VirtioMEMDeviceInfo *vmi; + SgxEPCDeviceInfo *se; for (info = info_list; info; info = info->next) { MemoryDeviceInfo *value = info->value; @@ -781,6 +782,12 @@ static void numa_stat_memory_devices(NumaNodeMem node_mem[]) node_mem[vmi->node].node_mem += vmi->size; node_mem[vmi->node].node_plugged_mem += vmi->size; break; + case MEMORY_DEVICE_INFO_KIND_SGX_EPC: + se = value->u.sgx_epc.data; + /* TODO: once we support numa, assign to right node */ + node_mem[0].node_mem += se->size; + node_mem[0].node_plugged_mem += se->size; + break; default: g_assert_not_reached(); } From 1fde73bcd72d94a6215ec7cd599aa5c7e6591d29 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 10 Nov 2021 09:39:21 -0300 Subject: [PATCH 1328/1334] spapr_numa.c: fix FORM1 distance-less nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 71e6fae3a99 fixed an issue with FORM2 affinity guests with NUMA nodes in which the distance info is absent in machine_state->numa_state->nodes. This happens when QEMU adds a default NUMA node and when the user adds NUMA nodes without specifying the distances. During the discussions of the forementioned patch [1] it was found that FORM1 guests were behaving in a strange way in the same scenario, with the kernel seeing the distances between the nodes as '160', as we can see in this example with 4 NUMA nodes without distance information: $ numactl -H available: 4 nodes (0-3) (...) node distances: node 0 1 2 3 0: 10 160 160 160 1: 160 10 160 160 2: 160 160 10 160 3: 160 160 160 10 Turns out that we have the same problem with FORM1 guests - we are calculating associativity domain using zeroed values. And as it also turns out, the solution from 71e6fae3a99 applies to FORM1 as well. This patch creates a wrapper called 'get_numa_distance' that contains the logic used in FORM2 to define node distances when this information is absent. This helper is then used in all places where we need to read distance information from machine_state->numa_state->nodes. That way we'll guarantee that the NUMA node distance is always being curated before being used. After this patch, the FORM1 guest mentioned above will have the following topology: $ numactl -H available: 4 nodes (0-3) (...) node distances: node 0 1 2 3 0: 10 20 20 20 1: 20 10 20 20 2: 20 20 10 20 3: 20 20 20 10 This is compatible with what FORM2 guests and other archs do in this case. [1] https://lists.gnu.org/archive/html/qemu-devel/2021-11/msg01960.html Fixes: 690fbe4295d5 ("spapr_numa: consider user input when defining associativity") CC: Aneesh Kumar K.V CC: Nicholas Piggin Reviewed-by: Richard Henderson Signed-off-by: Daniel Henrique Barboza Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_numa.c | 62 ++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index 56ab2a5fb6..e9ef7e7646 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -66,16 +66,41 @@ static const uint32_t *get_associativity(SpaprMachineState *spapr, int node_id) return spapr->FORM1_assoc_array[node_id]; } +/* + * Wrapper that returns node distance from ms->numa_state->nodes + * after handling edge cases where the distance might be absent. + */ +static int get_numa_distance(MachineState *ms, int src, int dst) +{ + NodeInfo *numa_info = ms->numa_state->nodes; + int ret = numa_info[src].distance[dst]; + + if (ret != 0) { + return ret; + } + + /* + * In case QEMU adds a default NUMA single node when the user + * did not add any, or where the user did not supply distances, + * the distance will be absent (zero). Return local/remote + * distance in this case. + */ + if (src == dst) { + return NUMA_DISTANCE_MIN; + } + + return NUMA_DISTANCE_DEFAULT; +} + static bool spapr_numa_is_symmetrical(MachineState *ms) { - int src, dst; int nb_numa_nodes = ms->numa_state->num_nodes; - NodeInfo *numa_info = ms->numa_state->nodes; + int src, dst; for (src = 0; src < nb_numa_nodes; src++) { for (dst = src; dst < nb_numa_nodes; dst++) { - if (numa_info[src].distance[dst] != - numa_info[dst].distance[src]) { + if (get_numa_distance(ms, src, dst) != + get_numa_distance(ms, dst, src)) { return false; } } @@ -133,7 +158,6 @@ static uint8_t spapr_numa_get_numa_level(uint8_t distance) static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) { MachineState *ms = MACHINE(spapr); - NodeInfo *numa_info = ms->numa_state->nodes; int nb_numa_nodes = ms->numa_state->num_nodes; int src, dst, i, j; @@ -170,7 +194,7 @@ static void spapr_numa_define_FORM1_domains(SpaprMachineState *spapr) * The PPC kernel expects the associativity domains of node 0 to * be always 0, and this algorithm will grant that by default. */ - uint8_t distance = numa_info[src].distance[dst]; + uint8_t distance = get_numa_distance(ms, src, dst); uint8_t n_level = spapr_numa_get_numa_level(distance); uint32_t assoc_src; @@ -498,7 +522,6 @@ static void spapr_numa_FORM2_write_rtas_tables(SpaprMachineState *spapr, void *fdt, int rtas) { MachineState *ms = MACHINE(spapr); - NodeInfo *numa_info = ms->numa_state->nodes; int nb_numa_nodes = ms->numa_state->num_nodes; int distance_table_entries = nb_numa_nodes * nb_numa_nodes; g_autofree uint32_t *lookup_index_table = NULL; @@ -540,30 +563,7 @@ static void spapr_numa_FORM2_write_rtas_tables(SpaprMachineState *spapr, for (src = 0; src < nb_numa_nodes; src++) { for (dst = 0; dst < nb_numa_nodes; dst++) { - /* - * We need to be explicit with the local distance - * value to cover the case where the user didn't added any - * NUMA nodes, but QEMU adds the default NUMA node without - * adding the numa_info to retrieve distance info from. - */ - distance_table[i] = numa_info[src].distance[dst]; - if (distance_table[i] == 0) { - /* - * In case QEMU adds a default NUMA single node when the user - * did not add any, or where the user did not supply distances, - * the value will be 0 here. Populate the table with a fallback - * simple local / remote distance. - */ - if (src == dst) { - distance_table[i] = NUMA_DISTANCE_MIN; - } else { - distance_table[i] = numa_info[src].distance[dst]; - if (distance_table[i] < NUMA_DISTANCE_MIN) { - distance_table[i] = NUMA_DISTANCE_DEFAULT; - } - } - } - i++; + distance_table[i++] = get_numa_distance(ms, src, dst); } } From 2c3132279b9a962c27adaea53b4c8e8480385706 Mon Sep 17 00:00:00 2001 From: Yang Zhong Date: Mon, 1 Nov 2021 12:20:09 -0400 Subject: [PATCH 1329/1334] sgx: Reset the vEPC regions during VM reboot For bare-metal SGX on real hardware, the hardware provides guarantees SGX state at reboot. For instance, all pages start out uninitialized. The vepc driver provides a similar guarantee today for freshly-opened vepc instances, but guests such as Windows expect all pages to be in uninitialized state on startup, including after every guest reboot. Qemu can invoke the ioctl to bring its vEPC pages back to uninitialized state. There is a possibility that some pages fail to be removed if they are SECS pages, and the child and SECS pages could be in separate vEPC regions. Therefore, the ioctl returns the number of EREMOVE failures, telling Qemu to try the ioctl again after it's done with all vEPC regions. The related kernel patches: Link: https://lkml.kernel.org/r/20211021201155.1523989-3-pbonzini@redhat.com Signed-off-by: Yang Zhong Message-Id: <20211101162009.62161-6-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/sgx.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 11607568b6..8fef3dd8fa 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -21,6 +21,8 @@ #include "qapi/qapi-commands-misc-target.h" #include "exec/address-spaces.h" #include "sysemu/hw_accel.h" +#include "sysemu/reset.h" +#include #define SGX_MAX_EPC_SECTIONS 8 #define SGX_CPUID_EPC_INVALID 0x0 @@ -29,6 +31,11 @@ #define SGX_CPUID_EPC_SECTION 0x1 #define SGX_CPUID_EPC_MASK 0xF +#define SGX_MAGIC 0xA4 +#define SGX_IOC_VEPC_REMOVE_ALL _IO(SGX_MAGIC, 0x04) + +#define RETRY_NUM 2 + static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high) { return (low & MAKE_64BIT_MASK(12, 20)) + @@ -59,6 +66,46 @@ static uint64_t sgx_calc_host_epc_section_size(void) return size; } +static void sgx_epc_reset(void *opaque) +{ + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + HostMemoryBackend *hostmem; + SGXEPCDevice *epc; + int failures; + int fd, i, j, r; + static bool warned = false; + + /* + * The second pass is needed to remove SECS pages that could not + * be removed during the first. + */ + for (i = 0; i < RETRY_NUM; i++) { + failures = 0; + for (j = 0; j < pcms->sgx_epc.nr_sections; j++) { + epc = pcms->sgx_epc.sections[j]; + hostmem = MEMORY_BACKEND(epc->hostmem); + fd = memory_region_get_fd(host_memory_backend_get_memory(hostmem)); + + r = ioctl(fd, SGX_IOC_VEPC_REMOVE_ALL); + if (r == -ENOTTY && !warned) { + warned = true; + warn_report("kernel does not support SGX_IOC_VEPC_REMOVE_ALL"); + warn_report("SGX might operate incorrectly in the guest after reset"); + break; + } else if (r > 0) { + /* SECS pages remain */ + failures++; + if (i == 1) { + error_report("cannot reset vEPC section %d", j); + } + } + } + if (!failures) { + break; + } + } +} + SGXInfo *qmp_query_sgx_capabilities(Error **errp) { SGXInfo *info = NULL; @@ -190,4 +237,7 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) } memory_region_set_size(&sgx_epc->mr, sgx_epc->size); + + /* register the reset callback for sgx epc */ + qemu_register_reset(sgx_epc_reset, NULL); } From d139786e1b3d67991e6cb49a8a59bb2182350285 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 10 Nov 2021 17:25:16 -0300 Subject: [PATCH 1330/1334] ppc/mmu_helper.c: do not truncate 'ea' in booke206_invalidate_ea_tlb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'tlbivax' is implemented by gen_tlbivax_booke206() via gen_helper_booke206_tlbivax(). In case the TLB needs to be flushed, booke206_invalidate_ea_tlb() is called. All these functions, but booke206_invalidate_ea_tlb(), uses a 64-bit effective address 'ea'. booke206_invalidate_ea_tlb() uses an uint32_t 'ea' argument that truncates the original 'ea' value for apparently no particular reason. This function retrieves the tlb pointer by calling booke206_get_tlbm(), which also uses a target_ulong address as parameter - in this case, a truncated 'ea' address. All the surrounding logic considers the effective TLB address as a 64 bit value, aside from the signature of booke206_invalidate_ea_tlb(). Last but not the least, PowerISA 2.07B section 6.11.4.9 [2] makes it clear that the effective address "EA" is a 64 bit value. Commit 01662f3e5133 introduced this code and no changes were made ever since. An user detected a problem with tlbivax [1] stating that this address truncation was the cause. This same behavior might be the source of several subtle bugs that were never caught. For all these reasons, this patch assumes that this address truncation is the result of a mistake/oversight of the original commit, and changes booke206_invalidate_ea_tlb() 'ea' argument to 'vaddr'. [1] https://gitlab.com/qemu-project/qemu/-/issues/52 [2] https://wiki.raptorcs.com/wiki/File:PowerISA_V2.07B.pdf Fixes: 01662f3e5133 ("PPC: Implement e500 (FSL) MMU") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/52 Signed-off-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- target/ppc/mmu_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 2cb98c5169..e0c4950dda 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -1216,7 +1216,7 @@ void helper_booke206_tlbsx(CPUPPCState *env, target_ulong address) } static inline void booke206_invalidate_ea_tlb(CPUPPCState *env, int tlbn, - uint32_t ea) + vaddr ea) { int i; int ways = booke206_tlb_ways(env, tlbn); From 225bec0c0e42b5b0be3007fd01de6e082a8ee9bf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 9 Nov 2021 23:17:59 +0100 Subject: [PATCH 1331/1334] tcg/optimize: Add an extra cast to fold_extract2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no bug, but silence a warning about computation in int32_t being assigned to a uint64_t. Reported-by: Coverity CID 1465220 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index dbb2d46e88..2397f2cf93 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1365,7 +1365,7 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) v2 <<= 64 - shr; } else { v1 = (uint32_t)v1 >> shr; - v2 = (int32_t)v2 << (32 - shr); + v2 = (uint64_t)((int32_t)v2 << (32 - shr)); } return tcg_opt_gen_movi(ctx, op, op->args[0], v1 | v2); } From f1f727ac8a19c9106a4c92a98a3cde4b284fcef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 6 Nov 2021 12:14:57 +0100 Subject: [PATCH 1332/1334] tcg: Remove TCI experimental status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following commits (released in v6.0.0) made raised the quality of the TCI backend to the other TCG architectures, thus is is not considerated experimental anymore: - c6fbea47664..2f74f45e32b - dc09f047edd..9e9acb7b348 - b6139eb0578..2fc6f16ca5e - dbcbda2cd84..5e8892db93f Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211106111457.517546-1-f4bug@amsat.org> Signed-off-by: Richard Henderson --- docs/about/build-platforms.rst | 10 ++++++---- meson.build | 4 ++-- meson_options.txt | 2 +- scripts/meson-buildoptions.sh | 3 +-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index bcb1549721..c29a4b8fe6 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -54,10 +54,12 @@ Those hosts are officially supported, with various accelerators: * - x86 - hax, hvf (64 bit only), kvm, nvmm, tcg, whpx (64 bit only), xen -Other host architectures are not supported. It is possible to build QEMU on an -unsupported host architecture using the configure ``--enable-tcg-interpreter`` -option to enable the experimental TCI support, but note that this is very slow -and is not recommended. +Other host architectures are not supported. It is possible to build QEMU system +emulation on an unsupported host architecture using the configure +``--enable-tcg-interpreter`` option to enable the TCI support, but note that +this is very slow and is not recommended for normal use. QEMU user emulation +requires host-specific support for signal handling, therefore TCI won't help +on unsupported host architectures. Non-supported architectures may be removed in the future following the :ref:`deprecation process`. diff --git a/meson.build b/meson.build index 9702fdce6d..2ece4fe088 100644 --- a/meson.build +++ b/meson.build @@ -335,7 +335,7 @@ tcg_arch = config_host['ARCH'] if not get_option('tcg').disabled() if cpu not in supported_cpus if get_option('tcg_interpreter') - warning('Unsupported CPU @0@, will use TCG with TCI (experimental and slow)'.format(cpu)) + warning('Unsupported CPU @0@, will use TCG with TCI (slow)'.format(cpu)) else error('Unsupported CPU @0@, try --enable-tcg-interpreter'.format(cpu)) endif @@ -3290,7 +3290,7 @@ endif summary_info += {'TCG support': config_all.has_key('CONFIG_TCG')} if config_all.has_key('CONFIG_TCG') if get_option('tcg_interpreter') - summary_info += {'TCG backend': 'TCI (TCG with bytecode interpreter, experimental and slow)'} + summary_info += {'TCG backend': 'TCI (TCG with bytecode interpreter, slow)'} else summary_info += {'TCG backend': 'native (@0@)'.format(cpu)} endif diff --git a/meson_options.txt b/meson_options.txt index e740dce2a5..411952bc91 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -59,7 +59,7 @@ option('xen_pci_passthrough', type: 'feature', value: 'auto', option('tcg', type: 'feature', value: 'auto', description: 'TCG support') option('tcg_interpreter', type: 'boolean', value: false, - description: 'TCG with bytecode interpreter (experimental and slow)') + description: 'TCG with bytecode interpreter (slow)') option('cfi', type: 'boolean', value: 'false', description: 'Control-Flow Integrity (CFI)') option('cfi_debug', type: 'boolean', value: 'false', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 55b8a78560..45e1f2e20d 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -13,8 +13,7 @@ meson_options_help() { printf "%s\n" ' jemalloc/system/tcmalloc)' printf "%s\n" ' --enable-slirp[=CHOICE] Whether and how to find the slirp library' printf "%s\n" ' (choices: auto/disabled/enabled/internal/system)' - printf "%s\n" ' --enable-tcg-interpreter TCG with bytecode interpreter (experimental and' - printf "%s\n" ' slow)' + printf "%s\n" ' --enable-tcg-interpreter TCG with bytecode interpreter (slow)' printf "%s\n" ' --enable-trace-backends=CHOICE' printf "%s\n" ' Set available tracing backends [log] (choices:' printf "%s\n" ' dtrace/ftrace/log/nop/simple/syslog/ust)' From 8d30f0473e0beaf135f81984b3af90c23a228dc2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 10 Nov 2021 13:17:31 +0100 Subject: [PATCH 1333/1334] tcg: Document ctpop opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: a768e4e99247 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/658 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/README | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tcg/README b/tcg/README index c2e7762a37..bc15cc3b32 100644 --- a/tcg/README +++ b/tcg/README @@ -254,6 +254,12 @@ t0 = t1 ? clz(t1) : t2 t0 = t1 ? ctz(t1) : t2 +* ctpop_i32/i64 t0, t1 + +t0 = number of bits set in t1 +With "ctpop" short for "count population", matching +the function name used in include/qemu/host-utils.h. + ********* Shifts/Rotates * shl_i32/i64 t0, t1, t2 From d58f01733b94845b0c2232018a2bedb6a2347ec5 Mon Sep 17 00:00:00 2001 From: Miroslav Rezanina Date: Wed, 27 Oct 2021 04:56:29 -0400 Subject: [PATCH 1334/1334] tcg/s390x: Fix tcg_out_vec_op argument type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newly defined tcg_out_vec_op (34ef767609 tcg/s390x: Add host vector framework) for s390x uses pointer argument definition. This fails on gcc 11 as original declaration uses array argument: In file included from ../tcg/tcg.c:430: /builddir/build/BUILD/qemu-6.1.50/tcg/s390x/tcg-target.c.inc:2702:42: error: argument 5 of type 'const TCGArg *' {aka 'const long unsigned int *'} declared as a pointer [-Werror=array-parameter=] 2702 | const TCGArg *args, const int *const_args) | ~~~~~~~~~~~~~~^~~~ ../tcg/tcg.c:121:41: note: previously declared as an array 'const TCGArg[16]' {aka 'const long unsigned int[16]'} 121 | const TCGArg args[TCG_MAX_OP_ARGS], | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~ In file included from ../tcg/tcg.c:430: /builddir/build/BUILD/qemu-6.1.50/tcg/s390x/tcg-target.c.inc:2702:59: error: argument 6 of type 'const int *' declared as a pointer [-Werror=array-parameter=] 2702 | const TCGArg *args, const int *const_args) | ~~~~~~~~~~~^~~~~~~~~~ ../tcg/tcg.c:122:38: note: previously declared as an array 'const int[16]' 122 | const int const_args[TCG_MAX_OP_ARGS]); | ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~ Fixing argument type to pass build. Signed-off-by: Miroslav Rezanina Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Acked-by: David Hildenbrand Message-Id: <20211027085629.240704-1-mrezanin@redhat.com> Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 8938c446c8..57e803e339 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2699,7 +2699,8 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned vece, - const TCGArg *args, const int *const_args) + const TCGArg args[TCG_MAX_OP_ARGS], + const int const_args[TCG_MAX_OP_ARGS]) { TCGType type = vecl + TCG_TYPE_V64; TCGArg a0 = args[0], a1 = args[1], a2 = args[2];

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�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 >7pKbaf>f~#n2G++6P{HI$2gtGI;&bZ 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%0DgZ=-`5tiQK8?8b4zMTIqFk^5(x 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%0D}~1N?9%XHF3&K+ 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 z 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 Date: Thu, 7 Oct 2021 11:19:49 +0200 Subject: [PATCH 0422/1334] MAINTAINERS: Add myself as reviewer of the 'Memory API' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having developed interest with the Memory API, volunteer to review the patches. Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Hildenbrand Message-Id: <20211007091949.319404-1-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 50435b8d2f..32b668e92f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2502,6 +2502,7 @@ Memory API M: Paolo Bonzini M: Peter Xu M: David Hildenbrand +R: Philippe Mathieu-Daudé S: Supported F: include/exec/ioport.h F: include/exec/memop.h From 8009806ec81ff2caa711c99e87ca394f9ba50565 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Oct 2021 04:33:15 -0400 Subject: [PATCH 0423/1334] tests: add missing dependency for check-block qemu-iotests run qemu-storage-daemon, make sure it is up to date. Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Makefile.include b/tests/Makefile.include index 7bb8961515..cc1e4f2c07 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -151,6 +151,7 @@ ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy) check: check-block export PYTHON check-block: $(SRC_PATH)/tests/check-block.sh qemu-img$(EXESUF) \ + storage-daemon/qemu-storage-daemon$(EXESUF) \ qemu-io$(EXESUF) qemu-nbd$(EXESUF) $(QEMU_IOTESTS_HELPERS-y) \ $(filter qemu-system-%, $(ninja-targets)) @$< From 097a5f19a357b6bc699a05ccd119bfa89ea409cc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Oct 2021 04:18:27 -0400 Subject: [PATCH 0424/1334] build: fix "make check" without earlier "make" "make check", if not preceded by "make", will not build the tools needed by qemu-iotests. This happens because qemu-iotests, aka "make check-block", is not yet part of meson.build. While at it, remove the reference to the now-dead QEMU_IOTESTS_HELPERS-y variable. Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index cc1e4f2c07..8434a33fe6 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -150,14 +150,21 @@ check: ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy) check: check-block export PYTHON -check-block: $(SRC_PATH)/tests/check-block.sh qemu-img$(EXESUF) \ - storage-daemon/qemu-storage-daemon$(EXESUF) \ - qemu-io$(EXESUF) qemu-nbd$(EXESUF) $(QEMU_IOTESTS_HELPERS-y) \ - $(filter qemu-system-%, $(ninja-targets)) + +ifneq ($(filter check check-block check-build, $(MAKECMDGOALS)),) +ninja-cmd-goals += \ + qemu-img$(EXESUF) \ + qemu-io$(EXESUF) \ + qemu-nbd$(EXESUF) \ + storage-daemon/qemu-storage-daemon$(EXESUF) \ + $(filter qemu-system-%, $(ninja-targets)) +endif + +check-block: $(SRC_PATH)/tests/check-block.sh run-ninja @$< endif -check-build: $(QEMU_IOTESTS_HELPERS-y) +check-build: run-ninja check-clean: rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR) From ecc00666a020368ec2c3712c40386dc7ea75a8a9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Oct 2021 11:24:22 +0200 Subject: [PATCH 0425/1334] qemu-iotests: flush after every test This makes it possible to see what is happening, even if the output of "make check-block" is not sent to a tty (for example if it is sent to grep or tee). Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- tests/qemu-iotests/testrunner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index a56b6da396..0e29c2fddd 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -341,6 +341,7 @@ class TestRunner(ContextManager['TestRunner']): elif res.status == 'not run': notrun.append(name) + sys.stdout.flush() if res.interrupted: break From 6bd17dccb63e152266cf7a7399931c9fe11bc8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20S=C5=82omi=C5=84ski?= Date: Sun, 5 Sep 2021 03:16:22 +0200 Subject: [PATCH 0426/1334] util/compatfd.c: use libc signalfd wrapper instead of raw syscall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows the use of native signalfd instead of the sigtimedwait based emulation on systems other than Linux. Signed-off-by: Kacper Słomiński Message-Id: <20210905011621.200785-1-kacper.slominski72@gmail.com> Signed-off-by: Paolo Bonzini --- meson.build | 7 +++---- util/compatfd.c | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index 99a0a3e689..2d373a61a6 100644 --- a/meson.build +++ b/meson.build @@ -1420,10 +1420,9 @@ config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' #include int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }''')) config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + ''' - #include - #include - #include - int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); }''')) + #include + #include + int main(void) { return signalfd(-1, NULL, SFD_CLOEXEC); }''')) config_host_data.set('CONFIG_SPLICE', cc.links(gnu_source_prefix + ''' #include #include diff --git a/util/compatfd.c b/util/compatfd.c index a8ec525c6c..ab810c42a9 100644 --- a/util/compatfd.c +++ b/util/compatfd.c @@ -17,7 +17,7 @@ #include "qemu/thread.h" #if defined(CONFIG_SIGNALFD) -#include +#include #endif struct sigfd_compat_info { @@ -96,9 +96,8 @@ int qemu_signalfd(const sigset_t *mask) #if defined(CONFIG_SIGNALFD) int ret; - ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); + ret = signalfd(-1, mask, SFD_CLOEXEC); if (ret != -1) { - qemu_set_cloexec(ret); return ret; } #endif From b50f6dc174cf391d725fca895d74c441b23e2e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:16:54 +0200 Subject: [PATCH 0427/1334] qapi/misc-target: Wrap long 'SEV Attestation Report' long lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap long lines before 70 characters for legibility. Suggested-by: Markus Armbruster Reviewed-by: Markus Armbruster Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-2-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- qapi/misc-target.json | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 594fbd1577..ae5577e039 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -300,8 +300,8 @@ ## # @SevAttestationReport: # -# The struct describes attestation report for a Secure Encrypted Virtualization -# feature. +# The struct describes attestation report for a Secure Encrypted +# Virtualization feature. # # @data: guest attestation report (base64 encoded) # @@ -315,10 +315,11 @@ ## # @query-sev-attestation-report: # -# This command is used to get the SEV attestation report, and is supported on AMD -# X86 platforms only. +# This command is used to get the SEV attestation report, and is +# supported on AMD X86 platforms only. # -# @mnonce: a random 16 bytes value encoded in base64 (it will be included in report) +# @mnonce: a random 16 bytes value encoded in base64 (it will be +# included in report) # # Returns: SevAttestationReport objects. # @@ -326,11 +327,13 @@ # # Example: # -# -> { "execute" : "query-sev-attestation-report", "arguments": { "mnonce": "aaaaaaa" } } +# -> { "execute" : "query-sev-attestation-report", +# "arguments": { "mnonce": "aaaaaaa" } } # <- { "return" : { "data": "aaaaaaaabbbddddd"} } # ## -{ 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' }, +{ 'command': 'query-sev-attestation-report', + 'data': { 'mnonce': 'str' }, 'returns': 'SevAttestationReport', 'if': 'TARGET_I386' } From 993e26058d10756f5e206742c20b0cd03a9cb973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:16:55 +0200 Subject: [PATCH 0428/1334] qapi/misc-target: Group SEV QAPI definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is already a section with various SEV commands / types, so move the SEV guest attestation together. Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-3-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- qapi/misc-target.json | 80 +++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/qapi/misc-target.json b/qapi/misc-target.json index ae5577e039..5aa2b95b7d 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -229,6 +229,46 @@ 'data': { 'packet-header': 'str', 'secret': 'str', '*gpa': 'uint64' }, 'if': 'TARGET_I386' } +## +# @SevAttestationReport: +# +# The struct describes attestation report for a Secure Encrypted +# Virtualization feature. +# +# @data: guest attestation report (base64 encoded) +# +# +# Since: 6.1 +## +{ 'struct': 'SevAttestationReport', + 'data': { 'data': 'str'}, + 'if': 'TARGET_I386' } + +## +# @query-sev-attestation-report: +# +# This command is used to get the SEV attestation report, and is +# supported on AMD X86 platforms only. +# +# @mnonce: a random 16 bytes value encoded in base64 (it will be +# included in report) +# +# Returns: SevAttestationReport objects. +# +# Since: 6.1 +# +# Example: +# +# -> { "execute" : "query-sev-attestation-report", +# "arguments": { "mnonce": "aaaaaaa" } } +# <- { "return" : { "data": "aaaaaaaabbbddddd"} } +# +## +{ 'command': 'query-sev-attestation-report', + 'data': { 'mnonce': 'str' }, + 'returns': 'SevAttestationReport', + 'if': 'TARGET_I386' } + ## # @dump-skeys: # @@ -297,46 +337,6 @@ 'if': 'TARGET_ARM' } -## -# @SevAttestationReport: -# -# The struct describes attestation report for a Secure Encrypted -# Virtualization feature. -# -# @data: guest attestation report (base64 encoded) -# -# -# Since: 6.1 -## -{ 'struct': 'SevAttestationReport', - 'data': { 'data': 'str'}, - 'if': 'TARGET_I386' } - -## -# @query-sev-attestation-report: -# -# This command is used to get the SEV attestation report, and is -# supported on AMD X86 platforms only. -# -# @mnonce: a random 16 bytes value encoded in base64 (it will be -# included in report) -# -# Returns: SevAttestationReport objects. -# -# Since: 6.1 -# -# Example: -# -# -> { "execute" : "query-sev-attestation-report", -# "arguments": { "mnonce": "aaaaaaa" } } -# <- { "return" : { "data": "aaaaaaaabbbddddd"} } -# -## -{ 'command': 'query-sev-attestation-report', - 'data': { 'mnonce': 'str' }, - 'returns': 'SevAttestationReport', - 'if': 'TARGET_I386' } - ## # @SGXInfo: # From 02310f3a91470575a103857222d3a6e8acea338e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:16:56 +0200 Subject: [PATCH 0429/1334] target/i386/kvm: Introduce i386_softmmu_kvm Meson source set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the i386_softmmu_kvm Meson source set to be able to add features dependent on CONFIG_KVM. Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-4-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/kvm/meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 0a533411ca..b1c76957c7 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -1,8 +1,12 @@ i386_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) -i386_softmmu_ss.add(when: 'CONFIG_KVM', if_true: files( +i386_softmmu_kvm_ss = ss.source_set() + +i386_softmmu_kvm_ss.add(files( 'kvm.c', 'kvm-cpu.c', )) i386_softmmu_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) + +i386_softmmu_ss.add_all(when: 'CONFIG_KVM', if_true: i386_softmmu_kvm_ss) From 773ab6cb16b34272dc7eb8171824924c38dbeb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:16:57 +0200 Subject: [PATCH 0430/1334] target/i386/kvm: Restrict SEV stubs to x86 architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SEV is x86-specific, no need to add its stub to other architectures. Move the stub file to target/i386/kvm/. Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-5-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- accel/kvm/meson.build | 1 - target/i386/kvm/meson.build | 2 ++ {accel => target/i386}/kvm/sev-stub.c | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename {accel => target/i386}/kvm/sev-stub.c (100%) diff --git a/accel/kvm/meson.build b/accel/kvm/meson.build index 8d219bea50..397a1fe1fd 100644 --- a/accel/kvm/meson.build +++ b/accel/kvm/meson.build @@ -3,6 +3,5 @@ kvm_ss.add(files( 'kvm-all.c', 'kvm-accel-ops.c', )) -kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) specific_ss.add_all(when: 'CONFIG_KVM', if_true: kvm_ss) diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index b1c76957c7..736df8b72e 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -7,6 +7,8 @@ i386_softmmu_kvm_ss.add(files( 'kvm-cpu.c', )) +i386_softmmu_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) + i386_softmmu_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) i386_softmmu_ss.add_all(when: 'CONFIG_KVM', if_true: i386_softmmu_kvm_ss) diff --git a/accel/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c similarity index 100% rename from accel/kvm/sev-stub.c rename to target/i386/kvm/sev-stub.c From 2c7233eb103adbf567778270b69950dd7a776c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:16:58 +0200 Subject: [PATCH 0431/1334] target/i386/sev: Prefix QMP errors with 'SEV' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple errors might be reported to the monitor, better to prefix the SEV ones so we can distinct them. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Dr. David Alan Gilbert Message-Id: <20211007161716.453984-6-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/monitor.c | 2 +- target/i386/sev.c | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 196c1c9e77..eabbeb9be9 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -717,7 +717,7 @@ SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) data = sev_get_launch_measurement(); if (!data) { - error_setg(errp, "Measurement is not available"); + error_setg(errp, "SEV launch measurement is not available"); return NULL; } diff --git a/target/i386/sev.c b/target/i386/sev.c index bcd9260fa4..4f1952cd32 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -440,7 +440,8 @@ sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); if (r < 0) { if (err != SEV_RET_INVALID_LEN) { - error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)", + error_setg(errp, "SEV: Failed to export PDH cert" + " ret=%d fw_err=%d (%s)", r, err, fw_error_to_str(err)); return 1; } @@ -453,7 +454,7 @@ sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); if (r < 0) { - error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)", + error_setg(errp, "SEV: Failed to export PDH cert ret=%d fw_err=%d (%s)", r, err, fw_error_to_str(err)); goto e_free; } @@ -491,7 +492,7 @@ sev_get_capabilities(Error **errp) fd = open(DEFAULT_SEV_DEVICE, O_RDWR); if (fd < 0) { - error_setg_errno(errp, errno, "Failed to open %s", + error_setg_errno(errp, errno, "SEV: Failed to open %s", DEFAULT_SEV_DEVICE); return NULL; } @@ -557,8 +558,9 @@ sev_get_attestation_report(const char *mnonce, Error **errp) &input, &err); if (ret < 0) { if (err != SEV_RET_INVALID_LEN) { - error_setg(errp, "failed to query the attestation report length " - "ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err)); + error_setg(errp, "SEV: Failed to query the attestation report" + " length ret=%d fw_err=%d (%s)", + ret, err, fw_error_to_str(err)); g_free(buf); return NULL; } @@ -572,7 +574,7 @@ sev_get_attestation_report(const char *mnonce, Error **errp) ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, &input, &err); if (ret) { - error_setg_errno(errp, errno, "Failed to get attestation report" + error_setg_errno(errp, errno, "SEV: Failed to get attestation report" " ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err)); goto e_free_data; } @@ -596,7 +598,7 @@ sev_read_file_base64(const char *filename, guchar **data, gsize *len) GError *error = NULL; if (!g_file_get_contents(filename, &base64, &sz, &error)) { - error_report("failed to read '%s' (%s)", filename, error->message); + error_report("SEV: Failed to read '%s' (%s)", filename, error->message); g_error_free(error); return -1; } @@ -911,7 +913,7 @@ sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) { int ret = sev_launch_update_data(sev_guest, ptr, len); if (ret < 0) { - error_setg(errp, "failed to encrypt pflash rom"); + error_setg(errp, "SEV: Failed to encrypt pflash rom"); return ret; } } @@ -930,7 +932,7 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, MemoryRegion *mr = NULL; if (!sev_guest) { - error_setg(errp, "SEV: SEV not enabled."); + error_setg(errp, "SEV not enabled for guest"); return 1; } From 9f885cac701325ebcbf7e4393aa9b21b32ec3c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:16:59 +0200 Subject: [PATCH 0432/1334] target/i386/monitor: Return QMP error when SEV is not enabled for guest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the management layer tries to inject a secret, it gets an empty response in case the guest doesn't have SEV enabled, or the binary is built without SEV: { "execute": "sev-inject-launch-secret", "arguments": { "packet-header": "mypkt", "secret": "mypass", "gpa": 4294959104 } } { "return": { } } Make it clearer by returning an error: { "execute": "sev-inject-launch-secret", "arguments": { "packet-header": "mypkt", "secret": "mypass", "gpa": 4294959104 } } { "error": { "class": "GenericError", "desc": "SEV not enabled for guest" } } Note: we will remove the sev_inject_launch_secret() stub in few commits, so we don't bother to add error_setg() there. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Connor Kuehl Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-7-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/monitor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index eabbeb9be9..ea836678f5 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -28,6 +28,7 @@ #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qerror.h" #include "sysemu/kvm.h" #include "sysemu/sev.h" #include "qapi/error.h" @@ -743,6 +744,10 @@ void qmp_sev_inject_launch_secret(const char *packet_hdr, bool has_gpa, uint64_t gpa, Error **errp) { + if (!sev_enabled()) { + error_setg(errp, "SEV not enabled for guest"); + return; + } if (!has_gpa) { uint8_t *data; struct sev_secret_area *area; From f83aeeaeba1cf3e25d6292d9cc12064bfe2e78ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:00 +0200 Subject: [PATCH 0433/1334] target/i386/cpu: Add missing 'qapi/error.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 00b81053244 ("target-i386: Remove assert_no_error usage") forgot to add the "qapi/error.h" for &error_abort, add it now. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Connor Kuehl Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-8-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a7b1b6aa93..b54b98551e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -27,6 +27,7 @@ #include "sysemu/hvf.h" #include "kvm/kvm_i386.h" #include "sev_i386.h" +#include "qapi/error.h" #include "qapi/qapi-visit-machine.h" #include "qapi/qmp/qerror.h" #include "qapi/qapi-commands-machine-target.h" From 40cbafe05bb8f463d9dc096985ceff5d2535dc56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:01 +0200 Subject: [PATCH 0434/1334] target/i386/sev_i386.h: Remove unused headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declarations don't require these headers, remove them. Reviewed-by: Connor Kuehl Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-9-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/sev-stub.c | 1 + target/i386/sev_i386.h | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c index d8e6583171..408441768d 100644 --- a/target/i386/sev-stub.c +++ b/target/i386/sev-stub.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "sev_i386.h" SevInfo *sev_get_info(void) diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h index 2afe108069..9bf6cd1878 100644 --- a/target/i386/sev_i386.h +++ b/target/i386/sev_i386.h @@ -14,11 +14,7 @@ #ifndef QEMU_SEV_I386_H #define QEMU_SEV_I386_H -#include "qom/object.h" -#include "qapi/error.h" -#include "sysemu/kvm.h" #include "sysemu/sev.h" -#include "qemu/error-report.h" #include "qapi/qapi-types-misc-target.h" #define SEV_POLICY_NODBG 0x1 From 4e7189e112966ac3a7bc884273a8b07291c3012e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:02 +0200 Subject: [PATCH 0435/1334] target/i386/sev: Remove sev_get_me_mask() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unused dead code makes review harder, so remove it. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Connor Kuehl Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-10-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/sev-stub.c | 5 ----- target/i386/sev.c | 9 --------- target/i386/sev_i386.h | 1 - 3 files changed, 15 deletions(-) diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c index 408441768d..20b1e18ec1 100644 --- a/target/i386/sev-stub.c +++ b/target/i386/sev-stub.c @@ -25,11 +25,6 @@ bool sev_enabled(void) return false; } -uint64_t sev_get_me_mask(void) -{ - return ~0; -} - uint32_t sev_get_cbit_position(void) { return 0; diff --git a/target/i386/sev.c b/target/i386/sev.c index 4f1952cd32..9e3f2ec8dd 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -65,7 +65,6 @@ struct SevGuestState { uint8_t api_major; uint8_t api_minor; uint8_t build_id; - uint64_t me_mask; int sev_fd; SevState state; gchar *measurement; @@ -389,12 +388,6 @@ sev_es_enabled(void) return sev_enabled() && (sev_guest->policy & SEV_POLICY_ES); } -uint64_t -sev_get_me_mask(void) -{ - return sev_guest ? sev_guest->me_mask : ~0; -} - uint32_t sev_get_cbit_position(void) { @@ -833,8 +826,6 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) goto err; } - sev->me_mask = ~(1UL << sev->cbitpos); - devname = object_property_get_str(OBJECT(sev), "sev-device", NULL); sev->sev_fd = open(devname, O_RDWR); if (sev->sev_fd < 0) { diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h index 9bf6cd1878..d83428fa26 100644 --- a/target/i386/sev_i386.h +++ b/target/i386/sev_i386.h @@ -36,7 +36,6 @@ typedef struct SevKernelLoaderContext { } SevKernelLoaderContext; extern bool sev_es_enabled(void); -extern uint64_t sev_get_me_mask(void); extern SevInfo *sev_get_info(void); extern uint32_t sev_get_cbit_position(void); extern uint32_t sev_get_reduced_phys_bits(void); From d58d9f55a78a69730b4f1641f1c969d8585b970b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:03 +0200 Subject: [PATCH 0436/1334] target/i386/sev: Mark unreachable code with g_assert_not_reached() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unique sev_encrypt_flash() invocation (in pc_system_flash_map) is protected by the "if (sev_enabled())" check, so is not reacheable. Replace the abort() call in sev_es_save_reset_vector() by g_assert_not_reached() which meaning is clearer. Reviewed-by: Connor Kuehl Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-11-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/sev-stub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c index 20b1e18ec1..55f1ec7419 100644 --- a/target/i386/sev-stub.c +++ b/target/i386/sev-stub.c @@ -54,7 +54,7 @@ int sev_inject_launch_secret(const char *hdr, const char *secret, int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) { - return 0; + g_assert_not_reached(); } bool sev_es_enabled(void) @@ -68,7 +68,7 @@ void sev_es_set_reset_vector(CPUState *cpu) int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) { - abort(); + g_assert_not_reached(); } SevAttestationReport * From ed84ae720d0b28c50906412a903e598f75ee1d7c Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 7 Oct 2021 18:17:04 +0200 Subject: [PATCH 0437/1334] target/i386/sev: sev_get_attestation_report use g_autofree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes a whole bunch of g_free's and a goto. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Connor Kuehl Reviewed-by: Brijesh Singh Message-Id: <20210603113017.34922-1-dgilbert@redhat.com> Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-12-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 9e3f2ec8dd..3a30ba6d94 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -521,8 +521,8 @@ sev_get_attestation_report(const char *mnonce, Error **errp) struct kvm_sev_attestation_report input = {}; SevAttestationReport *report = NULL; SevGuestState *sev = sev_guest; - guchar *data; - guchar *buf; + g_autofree guchar *data = NULL; + g_autofree guchar *buf = NULL; gsize len; int err = 0, ret; @@ -542,7 +542,6 @@ sev_get_attestation_report(const char *mnonce, Error **errp) if (len != sizeof(input.mnonce)) { error_setg(errp, "SEV: mnonce must be %zu bytes (got %" G_GSIZE_FORMAT ")", sizeof(input.mnonce), len); - g_free(buf); return NULL; } @@ -554,7 +553,6 @@ sev_get_attestation_report(const char *mnonce, Error **errp) error_setg(errp, "SEV: Failed to query the attestation report" " length ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err)); - g_free(buf); return NULL; } } @@ -569,7 +567,7 @@ sev_get_attestation_report(const char *mnonce, Error **errp) if (ret) { error_setg_errno(errp, errno, "SEV: Failed to get attestation report" " ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err)); - goto e_free_data; + return NULL; } report = g_new0(SevAttestationReport, 1); @@ -577,9 +575,6 @@ sev_get_attestation_report(const char *mnonce, Error **errp) trace_kvm_sev_attestation_report(mnonce, report->data); -e_free_data: - g_free(data); - g_free(buf); return report; } From 2f573c415e8c84ec79aa6deb1d04fa9fccdebca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:05 +0200 Subject: [PATCH 0438/1334] target/i386/sev: Use g_autofree in sev_launch_get_measure() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use g_autofree to remove a pair of g_free/goto. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-13-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 3a30ba6d94..5cbbcf0bb9 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -685,8 +685,8 @@ sev_launch_get_measure(Notifier *notifier, void *unused) { SevGuestState *sev = sev_guest; int ret, error; - guchar *data; - struct kvm_sev_launch_measure *measurement; + g_autofree guchar *data = NULL; + g_autofree struct kvm_sev_launch_measure *measurement = NULL; if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) { return; @@ -708,7 +708,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) if (!measurement->len) { error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", __func__, ret, error, fw_error_to_str(errno)); - goto free_measurement; + return; } data = g_new0(guchar, measurement->len); @@ -720,7 +720,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) if (ret) { error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", __func__, ret, error, fw_error_to_str(errno)); - goto free_data; + return; } sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET); @@ -728,11 +728,6 @@ sev_launch_get_measure(Notifier *notifier, void *unused) /* encode the measurement value and emit the event */ sev->measurement = g_base64_encode(data, measurement->len); trace_kvm_sev_launch_measurement(sev->measurement); - -free_data: - g_free(data); -free_measurement: - g_free(measurement); } char * From e24b3332042b7372f4817694aec48650dcfa68a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:06 +0200 Subject: [PATCH 0439/1334] target/i386/sev: Restrict SEV to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SEV is irrelevant on user emulation, so restrict it to sysemu. Some stubs are still required because used in cpu.c by x86_register_cpudef_types(), so move the sysemu specific stubs to sev-sysemu-stub.c instead. This will allow us to simplify monitor.c (which is not available in user emulation) in the next commit. Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-14-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/meson.build | 4 ++- target/i386/sev-stub.c | 43 ------------------------- target/i386/sev-sysemu-stub.c | 60 +++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 44 deletions(-) create mode 100644 target/i386/sev-sysemu-stub.c diff --git a/target/i386/meson.build b/target/i386/meson.build index dac19ec00d..a4f45c3ec1 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -6,7 +6,7 @@ i386_ss.add(files( 'xsave_helper.c', 'cpu-dump.c', )) -i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'sev.c'), if_false: files('sev-stub.c')) +i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c'), if_false: files('sev-stub.c')) # x86 cpu type i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) @@ -20,6 +20,8 @@ i386_softmmu_ss.add(files( 'monitor.c', 'cpu-sysemu.c', )) +i386_softmmu_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-sysemu-stub.c')) + i386_user_ss = ss.source_set() subdir('kvm') diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c index 55f1ec7419..170e9f50fe 100644 --- a/target/i386/sev-stub.c +++ b/target/i386/sev-stub.c @@ -15,11 +15,6 @@ #include "qapi/error.h" #include "sev_i386.h" -SevInfo *sev_get_info(void) -{ - return NULL; -} - bool sev_enabled(void) { return false; @@ -35,49 +30,11 @@ uint32_t sev_get_reduced_phys_bits(void) return 0; } -char *sev_get_launch_measurement(void) -{ - return NULL; -} - -SevCapability *sev_get_capabilities(Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - -int sev_inject_launch_secret(const char *hdr, const char *secret, - uint64_t gpa, Error **errp) -{ - return 1; -} - -int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) -{ - g_assert_not_reached(); -} - bool sev_es_enabled(void) { return false; } -void sev_es_set_reset_vector(CPUState *cpu) -{ -} - -int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) -{ - g_assert_not_reached(); -} - -SevAttestationReport * -sev_get_attestation_report(const char *mnonce, Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) { g_assert_not_reached(); diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c new file mode 100644 index 0000000000..d556b4f091 --- /dev/null +++ b/target/i386/sev-sysemu-stub.c @@ -0,0 +1,60 @@ +/* + * QEMU SEV system stub + * + * Copyright Advanced Micro Devices 2018 + * + * Authors: + * Brijesh Singh + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-commands-misc-target.h" +#include "qapi/error.h" +#include "sev_i386.h" + +SevInfo *sev_get_info(void) +{ + return NULL; +} + +char *sev_get_launch_measurement(void) +{ + return NULL; +} + +SevCapability *sev_get_capabilities(Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} + +int sev_inject_launch_secret(const char *hdr, const char *secret, + uint64_t gpa, Error **errp) +{ + return 1; +} + +int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) +{ + g_assert_not_reached(); +} + +void sev_es_set_reset_vector(CPUState *cpu) +{ +} + +int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) +{ + g_assert_not_reached(); +} + +SevAttestationReport *sev_get_attestation_report(const char *mnonce, + Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} From 93777de3650e7db4b0434d63dd461505b85519f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:07 +0200 Subject: [PATCH 0440/1334] target/i386/sev: Rename sev_i386.h -> sev.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SEV is a x86 specific feature, and the "sev_i386.h" header is already in target/i386/. Rename it as "sev.h" to simplify. Patch created mechanically using: $ git mv target/i386/sev_i386.h target/i386/sev.h $ sed -i s/sev_i386.h/sev.h/ $(git grep -l sev_i386.h) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Dr. David Alan Gilbert Message-Id: <20211007161716.453984-15-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86.c | 2 +- target/i386/cpu.c | 2 +- target/i386/kvm/kvm.c | 2 +- target/i386/monitor.c | 2 +- target/i386/sev-stub.c | 2 +- target/i386/sev-sysemu-stub.c | 2 +- target/i386/sev.c | 2 +- target/i386/{sev_i386.h => sev.h} | 0 8 files changed, 7 insertions(+), 7 deletions(-) rename target/i386/{sev_i386.h => sev.h} (100%) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 0c7c054e3a..76de7e2265 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -47,7 +47,7 @@ #include "hw/i386/fw_cfg.h" #include "hw/intc/i8259.h" #include "hw/rtc/mc146818rtc.h" -#include "target/i386/sev_i386.h" +#include "target/i386/sev.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/irq.h" diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b54b98551e..8289dc87bd 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -26,7 +26,7 @@ #include "sysemu/reset.h" #include "sysemu/hvf.h" #include "kvm/kvm_i386.h" -#include "sev_i386.h" +#include "sev.h" #include "qapi/error.h" #include "qapi/qapi-visit-machine.h" #include "qapi/qmp/qerror.h" diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index f25837f63f..a5f6ff63c8 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -28,7 +28,7 @@ #include "sysemu/kvm_int.h" #include "sysemu/runstate.h" #include "kvm_i386.h" -#include "sev_i386.h" +#include "sev.h" #include "hyperv.h" #include "hyperv-proto.h" diff --git a/target/i386/monitor.c b/target/i386/monitor.c index ea836678f5..109e4e61c0 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -32,7 +32,7 @@ #include "sysemu/kvm.h" #include "sysemu/sev.h" #include "qapi/error.h" -#include "sev_i386.h" +#include "sev.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" #include "hw/i386/pc.h" diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c index 170e9f50fe..7e8b6f9a25 100644 --- a/target/i386/sev-stub.c +++ b/target/i386/sev-stub.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sev_i386.h" +#include "sev.h" bool sev_enabled(void) { diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index d556b4f091..8082781feb 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/error.h" -#include "sev_i386.h" +#include "sev.h" SevInfo *sev_get_info(void) { diff --git a/target/i386/sev.c b/target/i386/sev.c index 5cbbcf0bb9..e43bbf3a17 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -25,7 +25,7 @@ #include "qemu/uuid.h" #include "crypto/hash.h" #include "sysemu/kvm.h" -#include "sev_i386.h" +#include "sev.h" #include "sysemu/sysemu.h" #include "sysemu/runstate.h" #include "trace.h" diff --git a/target/i386/sev_i386.h b/target/i386/sev.h similarity index 100% rename from target/i386/sev_i386.h rename to target/i386/sev.h From deae846f944a330e69109af8c807d6f2e66c95e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:08 +0200 Subject: [PATCH 0441/1334] target/i386/sev: Declare system-specific functions in 'sev.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "sysemu/sev.h" is only used from x86-specific files. Let's move it to include/hw/i386, and merge it with target/i386/sev.h. Suggested-by: Paolo Bonzini Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-16-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc_sysfw.c | 2 +- include/sysemu/sev.h | 28 ---------------------------- target/i386/kvm/kvm.c | 1 - target/i386/kvm/sev-stub.c | 2 +- target/i386/monitor.c | 1 - target/i386/sev.h | 12 +++++++++++- 6 files changed, 13 insertions(+), 33 deletions(-) delete mode 100644 include/sysemu/sev.h diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 68d6b1f783..c8b17af953 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -37,7 +37,7 @@ #include "hw/qdev-properties.h" #include "hw/block/flash.h" #include "sysemu/kvm.h" -#include "sysemu/sev.h" +#include "sev.h" #define FLASH_SECTOR_SIZE 4096 diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h deleted file mode 100644 index 94d821d737..0000000000 --- a/include/sysemu/sev.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * QEMU Secure Encrypted Virutualization (SEV) support - * - * Copyright: Advanced Micro Devices, 2016-2018 - * - * Authors: - * Brijesh Singh - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef QEMU_SEV_H -#define QEMU_SEV_H - -#include "sysemu/kvm.h" - -bool sev_enabled(void); -int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); -int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); -int sev_inject_launch_secret(const char *hdr, const char *secret, - uint64_t gpa, Error **errp); - -int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size); -void sev_es_set_reset_vector(CPUState *cpu); - -#endif diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index a5f6ff63c8..0eb7a0340c 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -44,7 +44,6 @@ #include "hw/i386/intel_iommu.h" #include "hw/i386/x86-iommu.h" #include "hw/i386/e820_memory_layout.h" -#include "sysemu/sev.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c index 9587d1b2a3..6080c007a2 100644 --- a/target/i386/kvm/sev-stub.c +++ b/target/i386/kvm/sev-stub.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" -#include "sysemu/sev.h" +#include "sev.h" int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 109e4e61c0..935a8ee8ca 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -30,7 +30,6 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "sysemu/kvm.h" -#include "sysemu/sev.h" #include "qapi/error.h" #include "sev.h" #include "qapi/qapi-commands-misc-target.h" diff --git a/target/i386/sev.h b/target/i386/sev.h index d83428fa26..c96072bf78 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -14,7 +14,7 @@ #ifndef QEMU_SEV_I386_H #define QEMU_SEV_I386_H -#include "sysemu/sev.h" +#include "exec/confidential-guest-support.h" #include "qapi/qapi-types-misc-target.h" #define SEV_POLICY_NODBG 0x1 @@ -35,6 +35,7 @@ typedef struct SevKernelLoaderContext { size_t cmdline_size; } SevKernelLoaderContext; +bool sev_enabled(void); extern bool sev_es_enabled(void); extern SevInfo *sev_get_info(void); extern uint32_t sev_get_cbit_position(void); @@ -45,4 +46,13 @@ extern SevAttestationReport * sev_get_attestation_report(const char *mnonce, Error **errp); extern bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); +int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); +int sev_inject_launch_secret(const char *hdr, const char *secret, + uint64_t gpa, Error **errp); + +int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size); +void sev_es_set_reset_vector(CPUState *cpu); + +int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); + #endif From 02eacf31374152e6fb44554a4632f477d82dd9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:09 +0200 Subject: [PATCH 0442/1334] target/i386/sev: Remove stubs by using code elision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only declare sev_enabled() and sev_es_enabled() when CONFIG_SEV is set, to allow the compiler to elide unused code. Remove unnecessary stubs. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Eric Blake Message-Id: <20211007161716.453984-17-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 13 +++++++------ target/i386/meson.build | 2 +- target/i386/sev-stub.c | 41 ----------------------------------------- target/i386/sev.h | 12 +++++++++++- 4 files changed, 19 insertions(+), 49 deletions(-) delete mode 100644 target/i386/sev-stub.c diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 8289dc87bd..fc3ed80ef1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5764,12 +5764,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; break; case 0x8000001F: - *eax = sev_enabled() ? 0x2 : 0; - *eax |= sev_es_enabled() ? 0x8 : 0; - *ebx = sev_get_cbit_position(); - *ebx |= sev_get_reduced_phys_bits() << 6; - *ecx = 0; - *edx = 0; + *eax = *ebx = *ecx = *edx = 0; + if (sev_enabled()) { + *eax = 0x2; + *eax |= sev_es_enabled() ? 0x8 : 0; + *ebx = sev_get_cbit_position(); + *ebx |= sev_get_reduced_phys_bits() << 6; + } break; default: /* reserved values: zero */ diff --git a/target/i386/meson.build b/target/i386/meson.build index a4f45c3ec1..ae38dc9563 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -6,7 +6,7 @@ i386_ss.add(files( 'xsave_helper.c', 'cpu-dump.c', )) -i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c'), if_false: files('sev-stub.c')) +i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c')) # x86 cpu type i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c deleted file mode 100644 index 7e8b6f9a25..0000000000 --- a/target/i386/sev-stub.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * QEMU SEV stub - * - * Copyright Advanced Micro Devices 2018 - * - * Authors: - * Brijesh Singh - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "sev.h" - -bool sev_enabled(void) -{ - return false; -} - -uint32_t sev_get_cbit_position(void) -{ - return 0; -} - -uint32_t sev_get_reduced_phys_bits(void) -{ - return 0; -} - -bool sev_es_enabled(void) -{ - return false; -} - -bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) -{ - g_assert_not_reached(); -} diff --git a/target/i386/sev.h b/target/i386/sev.h index c96072bf78..b6289234b0 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -14,6 +14,10 @@ #ifndef QEMU_SEV_I386_H #define QEMU_SEV_I386_H +#ifndef CONFIG_USER_ONLY +#include CONFIG_DEVICES /* CONFIG_SEV */ +#endif + #include "exec/confidential-guest-support.h" #include "qapi/qapi-types-misc-target.h" @@ -35,8 +39,14 @@ typedef struct SevKernelLoaderContext { size_t cmdline_size; } SevKernelLoaderContext; +#ifdef CONFIG_SEV bool sev_enabled(void); -extern bool sev_es_enabled(void); +bool sev_es_enabled(void); +#else +#define sev_enabled() 0 +#define sev_es_enabled() 0 +#endif + extern SevInfo *sev_get_info(void); extern uint32_t sev_get_cbit_position(void); extern uint32_t sev_get_reduced_phys_bits(void); From 3208de1cd23937254a456b95ef16658b68821a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:10 +0200 Subject: [PATCH 0443/1334] target/i386/sev: Move qmp_query_sev_attestation_report() to sev.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move qmp_query_sev_attestation_report() from monitor.c to sev.c and make sev_get_attestation_report() static. We don't need the stub anymore, remove it. Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-18-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/monitor.c | 6 ------ target/i386/sev-sysemu-stub.c | 5 +++-- target/i386/sev.c | 12 ++++++++++-- target/i386/sev.h | 2 -- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 935a8ee8ca..cf4a8a61a0 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -763,12 +763,6 @@ void qmp_sev_inject_launch_secret(const char *packet_hdr, sev_inject_launch_secret(packet_hdr, secret, gpa, errp); } -SevAttestationReport * -qmp_query_sev_attestation_report(const char *mnonce, Error **errp) -{ - return sev_get_attestation_report(mnonce, errp); -} - SGXInfo *qmp_query_sgx(Error **errp) { return sgx_get_info(errp); diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index 8082781feb..d5ec6b32e0 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qapi/qapi-commands-misc-target.h" +#include "qapi/qmp/qerror.h" #include "qapi/error.h" #include "sev.h" @@ -52,8 +53,8 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) g_assert_not_reached(); } -SevAttestationReport *sev_get_attestation_report(const char *mnonce, - Error **errp) +SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce, + Error **errp) { error_setg(errp, "SEV is not available in this QEMU"); return NULL; diff --git a/target/i386/sev.c b/target/i386/sev.c index e43bbf3a17..038fa56058 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -32,6 +32,8 @@ #include "migration/blocker.h" #include "qom/object.h" #include "monitor/monitor.h" +#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qmp/qerror.h" #include "exec/confidential-guest-support.h" #include "hw/i386/pc.h" @@ -515,8 +517,8 @@ out: return cap; } -SevAttestationReport * -sev_get_attestation_report(const char *mnonce, Error **errp) +static SevAttestationReport *sev_get_attestation_report(const char *mnonce, + Error **errp) { struct kvm_sev_attestation_report input = {}; SevAttestationReport *report = NULL; @@ -578,6 +580,12 @@ sev_get_attestation_report(const char *mnonce, Error **errp) return report; } +SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce, + Error **errp) +{ + return sev_get_attestation_report(mnonce, errp); +} + static int sev_read_file_base64(const char *filename, guchar **data, gsize *len) { diff --git a/target/i386/sev.h b/target/i386/sev.h index b6289234b0..529a54acb8 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -52,8 +52,6 @@ extern uint32_t sev_get_cbit_position(void); extern uint32_t sev_get_reduced_phys_bits(void); extern char *sev_get_launch_measurement(void); extern SevCapability *sev_get_capabilities(Error **errp); -extern SevAttestationReport * -sev_get_attestation_report(const char *mnonce, Error **errp); extern bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); From 11a6ed0e77e8dd37ccea07575791b70c0410efea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:11 +0200 Subject: [PATCH 0444/1334] target/i386/sev: Move qmp_sev_inject_launch_secret() to sev.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move qmp_sev_inject_launch_secret() from monitor.c to sev.c and make sev_inject_launch_secret() static. We don't need the stub anymore, remove it. Previously with binaries built without SEV, management layer was getting an empty response: { "execute": "sev-inject-launch-secret", "arguments": { "packet-header": "mypkt", "secret": "mypass", "gpa": 4294959104 } } { "return": { } } Now the response is explicit, mentioning the feature is disabled: { "execute": "sev-inject-launch-secret", "arguments": { "packet-header": "mypkt", "secret": "mypass", "gpa": 4294959104 } } { "error": { "class": "GenericError", "desc": "this feature or command is not currently supported" } } Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-19-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/monitor.c | 31 ------------------------------- target/i386/sev-sysemu-stub.c | 6 +++--- target/i386/sev.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index cf4a8a61a0..22883ef2eb 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -732,37 +732,6 @@ SevCapability *qmp_query_sev_capabilities(Error **errp) return sev_get_capabilities(errp); } -#define SEV_SECRET_GUID "4c2eb361-7d9b-4cc3-8081-127c90d3d294" -struct sev_secret_area { - uint32_t base; - uint32_t size; -}; - -void qmp_sev_inject_launch_secret(const char *packet_hdr, - const char *secret, - bool has_gpa, uint64_t gpa, - Error **errp) -{ - if (!sev_enabled()) { - error_setg(errp, "SEV not enabled for guest"); - return; - } - if (!has_gpa) { - uint8_t *data; - struct sev_secret_area *area; - - if (!pc_system_ovmf_table_find(SEV_SECRET_GUID, &data, NULL)) { - error_setg(errp, "SEV: no secret area found in OVMF," - " gpa must be specified."); - return; - } - area = (struct sev_secret_area *)data; - gpa = area->base; - } - - sev_inject_launch_secret(packet_hdr, secret, gpa, errp); -} - SGXInfo *qmp_query_sgx(Error **errp) { return sgx_get_info(errp); diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index d5ec6b32e0..82c5ebb92f 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -33,10 +33,10 @@ SevCapability *sev_get_capabilities(Error **errp) return NULL; } -int sev_inject_launch_secret(const char *hdr, const char *secret, - uint64_t gpa, Error **errp) +void qmp_sev_inject_launch_secret(const char *packet_header, const char *secret, + bool has_gpa, uint64_t gpa, Error **errp) { - return 1; + error_setg(errp, "SEV is not available in this QEMU"); } int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) diff --git a/target/i386/sev.c b/target/i386/sev.c index 038fa56058..072bb6f0fd 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -973,6 +973,37 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, return 0; } +#define SEV_SECRET_GUID "4c2eb361-7d9b-4cc3-8081-127c90d3d294" +struct sev_secret_area { + uint32_t base; + uint32_t size; +}; + +void qmp_sev_inject_launch_secret(const char *packet_hdr, + const char *secret, + bool has_gpa, uint64_t gpa, + Error **errp) +{ + if (!sev_enabled()) { + error_setg(errp, "SEV not enabled for guest"); + return; + } + if (!has_gpa) { + uint8_t *data; + struct sev_secret_area *area; + + if (!pc_system_ovmf_table_find(SEV_SECRET_GUID, &data, NULL)) { + error_setg(errp, "SEV: no secret area found in OVMF," + " gpa must be specified."); + return; + } + area = (struct sev_secret_area *)data; + gpa = area->base; + } + + sev_inject_launch_secret(packet_hdr, secret, gpa, errp); +} + static int sev_es_parse_reset_block(SevInfoBlock *info, uint32_t *addr) { From 8371df29021241b43a7a9df6369fc70f649551a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:12 +0200 Subject: [PATCH 0445/1334] target/i386/sev: Move qmp_query_sev_capabilities() to sev.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move qmp_query_sev_capabilities() from monitor.c to sev.c and make sev_get_capabilities() static. We don't need the stub anymore, remove it. Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-20-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/monitor.c | 5 ----- target/i386/sev-sysemu-stub.c | 2 +- target/i386/sev.c | 8 ++++++-- target/i386/sev.h | 1 - 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 22883ef2eb..4c017b59b3 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -727,11 +727,6 @@ SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) return info; } -SevCapability *qmp_query_sev_capabilities(Error **errp) -{ - return sev_get_capabilities(errp); -} - SGXInfo *qmp_query_sgx(Error **errp) { return sgx_get_info(errp); diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index 82c5ebb92f..3e8cab4c14 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -27,7 +27,7 @@ char *sev_get_launch_measurement(void) return NULL; } -SevCapability *sev_get_capabilities(Error **errp) +SevCapability *qmp_query_sev_capabilities(Error **errp) { error_setg(errp, "SEV is not available in this QEMU"); return NULL; diff --git a/target/i386/sev.c b/target/i386/sev.c index 072bb6f0fd..56e9e03acc 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -466,8 +466,7 @@ e_free: return 1; } -SevCapability * -sev_get_capabilities(Error **errp) +static SevCapability *sev_get_capabilities(Error **errp) { SevCapability *cap = NULL; guchar *pdh_data = NULL; @@ -517,6 +516,11 @@ out: return cap; } +SevCapability *qmp_query_sev_capabilities(Error **errp) +{ + return sev_get_capabilities(errp); +} + static SevAttestationReport *sev_get_attestation_report(const char *mnonce, Error **errp) { diff --git a/target/i386/sev.h b/target/i386/sev.h index 529a54acb8..35e702e57b 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -51,7 +51,6 @@ extern SevInfo *sev_get_info(void); extern uint32_t sev_get_cbit_position(void); extern uint32_t sev_get_reduced_phys_bits(void); extern char *sev_get_launch_measurement(void); -extern SevCapability *sev_get_capabilities(Error **errp); extern bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); From 0875a7038b8de0d79479a7d4662f0a9cdfa61940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:13 +0200 Subject: [PATCH 0446/1334] target/i386/sev: Move qmp_query_sev_launch_measure() to sev.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move qmp_query_sev_launch_measure() from monitor.c to sev.c and make sev_get_launch_measurement() static. We don't need the stub anymore, remove it. Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-21-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/monitor.c | 17 ----------------- target/i386/sev-sysemu-stub.c | 3 ++- target/i386/sev.c | 20 ++++++++++++++++++-- target/i386/sev.h | 1 - 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 4c017b59b3..bd24d0d473 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -710,23 +710,6 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict) qapi_free_SevInfo(info); } -SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) -{ - char *data; - SevLaunchMeasureInfo *info; - - data = sev_get_launch_measurement(); - if (!data) { - error_setg(errp, "SEV launch measurement is not available"); - return NULL; - } - - info = g_malloc0(sizeof(*info)); - info->data = data; - - return info; -} - SGXInfo *qmp_query_sgx(Error **errp) { return sgx_get_info(errp); diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index 3e8cab4c14..8d97d7c7e1 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -22,8 +22,9 @@ SevInfo *sev_get_info(void) return NULL; } -char *sev_get_launch_measurement(void) +SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) { + error_setg(errp, "SEV is not available in this QEMU"); return NULL; } diff --git a/target/i386/sev.c b/target/i386/sev.c index 56e9e03acc..ec874b3df8 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -742,8 +742,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) trace_kvm_sev_launch_measurement(sev->measurement); } -char * -sev_get_launch_measurement(void) +static char *sev_get_launch_measurement(void) { if (sev_guest && sev_guest->state >= SEV_STATE_LAUNCH_SECRET) { @@ -753,6 +752,23 @@ sev_get_launch_measurement(void) return NULL; } +SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) +{ + char *data; + SevLaunchMeasureInfo *info; + + data = sev_get_launch_measurement(); + if (!data) { + error_setg(errp, "SEV launch measurement is not available"); + return NULL; + } + + info = g_malloc0(sizeof(*info)); + info->data = data; + + return info; +} + static Notifier sev_machine_done_notify = { .notify = sev_launch_get_measure, }; diff --git a/target/i386/sev.h b/target/i386/sev.h index 35e702e57b..9ee1429395 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -50,7 +50,6 @@ bool sev_es_enabled(void); extern SevInfo *sev_get_info(void); extern uint32_t sev_get_cbit_position(void); extern uint32_t sev_get_reduced_phys_bits(void); -extern char *sev_get_launch_measurement(void); extern bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); From aa3950182f902a48f60d7040069b092eb5aaae21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:14 +0200 Subject: [PATCH 0447/1334] target/i386/sev: Move qmp_query_sev() & hmp_info_sev() to sev.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move qmp_query_sev() & hmp_info_sev()() from monitor.c to sev.c and make sev_get_info() static. We don't need the stub anymore, remove it. Add a stub for hmp_info_sev(). Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-22-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/monitor.c | 35 -------------------------------- target/i386/sev-sysemu-stub.c | 10 ++++++++- target/i386/sev.c | 38 +++++++++++++++++++++++++++++++++-- target/i386/sev.h | 2 -- 4 files changed, 45 insertions(+), 40 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index bd24d0d473..680d282591 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -31,7 +31,6 @@ #include "qapi/qmp/qerror.h" #include "sysemu/kvm.h" #include "qapi/error.h" -#include "sev.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" #include "hw/i386/pc.h" @@ -676,40 +675,6 @@ void hmp_info_io_apic(Monitor *mon, const QDict *qdict) "removed soon. Please use 'info pic' instead.\n"); } -SevInfo *qmp_query_sev(Error **errp) -{ - SevInfo *info; - - info = sev_get_info(); - if (!info) { - error_setg(errp, "SEV feature is not available"); - return NULL; - } - - return info; -} - -void hmp_info_sev(Monitor *mon, const QDict *qdict) -{ - SevInfo *info = sev_get_info(); - - if (info && info->enabled) { - monitor_printf(mon, "handle: %d\n", info->handle); - monitor_printf(mon, "state: %s\n", SevState_str(info->state)); - monitor_printf(mon, "build: %d\n", info->build_id); - monitor_printf(mon, "api version: %d.%d\n", - info->api_major, info->api_minor); - monitor_printf(mon, "debug: %s\n", - info->policy & SEV_POLICY_NODBG ? "off" : "on"); - monitor_printf(mon, "key-sharing: %s\n", - info->policy & SEV_POLICY_NOKS ? "off" : "on"); - } else { - monitor_printf(mon, "SEV is not enabled\n"); - } - - qapi_free_SevInfo(info); -} - SGXInfo *qmp_query_sgx(Error **errp) { return sgx_get_info(errp); diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index 8d97d7c7e1..68518fd3f9 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -12,13 +12,16 @@ */ #include "qemu/osdep.h" +#include "monitor/monitor.h" +#include "monitor/hmp.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qmp/qerror.h" #include "qapi/error.h" #include "sev.h" -SevInfo *sev_get_info(void) +SevInfo *qmp_query_sev(Error **errp) { + error_setg(errp, "SEV is not available in this QEMU"); return NULL; } @@ -60,3 +63,8 @@ SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce, error_setg(errp, "SEV is not available in this QEMU"); return NULL; } + +void hmp_info_sev(Monitor *mon, const QDict *qdict) +{ + monitor_printf(mon, "SEV is not available in this QEMU\n"); +} diff --git a/target/i386/sev.c b/target/i386/sev.c index ec874b3df8..19504796fb 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -32,6 +32,7 @@ #include "migration/blocker.h" #include "qom/object.h" #include "monitor/monitor.h" +#include "monitor/hmp.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qmp/qerror.h" #include "exec/confidential-guest-support.h" @@ -402,8 +403,7 @@ sev_get_reduced_phys_bits(void) return sev_guest ? sev_guest->reduced_phys_bits : 0; } -SevInfo * -sev_get_info(void) +static SevInfo *sev_get_info(void) { SevInfo *info; @@ -422,6 +422,40 @@ sev_get_info(void) return info; } +SevInfo *qmp_query_sev(Error **errp) +{ + SevInfo *info; + + info = sev_get_info(); + if (!info) { + error_setg(errp, "SEV feature is not available"); + return NULL; + } + + return info; +} + +void hmp_info_sev(Monitor *mon, const QDict *qdict) +{ + SevInfo *info = sev_get_info(); + + if (info && info->enabled) { + monitor_printf(mon, "handle: %d\n", info->handle); + monitor_printf(mon, "state: %s\n", SevState_str(info->state)); + monitor_printf(mon, "build: %d\n", info->build_id); + monitor_printf(mon, "api version: %d.%d\n", + info->api_major, info->api_minor); + monitor_printf(mon, "debug: %s\n", + info->policy & SEV_POLICY_NODBG ? "off" : "on"); + monitor_printf(mon, "key-sharing: %s\n", + info->policy & SEV_POLICY_NOKS ? "off" : "on"); + } else { + monitor_printf(mon, "SEV is not enabled\n"); + } + + qapi_free_SevInfo(info); +} + static int sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, size_t *cert_chain_len, Error **errp) diff --git a/target/i386/sev.h b/target/i386/sev.h index 9ee1429395..83e82aa42c 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -19,7 +19,6 @@ #endif #include "exec/confidential-guest-support.h" -#include "qapi/qapi-types-misc-target.h" #define SEV_POLICY_NODBG 0x1 #define SEV_POLICY_NOKS 0x2 @@ -47,7 +46,6 @@ bool sev_es_enabled(void); #define sev_es_enabled() 0 #endif -extern SevInfo *sev_get_info(void); extern uint32_t sev_get_cbit_position(void); extern uint32_t sev_get_reduced_phys_bits(void); extern bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); From cd35beb488c655a86da60d85949c026387187171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:15 +0200 Subject: [PATCH 0448/1334] monitor: Reduce hmp_info_sev() declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being conditionally used for TARGET_I386 in hmp-commands-info.hx, hmp_info_sev() is declared for all targets. Reduce its declaration to target including "monitor/hmp-target.h". This is a minor cleanup. Reviewed-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-23-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- include/monitor/hmp-target.h | 1 + include/monitor/hmp.h | 1 - target/i386/sev-sysemu-stub.c | 2 +- target/i386/sev.c | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h index dc53add7ee..96956d0fc4 100644 --- a/include/monitor/hmp-target.h +++ b/include/monitor/hmp-target.h @@ -49,6 +49,7 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict); void hmp_mce(Monitor *mon, const QDict *qdict); void hmp_info_local_apic(Monitor *mon, const QDict *qdict); void hmp_info_io_apic(Monitor *mon, const QDict *qdict); +void hmp_info_sev(Monitor *mon, const QDict *qdict); void hmp_info_sgx(Monitor *mon, const QDict *qdict); #endif /* MONITOR_HMP_TARGET_H */ diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 3baa1058e2..6bc27639e0 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -124,7 +124,6 @@ void hmp_info_ramblock(Monitor *mon, const QDict *qdict); void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict); void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict); void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict); -void hmp_info_sev(Monitor *mon, const QDict *qdict); void hmp_info_replay(Monitor *mon, const QDict *qdict); void hmp_replay_break(Monitor *mon, const QDict *qdict); void hmp_replay_delete_break(Monitor *mon, const QDict *qdict); diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index 68518fd3f9..7a29295d1e 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "monitor/monitor.h" -#include "monitor/hmp.h" +#include "monitor/hmp-target.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qmp/qerror.h" #include "qapi/error.h" diff --git a/target/i386/sev.c b/target/i386/sev.c index 19504796fb..4c64c68244 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -32,7 +32,7 @@ #include "migration/blocker.h" #include "qom/object.h" #include "monitor/monitor.h" -#include "monitor/hmp.h" +#include "monitor/hmp-target.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qmp/qerror.h" #include "exec/confidential-guest-support.h" From 8c9e7f8c8a6de68be1e31a645f7d76855fc219ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 18:17:16 +0200 Subject: [PATCH 0449/1334] MAINTAINERS: Cover SEV-related files with X86/KVM section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete the x86/KVM section with SEV-related files. Suggested-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007161716.453984-24-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 32b668e92f..e31c190b47 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -417,7 +417,9 @@ M: Paolo Bonzini M: Marcelo Tosatti L: kvm@vger.kernel.org S: Supported +F: docs/amd-memory-encryption.txt F: target/i386/kvm/ +F: target/i386/sev* F: scripts/kvm/vmxcap Guest CPU Cores (other accelerators) From f1279fc15bf4315fb8e103e6eabaee014ce1f914 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 28 Sep 2021 18:02:32 +0200 Subject: [PATCH 0450/1334] qapi: Make some ObjectTypes depend on the build settings Some of the ObjectType entries already depend on CONFIG_* switches. Some others also only make sense with certain configurations, but are currently always listed in the ObjectType enum. Let's make them depend on the correpsonding CONFIG_* switches, too, so that upper layers (like libvirt) have a better way to determine which features are available in QEMU. Signed-off-by: Thomas Huth Reviewed-by: Markus Armbruster Message-Id: <20210928160232.432980-1-thuth@redhat.com> [Do the same for MemoryBackendEpcProperties. - Paolo] Signed-off-by: Paolo Bonzini --- qapi/qom.json | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/qapi/qom.json b/qapi/qom.json index 0222bb4506..7231ac3f34 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -794,7 +794,8 @@ 'authz-pam', 'authz-simple', 'can-bus', - 'can-host-socketcan', + { 'name': 'can-host-socketcan', + 'if': 'CONFIG_LINUX' }, 'colo-compare', 'cryptodev-backend', 'cryptodev-backend-builtin', @@ -808,21 +809,26 @@ 'filter-replay', 'filter-rewriter', 'input-barrier', - 'input-linux', + { 'name': 'input-linux', + 'if': 'CONFIG_LINUX' }, 'iothread', + { 'name': 'memory-backend-epc', + 'if': 'CONFIG_LINUX' }, 'memory-backend-file', { 'name': 'memory-backend-memfd', 'if': 'CONFIG_LINUX' }, 'memory-backend-ram', - 'memory-backend-epc', 'pef-guest', - 'pr-manager-helper', + { 'name': 'pr-manager-helper', + 'if': 'CONFIG_LINUX' }, 'qtest', 'rng-builtin', 'rng-egd', - 'rng-random', + { 'name': 'rng-random', + 'if': 'CONFIG_POSIX' }, 'secret', - 'secret_keyring', + { 'name': 'secret_keyring', + 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest', 's390-pv-guest', 'throttle-group', @@ -853,7 +859,8 @@ 'authz-listfile': 'AuthZListFileProperties', 'authz-pam': 'AuthZPAMProperties', 'authz-simple': 'AuthZSimpleProperties', - 'can-host-socketcan': 'CanHostSocketcanProperties', + 'can-host-socketcan': { 'type': 'CanHostSocketcanProperties', + 'if': 'CONFIG_LINUX' }, 'colo-compare': 'ColoCompareProperties', 'cryptodev-backend': 'CryptodevBackendProperties', 'cryptodev-backend-builtin': 'CryptodevBackendProperties', @@ -867,20 +874,25 @@ 'filter-replay': 'NetfilterProperties', 'filter-rewriter': 'FilterRewriterProperties', 'input-barrier': 'InputBarrierProperties', - 'input-linux': 'InputLinuxProperties', + 'input-linux': { 'type': 'InputLinuxProperties', + 'if': 'CONFIG_LINUX' }, 'iothread': 'IothreadProperties', + 'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties', + 'if': 'CONFIG_LINUX' }, 'memory-backend-file': 'MemoryBackendFileProperties', 'memory-backend-memfd': { 'type': 'MemoryBackendMemfdProperties', 'if': 'CONFIG_LINUX' }, 'memory-backend-ram': 'MemoryBackendProperties', - 'memory-backend-epc': 'MemoryBackendEpcProperties', - 'pr-manager-helper': 'PrManagerHelperProperties', + 'pr-manager-helper': { 'type': 'PrManagerHelperProperties', + 'if': 'CONFIG_LINUX' }, 'qtest': 'QtestProperties', 'rng-builtin': 'RngProperties', 'rng-egd': 'RngEgdProperties', - 'rng-random': 'RngRandomProperties', + 'rng-random': { 'type': 'RngRandomProperties', + 'if': 'CONFIG_POSIX' }, 'secret': 'SecretProperties', - 'secret_keyring': 'SecretKeyringProperties', + 'secret_keyring': { 'type': 'SecretKeyringProperties', + 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest': 'SevGuestProperties', 'throttle-group': 'ThrottleGroupProperties', 'tls-creds-anon': 'TlsCredsAnonProperties', From 8e751e9c38e324737fd3d3aa0562f886313bba3c Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 8 Oct 2021 12:21:01 -0400 Subject: [PATCH 0451/1334] tests: tcg: Fix PVH test with binutils 2.36+ binutils started adding a .note.gnu.property ELF section which makes the PVH test fail: TEST hello on x86_64 qemu-system-x86_64: Error loading uncompressed kernel without PVH ELF Note Discard .note.gnu* while keeping the PVH .note bits intact. This also strips the build-id note, so drop the related comment. Signed-off-by: Cole Robinson Message-Id: <5ab2a54c262c61f64c22dbb49ade3e2db8a740bb.1633708346.git.crobinso@redhat.com> Signed-off-by: Paolo Bonzini --- tests/tcg/x86_64/system/kernel.ld | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/tcg/x86_64/system/kernel.ld b/tests/tcg/x86_64/system/kernel.ld index 49c12b04ae..ca5d6bd850 100644 --- a/tests/tcg/x86_64/system/kernel.ld +++ b/tests/tcg/x86_64/system/kernel.ld @@ -16,7 +16,10 @@ SECTIONS { *(.rodata) } :text - /* Keep build ID and PVH notes in same section */ + /DISCARD/ : { + *(.note.gnu*) + } + .notes : { *(.note.*) } :note From dec490db8b5c98d60814d07c23ddf70708a56d6e Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 8 Oct 2021 07:46:16 +0200 Subject: [PATCH 0452/1334] hvf: Determine slot count from struct layout We can handle up to a static amount of memory slots, capped by the size of an internal array. Let's make sure that array size is the only source of truth for the number of elements in that array. Signed-off-by: Alexander Graf Reviewed-by: Richard Henderson Message-Id: <20211008054616.43828-1-agraf@csgraf.de> Signed-off-by: Paolo Bonzini --- accel/hvf/hvf-accel-ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 6cbd2c3f97..2b2c411076 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -321,7 +321,7 @@ static int hvf_accel_init(MachineState *ms) s = g_new0(HVFState, 1); - s->num_slots = 32; + s->num_slots = ARRAY_SIZE(s->slots); for (x = 0; x < s->num_slots; ++x) { s->slots[x].size = 0; s->slots[x].slot_id = x; From a821186ec02ecf5ac4949ed42410bf2095bcea66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 19:56:09 +0200 Subject: [PATCH 0453/1334] MAINTAINERS: Cover SGX documentation file with X86/KVM section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Yang Zhong Cc: Paolo Bonzini Cc: Marcelo Tosatti Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007175612.496366-2-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e31c190b47..cfefe386ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -418,6 +418,7 @@ M: Marcelo Tosatti L: kvm@vger.kernel.org S: Supported F: docs/amd-memory-encryption.txt +F: docs/system/i386/sgx.rst F: target/i386/kvm/ F: target/i386/sev* F: scripts/kvm/vmxcap From 05fc8db720a2effad2fe523ab45c1ce5f8f32379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 19:56:10 +0200 Subject: [PATCH 0454/1334] hw/i386/sgx: Have sgx_epc_get_section() return a boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007175612.496366-3-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/sgx-stub.c | 2 +- hw/i386/sgx.c | 6 +++--- include/hw/i386/sgx-epc.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 3be9f5ca32..45c473119e 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -20,7 +20,7 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) memset(&pcms->sgx_epc, 0, sizeof(SGXEPCState)); } -int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) +bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { g_assert_not_reached(); } diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index e481e9358f..29724ff8f0 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -115,13 +115,13 @@ SGXInfo *sgx_get_info(Error **errp) return info; } -int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) +bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); SGXEPCDevice *epc; if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) { - return 1; + return true; } epc = pcms->sgx_epc.sections[section_nr]; @@ -129,7 +129,7 @@ int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) *addr = epc->addr; *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal); - return 0; + return false; } void pc_machine_init_sgx_epc(PCMachineState *pcms) diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h index 65a68ca753..a6a65be854 100644 --- a/include/hw/i386/sgx-epc.h +++ b/include/hw/i386/sgx-epc.h @@ -55,7 +55,7 @@ typedef struct SGXEPCState { int nr_sections; } SGXEPCState; -int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size); +bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size); static inline uint64_t sgx_epc_above_4g_end(SGXEPCState *sgx_epc) { From 021658566b71e138db46434849f0556bab3fc30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 19:56:11 +0200 Subject: [PATCH 0455/1334] hw/i386/sgx: Move qmp_query_sgx_capabilities() to hw/i386/sgx.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move qmp_query_sgx_capabilities() from target/i386/monitor.c to hw/i386/sgx.c, removing the sgx_get_capabilities() indirection. Suggested-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007175612.496366-4-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/sgx-stub.c | 4 +++- hw/i386/sgx.c | 3 ++- include/hw/i386/sgx.h | 1 - target/i386/monitor.c | 5 ----- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 45c473119e..3749656db1 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -2,6 +2,8 @@ #include "hw/i386/pc.h" #include "hw/i386/sgx-epc.h" #include "hw/i386/sgx.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" SGXInfo *sgx_get_info(Error **errp) { @@ -9,7 +11,7 @@ SGXInfo *sgx_get_info(Error **errp) return NULL; } -SGXInfo *sgx_get_capabilities(Error **errp) +SGXInfo *qmp_query_sgx_capabilities(Error **errp) { error_setg(errp, "SGX support is not compiled in"); return NULL; diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 29724ff8f0..713f136343 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -16,6 +16,7 @@ #include "hw/mem/memory-device.h" #include "monitor/qdev.h" #include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" #include "exec/address-spaces.h" #include "hw/i386/sgx.h" #include "sysemu/hw_accel.h" @@ -57,7 +58,7 @@ static uint64_t sgx_calc_host_epc_section_size(void) return size; } -SGXInfo *sgx_get_capabilities(Error **errp) +SGXInfo *qmp_query_sgx_capabilities(Error **errp) { SGXInfo *info = NULL; uint32_t eax, ebx, ecx, edx; diff --git a/include/hw/i386/sgx.h b/include/hw/i386/sgx.h index 16fc25725c..2bf90b3f4f 100644 --- a/include/hw/i386/sgx.h +++ b/include/hw/i386/sgx.h @@ -7,6 +7,5 @@ #include "qapi/qapi-types-misc-target.h" SGXInfo *sgx_get_info(Error **errp); -SGXInfo *sgx_get_capabilities(Error **errp); #endif diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 680d282591..84fba47f19 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -700,8 +700,3 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) monitor_printf(mon, "size: %" PRIu64 "\n", info->section_size); } - -SGXInfo *qmp_query_sgx_capabilities(Error **errp) -{ - return sgx_get_capabilities(errp); -} From 6e81733e27a250af7507c3dd5ae945c473ab0d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Oct 2021 19:56:12 +0200 Subject: [PATCH 0456/1334] hw/i386/sgx: Move qmp_query_sgx() and hmp_info_sgx() to hw/i386/sgx.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move qmp_query_sgx() and hmp_info_sgx() from target/i386/monitor.c to hw/i386/sgx.c, removing the sgx_get_info() indirection and the "hw/i386/sgx.h" header. Suggested-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211007175612.496366-5-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/sgx-stub.c | 10 ++++++++-- hw/i386/sgx.c | 26 ++++++++++++++++++++++++-- include/hw/i386/sgx.h | 11 ----------- target/i386/monitor.c | 27 --------------------------- 4 files changed, 32 insertions(+), 42 deletions(-) delete mode 100644 include/hw/i386/sgx.h diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 3749656db1..c9b379e665 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -1,11 +1,12 @@ #include "qemu/osdep.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" #include "hw/i386/pc.h" #include "hw/i386/sgx-epc.h" -#include "hw/i386/sgx.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" -SGXInfo *sgx_get_info(Error **errp) +SGXInfo *qmp_query_sgx(Error **errp) { error_setg(errp, "SGX support is not compiled in"); return NULL; @@ -17,6 +18,11 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) return NULL; } +void hmp_info_sgx(Monitor *mon, const QDict *qdict) +{ + monitor_printf(mon, "SGX is not available in this QEMU\n"); +} + void pc_machine_init_sgx_epc(PCMachineState *pcms) { memset(&pcms->sgx_epc, 0, sizeof(SGXEPCState)); diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 713f136343..11607568b6 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -15,10 +15,11 @@ #include "hw/i386/sgx-epc.h" #include "hw/mem/memory-device.h" #include "monitor/qdev.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" #include "exec/address-spaces.h" -#include "hw/i386/sgx.h" #include "sysemu/hw_accel.h" #define SGX_MAX_EPC_SECTIONS 8 @@ -86,7 +87,7 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) return info; } -SGXInfo *sgx_get_info(Error **errp) +SGXInfo *qmp_query_sgx(Error **errp) { SGXInfo *info = NULL; X86MachineState *x86ms; @@ -116,6 +117,27 @@ SGXInfo *sgx_get_info(Error **errp) return info; } +void hmp_info_sgx(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + g_autoptr(SGXInfo) info = qmp_query_sgx(&err); + + if (err) { + error_report_err(err); + return; + } + monitor_printf(mon, "SGX support: %s\n", + info->sgx ? "enabled" : "disabled"); + monitor_printf(mon, "SGX1 support: %s\n", + info->sgx1 ? "enabled" : "disabled"); + monitor_printf(mon, "SGX2 support: %s\n", + info->sgx2 ? "enabled" : "disabled"); + monitor_printf(mon, "FLC support: %s\n", + info->flc ? "enabled" : "disabled"); + monitor_printf(mon, "size: %" PRIu64 "\n", + info->section_size); +} + bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); diff --git a/include/hw/i386/sgx.h b/include/hw/i386/sgx.h deleted file mode 100644 index 2bf90b3f4f..0000000000 --- a/include/hw/i386/sgx.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef QEMU_SGX_H -#define QEMU_SGX_H - -#include "qom/object.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "qapi/qapi-types-misc-target.h" - -SGXInfo *sgx_get_info(Error **errp); - -#endif diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 84fba47f19..8166e17693 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -34,7 +34,6 @@ #include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" #include "hw/i386/pc.h" -#include "hw/i386/sgx.h" /* Perform linear address sign extension */ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr) @@ -674,29 +673,3 @@ void hmp_info_io_apic(Monitor *mon, const QDict *qdict) monitor_printf(mon, "This command is obsolete and will be " "removed soon. Please use 'info pic' instead.\n"); } - -SGXInfo *qmp_query_sgx(Error **errp) -{ - return sgx_get_info(errp); -} - -void hmp_info_sgx(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - g_autoptr(SGXInfo) info = qmp_query_sgx(&err); - - if (err) { - error_report_err(err); - return; - } - monitor_printf(mon, "SGX support: %s\n", - info->sgx ? "enabled" : "disabled"); - monitor_printf(mon, "SGX1 support: %s\n", - info->sgx1 ? "enabled" : "disabled"); - monitor_printf(mon, "SGX2 support: %s\n", - info->sgx2 ? "enabled" : "disabled"); - monitor_printf(mon, "FLC support: %s\n", - info->flc ? "enabled" : "disabled"); - monitor_printf(mon, "size: %" PRIu64 "\n", - info->section_size); -} From b71803a74612cc73eee82681f942a083038642ba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 Oct 2021 20:55:01 +0200 Subject: [PATCH 0457/1334] Revert "hw/misc: applesmc: use host osk as default on macs" This reverts commit 93ddefbc3c909bb6c3b76086f1dfc8ad98dd3725. The commit included code under the APSL 2.0, which is incompatible with the GPL v2. Signed-off-by: Paolo Bonzini --- hw/misc/applesmc.c | 192 +-------------------------------------------- 1 file changed, 1 insertion(+), 191 deletions(-) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index cec247b5ee..1b9acaf1d3 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -38,171 +38,6 @@ #include "qemu/timer.h" #include "qom/object.h" -#if defined(__APPLE__) && defined(__MACH__) -#include - -enum { - kSMCSuccess = 0x00, - kSMCKeyNotFound = 0x84 -}; - -enum { - kSMCUserClientOpen = 0x00, - kSMCUserClientClose = 0x01, - kSMCHandleYPCEvent = 0x02, - kSMCReadKey = 0x05, - kSMCGetKeyInfo = 0x09 -}; - -typedef struct SMCVersion { - uint8_t major; - uint8_t minor; - uint8_t build; - uint8_t reserved; - uint16_t release; -} SMCVersion; - -typedef struct SMCPLimitData { - uint16_t version; - uint16_t length; - uint32_t cpuPLimit; - uint32_t gpuPLimit; - uint32_t memPLimit; -} SMCPLimitData; - -typedef struct SMCKeyInfoData { - IOByteCount dataSize; - uint32_t dataType; - uint8_t dataAttributes; -} SMCKeyInfoData; - -typedef struct { - uint32_t key; - SMCVersion vers; - SMCPLimitData pLimitData; - SMCKeyInfoData keyInfo; - uint8_t result; - uint8_t status; - uint8_t data8; - uint32_t data32; - uint8_t bytes[32]; -} SMCParamStruct; - -static IOReturn smc_call_struct_method(uint32_t selector, - SMCParamStruct *inputStruct, - SMCParamStruct *outputStruct) -{ - IOReturn ret; - - size_t inputStructCnt = sizeof(SMCParamStruct); - size_t outputStructCnt = sizeof(SMCParamStruct); - - io_service_t smcService = IO_OBJECT_NULL; - io_connect_t smcConnect = IO_OBJECT_NULL; - - smcService = IOServiceGetMatchingService(kIOMasterPortDefault, - IOServiceMatching("AppleSMC")); - if (smcService == IO_OBJECT_NULL) { - ret = kIOReturnNotFound; - goto exit; - } - - ret = IOServiceOpen(smcService, mach_task_self(), 1, &smcConnect); - if (ret != kIOReturnSuccess) { - smcConnect = IO_OBJECT_NULL; - goto exit; - } - if (smcConnect == IO_OBJECT_NULL) { - ret = kIOReturnError; - goto exit; - } - - ret = IOConnectCallMethod(smcConnect, kSMCUserClientOpen, - NULL, 0, NULL, 0, - NULL, NULL, NULL, NULL); - if (ret != kIOReturnSuccess) { - goto exit; - } - - ret = IOConnectCallStructMethod(smcConnect, selector, - inputStruct, inputStructCnt, - outputStruct, &outputStructCnt); - -exit: - if (smcConnect != IO_OBJECT_NULL) { - IOConnectCallMethod(smcConnect, kSMCUserClientClose, - NULL, 0, NULL, 0, NULL, - NULL, NULL, NULL); - IOServiceClose(smcConnect); - } - - return ret; -} - -static IOReturn smc_read_key(uint32_t key, - uint8_t *bytes, - IOByteCount *dataSize) -{ - IOReturn ret; - - SMCParamStruct inputStruct; - SMCParamStruct outputStruct; - - if (key == 0 || bytes == NULL) { - ret = kIOReturnCannotWire; - goto exit; - } - - /* determine key's data size */ - memset(&inputStruct, 0, sizeof(SMCParamStruct)); - inputStruct.data8 = kSMCGetKeyInfo; - inputStruct.key = key; - - memset(&outputStruct, 0, sizeof(SMCParamStruct)); - ret = smc_call_struct_method(kSMCHandleYPCEvent, &inputStruct, &outputStruct); - if (ret != kIOReturnSuccess) { - goto exit; - } - if (outputStruct.result == kSMCKeyNotFound) { - ret = kIOReturnNotFound; - goto exit; - } - if (outputStruct.result != kSMCSuccess) { - ret = kIOReturnInternalError; - goto exit; - } - - /* get key value */ - memset(&inputStruct, 0, sizeof(SMCParamStruct)); - inputStruct.data8 = kSMCReadKey; - inputStruct.key = key; - inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; - - memset(&outputStruct, 0, sizeof(SMCParamStruct)); - ret = smc_call_struct_method(kSMCHandleYPCEvent, &inputStruct, &outputStruct); - if (ret != kIOReturnSuccess) { - goto exit; - } - if (outputStruct.result == kSMCKeyNotFound) { - ret = kIOReturnNotFound; - goto exit; - } - if (outputStruct.result != kSMCSuccess) { - ret = kIOReturnInternalError; - goto exit; - } - - memset(bytes, 0, *dataSize); - if (*dataSize > inputStruct.keyInfo.dataSize) { - *dataSize = inputStruct.keyInfo.dataSize; - } - memcpy(bytes, outputStruct.bytes, *dataSize); - -exit: - return ret; -} -#endif - /* #define DEBUG_SMC */ #define APPLESMC_DEFAULT_IOBASE 0x300 @@ -480,7 +315,6 @@ static const MemoryRegionOps applesmc_err_io_ops = { static void applesmc_isa_realize(DeviceState *dev, Error **errp) { AppleSMCState *s = APPLE_SMC(dev); - bool valid_key = false; memory_region_init_io(&s->io_data, OBJECT(s), &applesmc_data_io_ops, s, "applesmc-data", 1); @@ -497,31 +331,7 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp) isa_register_ioport(&s->parent_obj, &s->io_err, s->iobase + APPLESMC_ERR_PORT); - if (s->osk) { - valid_key = strlen(s->osk) == 64; - } else { -#if defined(__APPLE__) && defined(__MACH__) - IOReturn ret; - IOByteCount size = 32; - - ret = smc_read_key('OSK0', (uint8_t *) default_osk, &size); - if (ret != kIOReturnSuccess) { - goto failure; - } - - ret = smc_read_key('OSK1', (uint8_t *) default_osk + size, &size); - if (ret != kIOReturnSuccess) { - goto failure; - } - - warn_report("Using AppleSMC with host key"); - valid_key = true; - s->osk = default_osk; -failure:; -#endif - } - - if (!valid_key) { + if (!s->osk || (strlen(s->osk) != 64)) { warn_report("Using AppleSMC with invalid key"); s->osk = default_osk; } From 69520261204b9a1769134ed24b92cc0fa8445951 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 16 Sep 2021 13:17:07 +0200 Subject: [PATCH 0458/1334] monitor: Tidy up find_device_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 6287d827d4 "monitor: allow device_del to accept QOM paths" extended find_device_state() to accept QOM paths in addition to qdev IDs. This added a checked conversion to TYPE_DEVICE at the end, which duplicates the check done for the qdev ID case earlier, except it sets a *different* error: GenericError "ID is not a hotpluggable device" when passed a QOM path, and DeviceNotFound "Device 'ID' not found" when passed a qdev ID. Fortunately, the latter won't happen as long as we add only devices to /machine/peripheral/. Earlier, commit b6cc36abb2 "qdev: device_del: Search for to be unplugged device in 'peripheral' container" rewrote the lookup by qdev ID to use QOM instead of qdev_find_recursive(), so it can handle buss-less devices. It does so by constructing an absolute QOM path. Works, but object_resolve_path_component() is easier. Switching to it also gets rid of the unclean duplication described above. While there, avoid converting to TYPE_DEVICE twice, first to check whether it's possible, and then for real. Signed-off-by: Markus Armbruster Reviewed-by: Damien Hedde Reviewed-by: Daniel P. Berrangé Message-Id: <20210916111707.84999-1-armbru@redhat.com> Signed-off-by: Paolo Bonzini --- softmmu/qdev-monitor.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 0705f00846..3df99ce9fc 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -836,16 +836,12 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp) static DeviceState *find_device_state(const char *id, Error **errp) { Object *obj; + DeviceState *dev; if (id[0] == '/') { obj = object_resolve_path(id, NULL); } else { - char *root_path = object_get_canonical_path(qdev_get_peripheral()); - char *path = g_strdup_printf("%s/%s", root_path, id); - - g_free(root_path); - obj = object_resolve_path_type(path, TYPE_DEVICE, NULL); - g_free(path); + obj = object_resolve_path_component(qdev_get_peripheral(), id); } if (!obj) { @@ -854,12 +850,13 @@ static DeviceState *find_device_state(const char *id, Error **errp) return NULL; } - if (!object_dynamic_cast(obj, TYPE_DEVICE)) { + dev = (DeviceState *)object_dynamic_cast(obj, TYPE_DEVICE); + if (!dev) { error_setg(errp, "%s is not a hotpluggable device", id); return NULL; } - return DEVICE(obj); + return dev; } void qdev_unplug(DeviceState *dev, Error **errp) From eb8257a261325988873e047872975b5eda03efb4 Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Mon, 11 Oct 2021 17:30:25 +0000 Subject: [PATCH 0459/1334] target/i386/sev: Use local variable for kvm_sev_launch_start The struct kvm_sev_launch_start has a constant and small size, and therefore we can use a regular local variable for it instead of allocating and freeing heap memory for it. Signed-off-by: Dov Murik Reviewed-by: Dr. David Alan Gilbert Message-Id: <20211011173026.2454294-2-dovmurik@linux.ibm.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 4c64c68244..0062566c71 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -647,31 +647,29 @@ sev_launch_start(SevGuestState *sev) gsize sz; int ret = 1; int fw_error, rc; - struct kvm_sev_launch_start *start; + struct kvm_sev_launch_start start = { + .handle = sev->handle, .policy = sev->policy + }; guchar *session = NULL, *dh_cert = NULL; - start = g_new0(struct kvm_sev_launch_start, 1); - - start->handle = sev->handle; - start->policy = sev->policy; if (sev->session_file) { if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { goto out; } - start->session_uaddr = (unsigned long)session; - start->session_len = sz; + start.session_uaddr = (unsigned long)session; + start.session_len = sz; } if (sev->dh_cert_file) { if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { goto out; } - start->dh_uaddr = (unsigned long)dh_cert; - start->dh_len = sz; + start.dh_uaddr = (unsigned long)dh_cert; + start.dh_len = sz; } - trace_kvm_sev_launch_start(start->policy, session, dh_cert); - rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); + trace_kvm_sev_launch_start(start.policy, session, dh_cert); + rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error); if (rc < 0) { error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); @@ -679,11 +677,10 @@ sev_launch_start(SevGuestState *sev) } sev_set_guest_state(sev, SEV_STATE_LAUNCH_UPDATE); - sev->handle = start->handle; + sev->handle = start.handle; ret = 0; out: - g_free(start); g_free(session); g_free(dh_cert); return ret; From 59e42d88b6851b238927cbe48505b1cef547ce3b Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Mon, 11 Oct 2021 17:30:26 +0000 Subject: [PATCH 0460/1334] target/i386/sev: Use local variable for kvm_sev_launch_measure The struct kvm_sev_launch_measure has a constant and small size, and therefore we can use a regular local variable for it instead of allocating and freeing heap memory for it. Signed-off-by: Dov Murik Reviewed-by: Dr. David Alan Gilbert Message-Id: <20211011173026.2454294-3-dovmurik@linux.ibm.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 0062566c71..eede07f11d 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -729,7 +729,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) SevGuestState *sev = sev_guest; int ret, error; g_autofree guchar *data = NULL; - g_autofree struct kvm_sev_launch_measure *measurement = NULL; + struct kvm_sev_launch_measure measurement = {}; if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) { return; @@ -743,23 +743,21 @@ sev_launch_get_measure(Notifier *notifier, void *unused) } } - measurement = g_new0(struct kvm_sev_launch_measure, 1); - /* query the measurement blob length */ ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE, - measurement, &error); - if (!measurement->len) { + &measurement, &error); + if (!measurement.len) { error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", __func__, ret, error, fw_error_to_str(errno)); return; } - data = g_new0(guchar, measurement->len); - measurement->uaddr = (unsigned long)data; + data = g_new0(guchar, measurement.len); + measurement.uaddr = (unsigned long)data; /* get the measurement blob */ ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE, - measurement, &error); + &measurement, &error); if (ret) { error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", __func__, ret, error, fw_error_to_str(errno)); @@ -769,7 +767,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET); /* encode the measurement value and emit the event */ - sev->measurement = g_base64_encode(data, measurement->len); + sev->measurement = g_base64_encode(data, measurement.len); trace_kvm_sev_launch_measurement(sev->measurement); } From eeecc2ede44b2a5f2551dfcebd561a3945d4c132 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 Sep 2021 12:45:12 +0200 Subject: [PATCH 0461/1334] ebpf: really include it only in system emulators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eBPF libraries are being included in user emulators, which is useless and also breaks --static compilation if a shared library for libbpf is present in the system. Reported-by: Alex Bennée Signed-off-by: Paolo Bonzini --- meson.build | 2 -- 1 file changed, 2 deletions(-) diff --git a/meson.build b/meson.build index 2d373a61a6..c1314baace 100644 --- a/meson.build +++ b/meson.build @@ -2300,8 +2300,6 @@ subdir('bsd-user') subdir('linux-user') subdir('ebpf') -common_ss.add(libbpf) - bsd_user_ss.add(files('gdbstub.c')) specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss) From f1420101023c7d170d802efc8daab686189ed1bf Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Wed, 6 Oct 2021 11:26:30 +0200 Subject: [PATCH 0462/1334] s390x/ipl: check kernel command line size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check if the provided kernel command line exceeds the maximum size of the s390x Linux kernel command line size, which is 896 bytes. Reported-by: Sven Schnelle Signed-off-by: Marc Hartmayer Message-Id: <20211006092631.20732-1-mhartmay@linux.ibm.com> Reviewed-by: Christian Borntraeger Reviewed-by: Philippe Mathieu-Daudé [thuth: Adjusted format specifier for size_t] Signed-off-by: Thomas Huth --- hw/s390x/ipl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 1821c6faee..7ddca0127f 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -38,6 +38,7 @@ #define KERN_IMAGE_START 0x010000UL #define LINUX_MAGIC_ADDR 0x010008UL #define KERN_PARM_AREA 0x010480UL +#define KERN_PARM_AREA_SIZE 0x000380UL #define INITRD_START 0x800000UL #define INITRD_PARM_START 0x010408UL #define PARMFILE_START 0x001000UL @@ -190,10 +191,19 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * loader) and it won't work. For this case we force it to 0x10000, too. */ if (pentry == KERN_IMAGE_START || pentry == 0x800) { - char *parm_area = rom_ptr(KERN_PARM_AREA, strlen(ipl->cmdline) + 1); + size_t cmdline_size = strlen(ipl->cmdline) + 1; + char *parm_area = rom_ptr(KERN_PARM_AREA, cmdline_size); + ipl->start_addr = KERN_IMAGE_START; /* Overwrite parameters in the kernel image, which are "rom" */ if (parm_area) { + if (cmdline_size > KERN_PARM_AREA_SIZE) { + error_setg(errp, + "kernel command line exceeds maximum size: %zu > %lu", + cmdline_size, KERN_PARM_AREA_SIZE); + return; + } + strcpy(parm_area, ipl->cmdline); } } else { From 998eb7448c75b432a13257d019f6866e604ce43c Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Fri, 8 Oct 2021 22:38:10 +0200 Subject: [PATCH 0463/1334] s390x: sigp: Force Set Architecture to return Invalid Parameter According to the Principles of Operation, the SIGP Set Architecture order will return Incorrect State if some CPUs are not stopped, but only if the CZAM facility is not present. If it is, the order will return Invalid Parameter because the architecture mode cannot be changed. Since CZAM always exists when S390_FEAT_ZARCH exists, which in turn exists for every defined CPU model, we can simplify this code. Fixes: 075e52b81664 ("s390x/cpumodel: we are always in zarchitecture mode") Signed-off-by: Eric Farman Reviewed-by: Christian Borntraeger Reviewed-by: Janosch Frank Message-Id: <20211008203811.1980478-2-farman@linux.ibm.com> Reviewed-by: Thomas Huth Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- target/s390x/sigp.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index d57427ced8..51c727834c 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -428,26 +428,10 @@ static int handle_sigp_single_dst(S390CPU *cpu, S390CPU *dst_cpu, uint8_t order, static int sigp_set_architecture(S390CPU *cpu, uint32_t param, uint64_t *status_reg) { - CPUState *cur_cs; - S390CPU *cur_cpu; - bool all_stopped = true; - - CPU_FOREACH(cur_cs) { - cur_cpu = S390_CPU(cur_cs); - - if (cur_cpu == cpu) { - continue; - } - if (s390_cpu_get_state(cur_cpu) != S390_CPU_STATE_STOPPED) { - all_stopped = false; - } - } - *status_reg &= 0xffffffff00000000ULL; /* Reject set arch order, with czam we're always in z/Arch mode. */ - *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER : - SIGP_STAT_INCORRECT_STATE); + *status_reg |= SIGP_STAT_INVALID_PARAMETER; return SIGP_CC_STATUS_STORED; } From ebbc6034dcfe772473d1d29724b36ce7a9a49ad2 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 12 Oct 2021 16:40:38 +0200 Subject: [PATCH 0464/1334] vfio-ccw: step down as maintainer I currently don't have time to act as vfio-ccw maintainer anymore, so remove myself there. Signed-off-by: Cornelia Huck Message-Id: <20211012144040.360887-2-cohuck@redhat.com> Acked-by: Matthew Rosato Acked-by: Eric Farman Signed-off-by: Thomas Huth --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 50435b8d2f..14d1312941 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1862,7 +1862,6 @@ F: docs/igd-assign.txt F: docs/devel/vfio-migration.rst vfio-ccw -M: Cornelia Huck M: Eric Farman M: Matthew Rosato S: Supported @@ -1870,7 +1869,6 @@ F: hw/vfio/ccw.c F: hw/s390x/s390-ccw.c F: include/hw/s390x/s390-ccw.h F: include/hw/s390x/vfio-ccw.h -T: git https://gitlab.com/cohuck/qemu.git s390-next L: qemu-s390x@nongnu.org vfio-ap From 373b2ab35635537c67bb6fe9aa36367f28a067a2 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 12 Oct 2021 16:40:39 +0200 Subject: [PATCH 0465/1334] s390x/kvm: step down as maintainer I'm no longer involved with KVM/s390 on the kernel side, and I don't have enough resources to work on the s390 KVM cpus support, so I'll step down. Signed-off-by: Cornelia Huck Acked-by: Halil Pasic Message-Id: <20211012144040.360887-3-cohuck@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 14d1312941..234fcaa233 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -393,7 +393,6 @@ F: target/ppc/kvm.c S390 KVM CPUs M: Halil Pasic -M: Cornelia Huck M: Christian Borntraeger S: Supported F: target/s390x/kvm/ @@ -408,7 +407,6 @@ F: hw/intc/s390_flic.c F: hw/intc/s390_flic_kvm.c F: include/hw/s390x/s390_flic.h F: gdb-xml/s390*.xml -T: git https://gitlab.com/cohuck/qemu.git s390-next T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org From afe5c8c5c14aff25fab6ae83957e87c717415be3 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 12 Oct 2021 16:40:40 +0200 Subject: [PATCH 0466/1334] s390x virtio-ccw machine: step down as maintainer I currently don't have time to work on the s390x virtio-ccw machine anymore, so let's step down. (I will, however, continue as a maintainer for the virtio-ccw *transport*.) Signed-off-by: Cornelia Huck Acked-by: Halil Pasic Message-Id: <20211012144040.360887-4-cohuck@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 234fcaa233..c25793bc39 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1509,7 +1509,6 @@ F: tests/acceptance/machine_sparc_leon3.py S390 Machines ------------- S390 Virtio-ccw -M: Cornelia Huck M: Halil Pasic M: Christian Borntraeger S: Supported @@ -1521,7 +1520,6 @@ F: hw/watchdog/wdt_diag288.c F: include/hw/watchdog/wdt_diag288.h F: configs/devices/s390x-softmmu/default.mak F: tests/acceptance/machine_s390_ccw_virtio.py -T: git https://gitlab.com/cohuck/qemu.git s390-next T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org From 7a7142f0254464d16f0ddefd61237b40bfa97a47 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 11 Oct 2021 19:32:43 +0200 Subject: [PATCH 0467/1334] memory: Log access direction for invalid accesses In memory_region_access_valid() invalid accesses are logged to help debugging but the log message does not say if it was a read or write. Log that too to better identify the access causing the problem. Reviewed-by: David Hildenbrand Signed-off-by: BALATON Zoltan Message-Id: <20211011173616.F1DE0756022@zero.eik.bme.hu> Signed-off-by: Richard Henderson --- softmmu/memory.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/softmmu/memory.c b/softmmu/memory.c index db182e5d3d..e5826faa0c 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1378,17 +1378,17 @@ bool memory_region_access_valid(MemoryRegion *mr, { if (mr->ops->valid.accepts && !mr->ops->valid.accepts(mr->opaque, addr, size, is_write, attrs)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access at addr " - "0x%" HWADDR_PRIX ", size %u, " - "region '%s', reason: rejected\n", + qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + ", size %u, region '%s', reason: rejected\n", + is_write ? "write" : "read", addr, size, memory_region_name(mr)); return false; } if (!mr->ops->valid.unaligned && (addr & (size - 1))) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access at addr " - "0x%" HWADDR_PRIX ", size %u, " - "region '%s', reason: unaligned\n", + qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + ", size %u, region '%s', reason: unaligned\n", + is_write ? "write" : "read", addr, size, memory_region_name(mr)); return false; } @@ -1400,10 +1400,10 @@ bool memory_region_access_valid(MemoryRegion *mr, if (size > mr->ops->valid.max_access_size || size < mr->ops->valid.min_access_size) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access at addr " - "0x%" HWADDR_PRIX ", size %u, " - "region '%s', reason: invalid size " - "(min:%u max:%u)\n", + qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + ", size %u, region '%s', reason: invalid size " + "(min:%u max:%u)\n", + is_write ? "write" : "read", addr, size, memory_region_name(mr), mr->ops->valid.min_access_size, mr->ops->valid.max_access_size); From c21751f394591f2444c62bfcb1124300849ab46a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 26 Jul 2021 13:38:20 -1000 Subject: [PATCH 0468/1334] target/arm: Use MO_128 for 16 byte atomics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: qemu-arm@nongnu.org Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/helper-a64.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index c5af779006..4cafd3c11a 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -560,7 +560,7 @@ uint64_t HELPER(paired_cmpxchg64_le_parallel)(CPUARMState *env, uint64_t addr, assert(HAVE_CMPXCHG128); mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + oi = make_memop_idx(MO_LE | MO_128 | MO_ALIGN, mem_idx); cmpv = int128_make128(env->exclusive_val, env->exclusive_high); newv = int128_make128(new_lo, new_hi); @@ -630,7 +630,7 @@ uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr, assert(HAVE_CMPXCHG128); mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); + oi = make_memop_idx(MO_BE | MO_128 | MO_ALIGN, mem_idx); /* * High and low need to be switched here because this is not actually a @@ -656,7 +656,7 @@ void HELPER(casp_le_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, assert(HAVE_CMPXCHG128); mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + oi = make_memop_idx(MO_LE | MO_128 | MO_ALIGN, mem_idx); cmpv = int128_make128(env->xregs[rs], env->xregs[rs + 1]); newv = int128_make128(new_lo, new_hi); @@ -677,7 +677,7 @@ void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, assert(HAVE_CMPXCHG128); mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + oi = make_memop_idx(MO_LE | MO_128 | MO_ALIGN, mem_idx); cmpv = int128_make128(env->xregs[rs + 1], env->xregs[rs]); newv = int128_make128(new_lo, new_hi); From 26b14640d933d703c175a314479bdd0b8c26fedd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 26 Jul 2021 13:38:42 -1000 Subject: [PATCH 0469/1334] target/i386: Use MO_128 for 16 byte atomics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/tcg/mem_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/mem_helper.c b/target/i386/tcg/mem_helper.c index 0fd696f9c1..a207e624cb 100644 --- a/target/i386/tcg/mem_helper.c +++ b/target/i386/tcg/mem_helper.c @@ -136,7 +136,7 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) Int128 newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); int mem_idx = cpu_mmu_index(env, false); - MemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + MemOpIdx oi = make_memop_idx(MO_TE | MO_128 | MO_ALIGN, mem_idx); Int128 oldv = cpu_atomic_cmpxchgo_le_mmu(env, a0, cmpv, newv, oi, ra); if (int128_eq(oldv, cmpv)) { From 68e33d869d2be9a765644782a0834fd87eec98f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 26 Jul 2021 13:38:54 -1000 Subject: [PATCH 0470/1334] target/ppc: Use MO_128 for 16 byte atomics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: qemu-ppc@nongnu.org Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/ppc/translate.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index b985e9e55b..9ca78ee156 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3462,10 +3462,12 @@ static void gen_std(DisasContext *ctx) if (HAVE_ATOMIC128) { TCGv_i32 oi = tcg_temp_new_i32(); if (ctx->le_mode) { - tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); + tcg_gen_movi_i32(oi, make_memop_idx(MO_LE | MO_128, + ctx->mem_idx)); gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi); } else { - tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); + tcg_gen_movi_i32(oi, make_memop_idx(MO_BE | MO_128, + ctx->mem_idx)); gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi); } tcg_temp_free_i32(oi); @@ -4067,11 +4069,11 @@ static void gen_lqarx(DisasContext *ctx) if (HAVE_ATOMIC128) { TCGv_i32 oi = tcg_temp_new_i32(); if (ctx->le_mode) { - tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ | MO_ALIGN_16, + tcg_gen_movi_i32(oi, make_memop_idx(MO_LE | MO_128 | MO_ALIGN, ctx->mem_idx)); gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); } else { - tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ | MO_ALIGN_16, + tcg_gen_movi_i32(oi, make_memop_idx(MO_BE | MO_128 | MO_ALIGN, ctx->mem_idx)); gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); } @@ -4122,7 +4124,7 @@ static void gen_stqcx_(DisasContext *ctx) if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { if (HAVE_CMPXCHG128) { - TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16); + TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_128) | MO_ALIGN); if (ctx->le_mode) { gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi); From 35c65de02998d23a26964c61b8c2e3b4235dcceb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 26 Jul 2021 13:39:07 -1000 Subject: [PATCH 0471/1334] target/s390x: Use MO_128 for 16 byte atomics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/tcg/mem_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 75f6735545..e64d1bc725 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -1811,7 +1811,7 @@ void HELPER(cdsg_parallel)(CPUS390XState *env, uint64_t addr, assert(HAVE_CMPXCHG128); mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + oi = make_memop_idx(MO_TE | MO_128 | MO_ALIGN, mem_idx); oldv = cpu_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); fail = !int128_eq(oldv, cmpv); @@ -1940,7 +1940,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra); cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra); } else if (HAVE_CMPXCHG128) { - MemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + MemOpIdx oi = make_memop_idx(MO_TE | MO_128 | MO_ALIGN, mem_idx); ov = cpu_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra); cc = !int128_eq(ov, cv); } else { From f79e80899dbcd306ae2e8a43bc5e139b051f30f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 06:50:38 -1000 Subject: [PATCH 0472/1334] target/hexagon: Implement cpu_mmu_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function is trivial for user-only, but still must be present. Reviewed-by: Taylor Simpson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hexagon/cpu.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index f7d043865b..f90c187888 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -141,6 +141,15 @@ static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, target_ulong *pc, #endif } +static inline int cpu_mmu_index(CPUHexagonState *env, bool ifetch) +{ +#ifdef CONFIG_USER_ONLY + return MMU_USER_IDX; +#else +#error System mode not supported on Hexagon yet +#endif +} + typedef struct CPUHexagonState CPUArchState; typedef HexagonCPU ArchCPU; From f83bcecb1ffe25a18367409eaf4ba1453c835c48 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 07:48:55 -1000 Subject: [PATCH 0473/1334] accel/tcg: Add cpu_{ld,st}*_mmu interfaces These functions are much closer to the softmmu helper functions, in that they take the complete MemOpIdx, and from that they may enforce required alignment. The previous cpu_ldst.h functions did not have alignment info, and so did not enforce it. Retain this by adding MO_UNALN to the MemOp that we create in calling the new functions. Note that we are not yet enforcing alignment for user-only, but we now have the information with which to do so. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 392 ++++++++++++------------------------ accel/tcg/ldst_common.c.inc | 307 ++++++++++++++++++++++++++++ accel/tcg/user-exec.c | 385 +++++++++++++++-------------------- docs/devel/loads-stores.rst | 52 ++++- include/exec/cpu_ldst.h | 245 ++++++++-------------- 5 files changed, 717 insertions(+), 664 deletions(-) create mode 100644 accel/tcg/ldst_common.c.inc diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 46140ccff3..b350cafa3d 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1839,6 +1839,25 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, cpu_loop_exit_atomic(env_cpu(env), retaddr); } +/* + * Verify that we have passed the correct MemOp to the correct function. + * + * In the case of the helper_*_mmu functions, we will have done this by + * using the MemOp to look up the helper during code generation. + * + * In the case of the cpu_*_mmu functions, this is up to the caller. + * We could present one function to target code, and dispatch based on + * the MemOp, but so far we have worked hard to avoid an indirect function + * call along the memory path. + */ +static void validate_memop(MemOpIdx oi, MemOp expected) +{ +#ifdef CONFIG_DEBUG_TCG + MemOp have = get_memop(oi) & (MO_SIZE | MO_BSWAP); + assert(have == expected); +#endif +} + /* * Load Helpers * @@ -1992,6 +2011,7 @@ load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi, static uint64_t full_ldub_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_UB); return load_helper(env, addr, oi, retaddr, MO_UB, false, full_ldub_mmu); } @@ -2004,6 +2024,7 @@ tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr, static uint64_t full_le_lduw_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_LEUW); return load_helper(env, addr, oi, retaddr, MO_LEUW, false, full_le_lduw_mmu); } @@ -2017,6 +2038,7 @@ tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr, static uint64_t full_be_lduw_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_BEUW); return load_helper(env, addr, oi, retaddr, MO_BEUW, false, full_be_lduw_mmu); } @@ -2030,6 +2052,7 @@ tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr, static uint64_t full_le_ldul_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_LEUL); return load_helper(env, addr, oi, retaddr, MO_LEUL, false, full_le_ldul_mmu); } @@ -2043,6 +2066,7 @@ tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr, static uint64_t full_be_ldul_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_BEUL); return load_helper(env, addr, oi, retaddr, MO_BEUL, false, full_be_ldul_mmu); } @@ -2056,6 +2080,7 @@ tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr, uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_LEQ); return load_helper(env, addr, oi, retaddr, MO_LEQ, false, helper_le_ldq_mmu); } @@ -2063,6 +2088,7 @@ uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr, uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_BEQ); return load_helper(env, addr, oi, retaddr, MO_BEQ, false, helper_be_ldq_mmu); } @@ -2108,186 +2134,56 @@ tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr, */ static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t retaddr, - MemOp op, FullLoadHelper *full_load) + MemOpIdx oi, uintptr_t retaddr, + FullLoadHelper *full_load) { - MemOpIdx oi = make_memop_idx(op, mmu_idx); uint64_t ret; trace_guest_ld_before_exec(env_cpu(env), addr, oi); - ret = full_load(env, addr, oi, retaddr); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); - return ret; } -uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) +uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) { - return cpu_load_helper(env, addr, mmu_idx, ra, MO_UB, full_ldub_mmu); + return cpu_load_helper(env, addr, oi, ra, full_ldub_mmu); } -int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) +uint16_t cpu_ldw_be_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra); + return cpu_load_helper(env, addr, oi, ra, full_be_lduw_mmu); } -uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) +uint32_t cpu_ldl_be_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_load_helper(env, addr, mmu_idx, ra, MO_BEUW, full_be_lduw_mmu); + return cpu_load_helper(env, addr, oi, ra, full_be_ldul_mmu); } -int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) +uint64_t cpu_ldq_be_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra); + return cpu_load_helper(env, addr, oi, MO_BEQ, helper_be_ldq_mmu); } -uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) +uint16_t cpu_ldw_le_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_load_helper(env, addr, mmu_idx, ra, MO_BEUL, full_be_ldul_mmu); + return cpu_load_helper(env, addr, oi, ra, full_le_lduw_mmu); } -uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) +uint32_t cpu_ldl_le_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_load_helper(env, addr, mmu_idx, ra, MO_BEQ, helper_be_ldq_mmu); + return cpu_load_helper(env, addr, oi, ra, full_le_ldul_mmu); } -uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) +uint64_t cpu_ldq_le_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_load_helper(env, addr, mmu_idx, ra, MO_LEUW, full_le_lduw_mmu); -} - -int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra); -} - -uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_load_helper(env, addr, mmu_idx, ra, MO_LEUL, full_le_ldul_mmu); -} - -uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_load_helper(env, addr, mmu_idx, ra, MO_LEQ, helper_le_ldq_mmu); -} - -uint32_t cpu_ldub_data_ra(CPUArchState *env, target_ulong ptr, - uintptr_t retaddr) -{ - return cpu_ldub_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -int cpu_ldsb_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) -{ - return cpu_ldsb_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -uint32_t cpu_lduw_be_data_ra(CPUArchState *env, target_ulong ptr, - uintptr_t retaddr) -{ - return cpu_lduw_be_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -int cpu_ldsw_be_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) -{ - return cpu_ldsw_be_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -uint32_t cpu_ldl_be_data_ra(CPUArchState *env, target_ulong ptr, - uintptr_t retaddr) -{ - return cpu_ldl_be_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -uint64_t cpu_ldq_be_data_ra(CPUArchState *env, target_ulong ptr, - uintptr_t retaddr) -{ - return cpu_ldq_be_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -uint32_t cpu_lduw_le_data_ra(CPUArchState *env, target_ulong ptr, - uintptr_t retaddr) -{ - return cpu_lduw_le_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -int cpu_ldsw_le_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) -{ - return cpu_ldsw_le_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -uint32_t cpu_ldl_le_data_ra(CPUArchState *env, target_ulong ptr, - uintptr_t retaddr) -{ - return cpu_ldl_le_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -uint64_t cpu_ldq_le_data_ra(CPUArchState *env, target_ulong ptr, - uintptr_t retaddr) -{ - return cpu_ldq_le_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); -} - -uint32_t cpu_ldub_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_ldub_data_ra(env, ptr, 0); -} - -int cpu_ldsb_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_ldsb_data_ra(env, ptr, 0); -} - -uint32_t cpu_lduw_be_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_lduw_be_data_ra(env, ptr, 0); -} - -int cpu_ldsw_be_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_ldsw_be_data_ra(env, ptr, 0); -} - -uint32_t cpu_ldl_be_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_ldl_be_data_ra(env, ptr, 0); -} - -uint64_t cpu_ldq_be_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_ldq_be_data_ra(env, ptr, 0); -} - -uint32_t cpu_lduw_le_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_lduw_le_data_ra(env, ptr, 0); -} - -int cpu_ldsw_le_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_ldsw_le_data_ra(env, ptr, 0); -} - -uint32_t cpu_ldl_le_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_ldl_le_data_ra(env, ptr, 0); -} - -uint64_t cpu_ldq_le_data(CPUArchState *env, target_ulong ptr) -{ - return cpu_ldq_le_data_ra(env, ptr, 0); + return cpu_load_helper(env, addr, oi, ra, helper_le_ldq_mmu); } /* @@ -2324,6 +2220,9 @@ store_memop(void *haddr, uint64_t val, MemOp op) } } +static void full_stb_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr); + static void __attribute__((noinline)) store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val, uintptr_t retaddr, size_t size, uintptr_t mmu_idx, @@ -2387,13 +2286,13 @@ store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val, for (i = 0; i < size; ++i) { /* Big-endian extract. */ uint8_t val8 = val >> (((size - 1) * 8) - (i * 8)); - helper_ret_stb_mmu(env, addr + i, val8, oi, retaddr); + full_stb_mmu(env, addr + i, val8, oi, retaddr); } } else { for (i = 0; i < size; ++i) { /* Little-endian extract. */ uint8_t val8 = val >> (i * 8); - helper_ret_stb_mmu(env, addr + i, val8, oi, retaddr); + full_stb_mmu(env, addr + i, val8, oi, retaddr); } } } @@ -2496,46 +2395,83 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, store_memop(haddr, val, op); } -void __attribute__((noinline)) -helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, - MemOpIdx oi, uintptr_t retaddr) +static void __attribute__((noinline)) +full_stb_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_UB); store_helper(env, addr, val, oi, retaddr, MO_UB); } +void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + full_stb_mmu(env, addr, val, oi, retaddr); +} + +static void full_le_stw_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + validate_memop(oi, MO_LEUW); + store_helper(env, addr, val, oi, retaddr, MO_LEUW); +} + void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, MemOpIdx oi, uintptr_t retaddr) { - store_helper(env, addr, val, oi, retaddr, MO_LEUW); + full_le_stw_mmu(env, addr, val, oi, retaddr); +} + +static void full_be_stw_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + validate_memop(oi, MO_BEUW); + store_helper(env, addr, val, oi, retaddr, MO_BEUW); } void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, MemOpIdx oi, uintptr_t retaddr) { - store_helper(env, addr, val, oi, retaddr, MO_BEUW); + full_be_stw_mmu(env, addr, val, oi, retaddr); +} + +static void full_le_stl_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + validate_memop(oi, MO_LEUL); + store_helper(env, addr, val, oi, retaddr, MO_LEUL); } void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, MemOpIdx oi, uintptr_t retaddr) { - store_helper(env, addr, val, oi, retaddr, MO_LEUL); + full_le_stl_mmu(env, addr, val, oi, retaddr); +} + +static void full_be_stl_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + validate_memop(oi, MO_BEUL); + store_helper(env, addr, val, oi, retaddr, MO_BEUL); } void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, MemOpIdx oi, uintptr_t retaddr) { - store_helper(env, addr, val, oi, retaddr, MO_BEUL); + full_be_stl_mmu(env, addr, val, oi, retaddr); } void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_LEQ); store_helper(env, addr, val, oi, retaddr, MO_LEQ); } void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, MemOpIdx oi, uintptr_t retaddr) { + validate_memop(oi, MO_BEQ); store_helper(env, addr, val, oi, retaddr, MO_BEQ); } @@ -2543,137 +2479,61 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, * Store Helpers for cpu_ldst.h */ -static inline void QEMU_ALWAYS_INLINE -cpu_store_helper(CPUArchState *env, target_ulong addr, uint64_t val, - int mmu_idx, uintptr_t retaddr, MemOp op) +typedef void FullStoreHelper(CPUArchState *env, target_ulong addr, + uint64_t val, MemOpIdx oi, uintptr_t retaddr); + +static inline void cpu_store_helper(CPUArchState *env, target_ulong addr, + uint64_t val, MemOpIdx oi, uintptr_t ra, + FullStoreHelper *full_store) { - MemOpIdx oi = make_memop_idx(op, mmu_idx); - trace_guest_st_before_exec(env_cpu(env), addr, oi); - - store_helper(env, addr, val, oi, retaddr, op); - + full_store(env, addr, val, oi, ra); qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -void cpu_stb_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, - int mmu_idx, uintptr_t retaddr) +void cpu_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_UB); + cpu_store_helper(env, addr, val, oi, retaddr, full_stb_mmu); } -void cpu_stw_be_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, - int mmu_idx, uintptr_t retaddr) +void cpu_stw_be_mmu(CPUArchState *env, target_ulong addr, uint16_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_BEUW); + cpu_store_helper(env, addr, val, oi, retaddr, full_be_stw_mmu); } -void cpu_stl_be_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, - int mmu_idx, uintptr_t retaddr) +void cpu_stl_be_mmu(CPUArchState *env, target_ulong addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_BEUL); + cpu_store_helper(env, addr, val, oi, retaddr, full_be_stl_mmu); } -void cpu_stq_be_mmuidx_ra(CPUArchState *env, target_ulong addr, uint64_t val, - int mmu_idx, uintptr_t retaddr) +void cpu_stq_be_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_BEQ); + cpu_store_helper(env, addr, val, oi, retaddr, helper_be_stq_mmu); } -void cpu_stw_le_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, - int mmu_idx, uintptr_t retaddr) +void cpu_stw_le_mmu(CPUArchState *env, target_ulong addr, uint16_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_LEUW); + cpu_store_helper(env, addr, val, oi, retaddr, full_le_stw_mmu); } -void cpu_stl_le_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, - int mmu_idx, uintptr_t retaddr) +void cpu_stl_le_mmu(CPUArchState *env, target_ulong addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_LEUL); + cpu_store_helper(env, addr, val, oi, retaddr, full_le_stl_mmu); } -void cpu_stq_le_mmuidx_ra(CPUArchState *env, target_ulong addr, uint64_t val, - int mmu_idx, uintptr_t retaddr) +void cpu_stq_le_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_LEQ); + cpu_store_helper(env, addr, val, oi, retaddr, helper_le_stq_mmu); } -void cpu_stb_data_ra(CPUArchState *env, target_ulong ptr, - uint32_t val, uintptr_t retaddr) -{ - cpu_stb_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); -} - -void cpu_stw_be_data_ra(CPUArchState *env, target_ulong ptr, - uint32_t val, uintptr_t retaddr) -{ - cpu_stw_be_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); -} - -void cpu_stl_be_data_ra(CPUArchState *env, target_ulong ptr, - uint32_t val, uintptr_t retaddr) -{ - cpu_stl_be_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); -} - -void cpu_stq_be_data_ra(CPUArchState *env, target_ulong ptr, - uint64_t val, uintptr_t retaddr) -{ - cpu_stq_be_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); -} - -void cpu_stw_le_data_ra(CPUArchState *env, target_ulong ptr, - uint32_t val, uintptr_t retaddr) -{ - cpu_stw_le_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); -} - -void cpu_stl_le_data_ra(CPUArchState *env, target_ulong ptr, - uint32_t val, uintptr_t retaddr) -{ - cpu_stl_le_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); -} - -void cpu_stq_le_data_ra(CPUArchState *env, target_ulong ptr, - uint64_t val, uintptr_t retaddr) -{ - cpu_stq_le_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); -} - -void cpu_stb_data(CPUArchState *env, target_ulong ptr, uint32_t val) -{ - cpu_stb_data_ra(env, ptr, val, 0); -} - -void cpu_stw_be_data(CPUArchState *env, target_ulong ptr, uint32_t val) -{ - cpu_stw_be_data_ra(env, ptr, val, 0); -} - -void cpu_stl_be_data(CPUArchState *env, target_ulong ptr, uint32_t val) -{ - cpu_stl_be_data_ra(env, ptr, val, 0); -} - -void cpu_stq_be_data(CPUArchState *env, target_ulong ptr, uint64_t val) -{ - cpu_stq_be_data_ra(env, ptr, val, 0); -} - -void cpu_stw_le_data(CPUArchState *env, target_ulong ptr, uint32_t val) -{ - cpu_stw_le_data_ra(env, ptr, val, 0); -} - -void cpu_stl_le_data(CPUArchState *env, target_ulong ptr, uint32_t val) -{ - cpu_stl_le_data_ra(env, ptr, val, 0); -} - -void cpu_stq_le_data(CPUArchState *env, target_ulong ptr, uint64_t val) -{ - cpu_stq_le_data_ra(env, ptr, val, 0); -} +#include "ldst_common.c.inc" /* * First set of functions passes in OI and RETADDR. diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc new file mode 100644 index 0000000000..bfefb275e7 --- /dev/null +++ b/accel/tcg/ldst_common.c.inc @@ -0,0 +1,307 @@ +/* + * Routines common to user and system emulation of load/store. + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + return cpu_ldb_mmu(env, addr, oi, ra); +} + +int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra); +} + +uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx); + return cpu_ldw_be_mmu(env, addr, oi, ra); +} + +int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra); +} + +uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx); + return cpu_ldl_be_mmu(env, addr, oi, ra); +} + +uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEQ | MO_UNALN, mmu_idx); + return cpu_ldq_be_mmu(env, addr, oi, ra); +} + +uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx); + return cpu_ldw_le_mmu(env, addr, oi, ra); +} + +int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra); +} + +uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); + return cpu_ldl_le_mmu(env, addr, oi, ra); +} + +uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEQ | MO_UNALN, mmu_idx); + return cpu_ldq_le_mmu(env, addr, oi, ra); +} + +void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + cpu_stb_mmu(env, addr, val, oi, ra); +} + +void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx); + cpu_stw_be_mmu(env, addr, val, oi, ra); +} + +void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx); + cpu_stl_be_mmu(env, addr, val, oi, ra); +} + +void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEQ | MO_UNALN, mmu_idx); + cpu_stq_be_mmu(env, addr, val, oi, ra); +} + +void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx); + cpu_stw_le_mmu(env, addr, val, oi, ra); +} + +void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); + cpu_stl_le_mmu(env, addr, val, oi, ra); +} + +void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEQ | MO_UNALN, mmu_idx); + cpu_stq_le_mmu(env, addr, val, oi, ra); +} + +/*--------------------------*/ + +uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return cpu_ldub_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); +} + +int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int8_t)cpu_ldub_data_ra(env, addr, ra); +} + +uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return cpu_lduw_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); +} + +int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int16_t)cpu_lduw_be_data_ra(env, addr, ra); +} + +uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return cpu_ldl_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); +} + +uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return cpu_ldq_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); +} + +uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return cpu_lduw_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); +} + +int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int16_t)cpu_lduw_le_data_ra(env, addr, ra); +} + +uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return cpu_ldl_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); +} + +uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return cpu_ldq_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); +} + +void cpu_stb_data_ra(CPUArchState *env, abi_ptr addr, + uint32_t val, uintptr_t ra) +{ + cpu_stb_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); +} + +void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr, + uint32_t val, uintptr_t ra) +{ + cpu_stw_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); +} + +void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr, + uint32_t val, uintptr_t ra) +{ + cpu_stl_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); +} + +void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr, + uint64_t val, uintptr_t ra) +{ + cpu_stq_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); +} + +void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr, + uint32_t val, uintptr_t ra) +{ + cpu_stw_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); +} + +void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr addr, + uint32_t val, uintptr_t ra) +{ + cpu_stl_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); +} + +void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr, + uint64_t val, uintptr_t ra) +{ + cpu_stq_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); +} + +/*--------------------------*/ + +uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldub_data_ra(env, addr, 0); +} + +int cpu_ldsb_data(CPUArchState *env, abi_ptr addr) +{ + return (int8_t)cpu_ldub_data(env, addr); +} + +uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_lduw_be_data_ra(env, addr, 0); +} + +int cpu_ldsw_be_data(CPUArchState *env, abi_ptr addr) +{ + return (int16_t)cpu_lduw_be_data(env, addr); +} + +uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldl_be_data_ra(env, addr, 0); +} + +uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldq_be_data_ra(env, addr, 0); +} + +uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_lduw_le_data_ra(env, addr, 0); +} + +int cpu_ldsw_le_data(CPUArchState *env, abi_ptr addr) +{ + return (int16_t)cpu_lduw_le_data(env, addr); +} + +uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldl_le_data_ra(env, addr, 0); +} + +uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldq_le_data_ra(env, addr, 0); +} + +void cpu_stb_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stb_data_ra(env, addr, val, 0); +} + +void cpu_stw_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stw_be_data_ra(env, addr, val, 0); +} + +void cpu_stl_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stl_be_data_ra(env, addr, val, 0); +} + +void cpu_stq_be_data(CPUArchState *env, abi_ptr addr, uint64_t val) +{ + cpu_stq_be_data_ra(env, addr, val, 0); +} + +void cpu_stw_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stw_le_data_ra(env, addr, val, 0); +} + +void cpu_stl_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stl_le_data_ra(env, addr, val, 0); +} + +void cpu_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val) +{ + cpu_stq_le_data_ra(env, addr, val, 0); +} diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 65d3c9b286..e6bb29b42d 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -886,300 +886,227 @@ int cpu_signal_handler(int host_signum, void *pinfo, /* The softmmu versions of these helpers are in cputlb.c. */ -uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr) +/* + * Verify that we have passed the correct MemOp to the correct function. + * + * We could present one function to target code, and dispatch based on + * the MemOp, but so far we have worked hard to avoid an indirect function + * call along the memory path. + */ +static void validate_memop(MemOpIdx oi, MemOp expected) { - MemOpIdx oi = make_memop_idx(MO_UB, MMU_USER_IDX); - uint32_t ret; +#ifdef CONFIG_DEBUG_TCG + MemOp have = get_memop(oi) & (MO_SIZE | MO_BSWAP); + assert(have == expected); +#endif +} - trace_guest_ld_before_exec(env_cpu(env), ptr, oi); - ret = ldub_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); +static void *cpu_mmu_lookup(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t ra, MMUAccessType type) +{ + void *ret; + + /* TODO: Enforce guest required alignment. */ + + ret = g2h(env_cpu(env), addr); + set_helper_retaddr(ra); return ret; } -int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr) +uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return (int8_t)cpu_ldub_data(env, ptr); -} + void *haddr; + uint8_t ret; -uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr) -{ - MemOpIdx oi = make_memop_idx(MO_BEUW, MMU_USER_IDX); - uint32_t ret; - - trace_guest_ld_before_exec(env_cpu(env), ptr, oi); - ret = lduw_be_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); + validate_memop(oi, MO_UB); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); + ret = ldub_p(haddr); + clear_helper_retaddr(); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); return ret; } -int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr) +uint16_t cpu_ldw_be_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return (int16_t)cpu_lduw_be_data(env, ptr); -} + void *haddr; + uint16_t ret; -uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr) -{ - MemOpIdx oi = make_memop_idx(MO_BEUL, MMU_USER_IDX); - uint32_t ret; - - trace_guest_ld_before_exec(env_cpu(env), ptr, oi); - ret = ldl_be_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); + validate_memop(oi, MO_BEUW); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); + ret = lduw_be_p(haddr); + clear_helper_retaddr(); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); return ret; } -uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr) +uint32_t cpu_ldl_be_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - MemOpIdx oi = make_memop_idx(MO_BEQ, MMU_USER_IDX); + void *haddr; + uint32_t ret; + + validate_memop(oi, MO_BEUL); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); + ret = ldl_be_p(haddr); + clear_helper_retaddr(); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + return ret; +} + +uint64_t cpu_ldq_be_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + void *haddr; uint64_t ret; - trace_guest_ld_before_exec(env_cpu(env), ptr, oi); - ret = ldq_be_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); + validate_memop(oi, MO_BEQ); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); + ret = ldq_be_p(haddr); + clear_helper_retaddr(); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); return ret; } -uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr) +uint16_t cpu_ldw_le_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - MemOpIdx oi = make_memop_idx(MO_LEUW, MMU_USER_IDX); + void *haddr; + uint16_t ret; + + validate_memop(oi, MO_LEUW); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); + ret = lduw_le_p(haddr); + clear_helper_retaddr(); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + return ret; +} + +uint32_t cpu_ldl_le_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + void *haddr; uint32_t ret; - trace_guest_ld_before_exec(env_cpu(env), ptr, oi); - ret = lduw_le_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); + validate_memop(oi, MO_LEUL); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); + ret = ldl_le_p(haddr); + clear_helper_retaddr(); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); return ret; } -int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr) +uint64_t cpu_ldq_le_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { - return (int16_t)cpu_lduw_le_data(env, ptr); -} - -uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr) -{ - MemOpIdx oi = make_memop_idx(MO_LEUL, MMU_USER_IDX); - uint32_t ret; - - trace_guest_ld_before_exec(env_cpu(env), ptr, oi); - ret = ldl_le_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); - return ret; -} - -uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr) -{ - MemOpIdx oi = make_memop_idx(MO_LEQ, MMU_USER_IDX); + void *haddr; uint64_t ret; - trace_guest_ld_before_exec(env_cpu(env), ptr, oi); - ret = ldq_le_p(g2h(env_cpu(env), ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_R); + validate_memop(oi, MO_LEQ); + trace_guest_ld_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); + ret = ldq_le_p(haddr); + clear_helper_retaddr(); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); return ret; } -uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, + MemOpIdx oi, uintptr_t ra) { - uint32_t ret; + void *haddr; - set_helper_retaddr(retaddr); - ret = cpu_ldub_data(env, ptr); + validate_memop(oi, MO_UB); + trace_guest_st_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); + stb_p(haddr, val); clear_helper_retaddr(); - return ret; + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +void cpu_stw_be_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, + MemOpIdx oi, uintptr_t ra) { - return (int8_t)cpu_ldub_data_ra(env, ptr, retaddr); -} + void *haddr; -uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) -{ - uint32_t ret; - - set_helper_retaddr(retaddr); - ret = cpu_lduw_be_data(env, ptr); + validate_memop(oi, MO_BEUW); + trace_guest_st_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); + stw_be_p(haddr, val); clear_helper_retaddr(); - return ret; + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +void cpu_stl_be_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, + MemOpIdx oi, uintptr_t ra) { - return (int16_t)cpu_lduw_be_data_ra(env, ptr, retaddr); -} + void *haddr; -uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) -{ - uint32_t ret; - - set_helper_retaddr(retaddr); - ret = cpu_ldl_be_data(env, ptr); + validate_memop(oi, MO_BEUL); + trace_guest_st_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); + stl_be_p(haddr, val); clear_helper_retaddr(); - return ret; + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +void cpu_stq_be_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, + MemOpIdx oi, uintptr_t ra) { - uint64_t ret; + void *haddr; - set_helper_retaddr(retaddr); - ret = cpu_ldq_be_data(env, ptr); + validate_memop(oi, MO_BEQ); + trace_guest_st_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); + stq_be_p(haddr, val); clear_helper_retaddr(); - return ret; + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +void cpu_stw_le_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, + MemOpIdx oi, uintptr_t ra) { - uint32_t ret; + void *haddr; - set_helper_retaddr(retaddr); - ret = cpu_lduw_le_data(env, ptr); + validate_memop(oi, MO_LEUW); + trace_guest_st_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); + stw_le_p(haddr, val); clear_helper_retaddr(); - return ret; + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +void cpu_stl_le_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, + MemOpIdx oi, uintptr_t ra) { - return (int16_t)cpu_lduw_le_data_ra(env, ptr, retaddr); -} + void *haddr; -uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) -{ - uint32_t ret; - - set_helper_retaddr(retaddr); - ret = cpu_ldl_le_data(env, ptr); + validate_memop(oi, MO_LEUL); + trace_guest_st_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); + stl_le_p(haddr, val); clear_helper_retaddr(); - return ret; + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +void cpu_stq_le_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, + MemOpIdx oi, uintptr_t ra) { - uint64_t ret; + void *haddr; - set_helper_retaddr(retaddr); - ret = cpu_ldq_le_data(env, ptr); - clear_helper_retaddr(); - return ret; -} - -void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val) -{ - MemOpIdx oi = make_memop_idx(MO_UB, MMU_USER_IDX); - - trace_guest_st_before_exec(env_cpu(env), ptr, oi); - stb_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) -{ - MemOpIdx oi = make_memop_idx(MO_BEUW, MMU_USER_IDX); - - trace_guest_st_before_exec(env_cpu(env), ptr, oi); - stw_be_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) -{ - MemOpIdx oi = make_memop_idx(MO_BEUL, MMU_USER_IDX); - - trace_guest_st_before_exec(env_cpu(env), ptr, oi); - stl_be_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val) -{ - MemOpIdx oi = make_memop_idx(MO_BEQ, MMU_USER_IDX); - - trace_guest_st_before_exec(env_cpu(env), ptr, oi); - stq_be_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) -{ - MemOpIdx oi = make_memop_idx(MO_LEUW, MMU_USER_IDX); - - trace_guest_st_before_exec(env_cpu(env), ptr, oi); - stw_le_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) -{ - MemOpIdx oi = make_memop_idx(MO_LEUL, MMU_USER_IDX); - - trace_guest_st_before_exec(env_cpu(env), ptr, oi); - stl_le_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val) -{ - MemOpIdx oi = make_memop_idx(MO_LEQ, MMU_USER_IDX); - - trace_guest_st_before_exec(env_cpu(env), ptr, oi); - stq_le_p(g2h(env_cpu(env), ptr), val); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t retaddr) -{ - set_helper_retaddr(retaddr); - cpu_stb_data(env, ptr, val); - clear_helper_retaddr(); -} - -void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t retaddr) -{ - set_helper_retaddr(retaddr); - cpu_stw_be_data(env, ptr, val); - clear_helper_retaddr(); -} - -void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t retaddr) -{ - set_helper_retaddr(retaddr); - cpu_stl_be_data(env, ptr, val); - clear_helper_retaddr(); -} - -void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint64_t val, uintptr_t retaddr) -{ - set_helper_retaddr(retaddr); - cpu_stq_be_data(env, ptr, val); - clear_helper_retaddr(); -} - -void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t retaddr) -{ - set_helper_retaddr(retaddr); - cpu_stw_le_data(env, ptr, val); - clear_helper_retaddr(); -} - -void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t retaddr) -{ - set_helper_retaddr(retaddr); - cpu_stl_le_data(env, ptr, val); - clear_helper_retaddr(); -} - -void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint64_t val, uintptr_t retaddr) -{ - set_helper_retaddr(retaddr); - cpu_stq_le_data(env, ptr, val); + validate_memop(oi, MO_LEQ); + trace_guest_st_before_exec(env_cpu(env), addr, oi); + haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); + stq_le_p(haddr, val); clear_helper_retaddr(); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr) @@ -1222,6 +1149,8 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) return ret; } +#include "ldst_common.c.inc" + /* * Do not allow unaligned operations to proceed. Return the host address. * diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index 568274baec..8f0035c821 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -68,15 +68,19 @@ Regexes for git grep - ``\`` - ``\`` -``cpu_{ld,st}*_mmuidx_ra`` -~~~~~~~~~~~~~~~~~~~~~~~~~~ +``cpu_{ld,st}*_mmu`` +~~~~~~~~~~~~~~~~~~~~ -These functions operate on a guest virtual address plus a context, -known as a "mmu index" or ``mmuidx``, which controls how that virtual -address is translated. The meaning of the indexes are target specific, -but specifying a particular index might be necessary if, for instance, -the helper requires an "always as non-privileged" access rather that -the default access for the current state of the guest CPU. +These functions operate on a guest virtual address, plus a context +known as a "mmu index" which controls how that virtual address is +translated, plus a ``MemOp`` which contains alignment requirements +among other things. The ``MemOp`` and mmu index are combined into +a single argument of type ``MemOpIdx``. + +The meaning of the indexes are target specific, but specifying a +particular index might be necessary if, for instance, the helper +requires a "always as non-privileged" access rather than the +default access for the current state of the guest CPU. These functions may cause a guest CPU exception to be taken (e.g. for an alignment fault or MMU fault) which will result in @@ -99,6 +103,35 @@ function, which is a return address into the generated code [#gpc]_. Function names follow the pattern: +load: ``cpu_ld{size}{end}_mmu(env, ptr, oi, retaddr)`` + +store: ``cpu_st{size}{end}_mmu(env, ptr, val, oi, retaddr)`` + +``size`` + - ``b`` : 8 bits + - ``w`` : 16 bits + - ``l`` : 32 bits + - ``q`` : 64 bits + +``end`` + - (empty) : for target endian, or 8 bit sizes + - ``_be`` : big endian + - ``_le`` : little endian + +Regexes for git grep: + - ``\`` + - ``\`` + + +``cpu_{ld,st}*_mmuidx_ra`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These functions work like the ``cpu_{ld,st}_mmu`` functions except +that the ``mmuidx`` parameter is not combined with a ``MemOp``, +and therefore there is no required alignment supplied or enforced. + +Function names follow the pattern: + load: ``cpu_ld{sign}{size}{end}_mmuidx_ra(env, ptr, mmuidx, retaddr)`` store: ``cpu_st{size}{end}_mmuidx_ra(env, ptr, val, mmuidx, retaddr)`` @@ -132,7 +165,8 @@ of the guest CPU, as determined by ``cpu_mmu_index(env, false)``. These are generally the preferred way to do accesses by guest virtual address from helper functions, unless the access should -be performed with a context other than the default. +be performed with a context other than the default, or alignment +should be enforced for the access. Function names follow the pattern: diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index ce6ce82618..a4dad0772f 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -28,10 +28,12 @@ * load: cpu_ld{sign}{size}{end}_{mmusuffix}(env, ptr) * cpu_ld{sign}{size}{end}_{mmusuffix}_ra(env, ptr, retaddr) * cpu_ld{sign}{size}{end}_mmuidx_ra(env, ptr, mmu_idx, retaddr) + * cpu_ld{sign}{size}{end}_mmu(env, ptr, oi, retaddr) * * store: cpu_st{size}{end}_{mmusuffix}(env, ptr, val) * cpu_st{size}{end}_{mmusuffix}_ra(env, ptr, val, retaddr) * cpu_st{size}{end}_mmuidx_ra(env, ptr, val, mmu_idx, retaddr) + * cpu_st{size}{end}_mmu(env, ptr, val, oi, retaddr) * * sign is: * (empty): for 32 and 64 bit sizes @@ -53,10 +55,15 @@ * The "mmuidx" suffix carries an extra mmu_idx argument that specifies * the index to use; the "data" and "code" suffixes take the index from * cpu_mmu_index(). + * + * The "mmu" suffix carries the full MemOpIdx, with both mmu_idx and the + * MemOp including alignment requirements. The alignment will be enforced. */ #ifndef CPU_LDST_H #define CPU_LDST_H +#include "exec/memopidx.h" + #if defined(CONFIG_USER_ONLY) /* sparc32plus has 64bit long but 32bit space address * this can make bad result with g2h() and h2g() @@ -118,12 +125,10 @@ typedef target_ulong abi_ptr; uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr); - uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr); int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr); uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr); uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr); - uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr); int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr); uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr); @@ -131,37 +136,31 @@ uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr); uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); - uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); - uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val); - void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val); void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val); void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val); - void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val); void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val); void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val); void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, uintptr_t ra); - void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, uintptr_t ra); void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, uintptr_t ra); void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, uintptr_t ra); - void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, uintptr_t ra); void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr ptr, @@ -169,6 +168,71 @@ void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr ptr, void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, uintptr_t ra); +uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); +uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, + int mmu_idx, uintptr_t ra); + +void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, + int mmu_idx, uintptr_t ra); +void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, + int mmu_idx, uintptr_t ra); +void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, + int mmu_idx, uintptr_t ra); +void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, + int mmu_idx, uintptr_t ra); +void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, + int mmu_idx, uintptr_t ra); +void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, + int mmu_idx, uintptr_t ra); +void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, + int mmu_idx, uintptr_t ra); + +uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_be_mmu(CPUArchState *env, abi_ptr ptr, + MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_be_mmu(CPUArchState *env, abi_ptr ptr, + MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_be_mmu(CPUArchState *env, abi_ptr ptr, + MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_le_mmu(CPUArchState *env, abi_ptr ptr, + MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_le_mmu(CPUArchState *env, abi_ptr ptr, + MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_le_mmu(CPUArchState *env, abi_ptr ptr, + MemOpIdx oi, uintptr_t ra); + +void cpu_stb_mmu(CPUArchState *env, abi_ptr ptr, uint8_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stw_be_mmu(CPUArchState *env, abi_ptr ptr, uint16_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stl_be_mmu(CPUArchState *env, abi_ptr ptr, uint32_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stq_be_mmu(CPUArchState *env, abi_ptr ptr, uint64_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stw_le_mmu(CPUArchState *env, abi_ptr ptr, uint16_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stl_le_mmu(CPUArchState *env, abi_ptr ptr, uint32_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stq_le_mmu(CPUArchState *env, abi_ptr ptr, uint64_t val, + MemOpIdx oi, uintptr_t ra); + #if defined(CONFIG_USER_ONLY) extern __thread uintptr_t helper_retaddr; @@ -193,119 +257,6 @@ static inline void clear_helper_retaddr(void) helper_retaddr = 0; } -/* - * Provide the same *_mmuidx_ra interface as for softmmu. - * The mmu_idx argument is ignored. - */ - -static inline uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_ldub_data_ra(env, addr, ra); -} - -static inline int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_ldsb_data_ra(env, addr, ra); -} - -static inline uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_lduw_be_data_ra(env, addr, ra); -} - -static inline int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_ldsw_be_data_ra(env, addr, ra); -} - -static inline uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_ldl_be_data_ra(env, addr, ra); -} - -static inline uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_ldq_be_data_ra(env, addr, ra); -} - -static inline uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_lduw_le_data_ra(env, addr, ra); -} - -static inline int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_ldsw_le_data_ra(env, addr, ra); -} - -static inline uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_ldl_le_data_ra(env, addr, ra); -} - -static inline uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return cpu_ldq_le_data_ra(env, addr, ra); -} - -static inline void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, int mmu_idx, uintptr_t ra) -{ - cpu_stb_data_ra(env, addr, val, ra); -} - -static inline void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, int mmu_idx, - uintptr_t ra) -{ - cpu_stw_be_data_ra(env, addr, val, ra); -} - -static inline void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, int mmu_idx, - uintptr_t ra) -{ - cpu_stl_be_data_ra(env, addr, val, ra); -} - -static inline void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - uint64_t val, int mmu_idx, - uintptr_t ra) -{ - cpu_stq_be_data_ra(env, addr, val, ra); -} - -static inline void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, int mmu_idx, - uintptr_t ra) -{ - cpu_stw_le_data_ra(env, addr, val, ra); -} - -static inline void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, int mmu_idx, - uintptr_t ra) -{ - cpu_stl_le_data_ra(env, addr, val, ra); -} - -static inline void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - uint64_t val, int mmu_idx, - uintptr_t ra) -{ - cpu_stq_le_data_ra(env, addr, val, ra); -} - #else /* Needed for TCG_OVERSIZED_GUEST */ @@ -336,46 +287,6 @@ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, return &env_tlb(env)->f[mmu_idx].table[tlb_index(env, mmu_idx, addr)]; } -uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); -int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); - -uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); -int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); -uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); - -uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); -int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); -uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra); - -void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t retaddr); - -void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t retaddr); -void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t retaddr); -void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, - int mmu_idx, uintptr_t retaddr); - -void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t retaddr); -void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t retaddr); -void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, - int mmu_idx, uintptr_t retaddr); - #endif /* defined(CONFIG_USER_ONLY) */ #ifdef TARGET_WORDS_BIGENDIAN @@ -391,6 +302,9 @@ void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, # define cpu_ldsw_mmuidx_ra cpu_ldsw_be_mmuidx_ra # define cpu_ldl_mmuidx_ra cpu_ldl_be_mmuidx_ra # define cpu_ldq_mmuidx_ra cpu_ldq_be_mmuidx_ra +# define cpu_ldw_mmu cpu_ldw_be_mmu +# define cpu_ldl_mmu cpu_ldl_be_mmu +# define cpu_ldq_mmu cpu_ldq_be_mmu # define cpu_stw_data cpu_stw_be_data # define cpu_stl_data cpu_stl_be_data # define cpu_stq_data cpu_stq_be_data @@ -400,6 +314,9 @@ void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, # define cpu_stw_mmuidx_ra cpu_stw_be_mmuidx_ra # define cpu_stl_mmuidx_ra cpu_stl_be_mmuidx_ra # define cpu_stq_mmuidx_ra cpu_stq_be_mmuidx_ra +# define cpu_stw_mmu cpu_stw_be_mmu +# define cpu_stl_mmu cpu_stl_be_mmu +# define cpu_stq_mmu cpu_stq_be_mmu #else # define cpu_lduw_data cpu_lduw_le_data # define cpu_ldsw_data cpu_ldsw_le_data @@ -413,6 +330,9 @@ void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, # define cpu_ldsw_mmuidx_ra cpu_ldsw_le_mmuidx_ra # define cpu_ldl_mmuidx_ra cpu_ldl_le_mmuidx_ra # define cpu_ldq_mmuidx_ra cpu_ldq_le_mmuidx_ra +# define cpu_ldw_mmu cpu_ldw_le_mmu +# define cpu_ldl_mmu cpu_ldl_le_mmu +# define cpu_ldq_mmu cpu_ldq_le_mmu # define cpu_stw_data cpu_stw_le_data # define cpu_stl_data cpu_stl_le_data # define cpu_stq_data cpu_stq_le_data @@ -422,6 +342,9 @@ void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, # define cpu_stw_mmuidx_ra cpu_stw_le_mmuidx_ra # define cpu_stl_mmuidx_ra cpu_stl_le_mmuidx_ra # define cpu_stq_mmuidx_ra cpu_stq_le_mmuidx_ra +# define cpu_stw_mmu cpu_stw_le_mmu +# define cpu_stl_mmu cpu_stl_le_mmu +# define cpu_stq_mmu cpu_stq_le_mmu #endif uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr); From b4c8f3d4dddcb6e547e1ba318f60ae9566743db1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 4 Oct 2021 17:40:58 -0700 Subject: [PATCH 0474/1334] accel/tcg: Move cpu_atomic decls to exec/cpu_ldst.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous placement in tcg/tcg.h was not logical. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/cpu_ldst.h | 87 +++++++++++++++++++++++++++++++++++ include/tcg/tcg.h | 87 ----------------------------------- target/arm/helper-a64.c | 1 - target/m68k/op_helper.c | 1 - target/ppc/mem_helper.c | 1 - target/s390x/tcg/mem_helper.c | 1 - 6 files changed, 87 insertions(+), 91 deletions(-) diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index a4dad0772f..a878fd0105 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -63,6 +63,7 @@ #define CPU_LDST_H #include "exec/memopidx.h" +#include "qemu/int128.h" #if defined(CONFIG_USER_ONLY) /* sparc32plus has 64bit long but 32bit space address @@ -233,6 +234,92 @@ void cpu_stl_le_mmu(CPUArchState *env, abi_ptr ptr, uint32_t val, void cpu_stq_le_mmu(CPUArchState *env, abi_ptr ptr, uint64_t val, MemOpIdx oi, uintptr_t ra); +uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, target_ulong addr, + uint64_t cmpv, uint64_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, target_ulong addr, + uint64_t cmpv, uint64_t newv, + MemOpIdx oi, uintptr_t retaddr); + +#define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ +TYPE cpu_atomic_ ## NAME ## SUFFIX ## _mmu \ + (CPUArchState *env, target_ulong addr, TYPE val, \ + MemOpIdx oi, uintptr_t retaddr); + +#ifdef CONFIG_ATOMIC64 +#define GEN_ATOMIC_HELPER_ALL(NAME) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) \ + GEN_ATOMIC_HELPER(NAME, uint64_t, q_le) \ + GEN_ATOMIC_HELPER(NAME, uint64_t, q_be) +#else +#define GEN_ATOMIC_HELPER_ALL(NAME) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) +#endif + +GEN_ATOMIC_HELPER_ALL(fetch_add) +GEN_ATOMIC_HELPER_ALL(fetch_sub) +GEN_ATOMIC_HELPER_ALL(fetch_and) +GEN_ATOMIC_HELPER_ALL(fetch_or) +GEN_ATOMIC_HELPER_ALL(fetch_xor) +GEN_ATOMIC_HELPER_ALL(fetch_smin) +GEN_ATOMIC_HELPER_ALL(fetch_umin) +GEN_ATOMIC_HELPER_ALL(fetch_smax) +GEN_ATOMIC_HELPER_ALL(fetch_umax) + +GEN_ATOMIC_HELPER_ALL(add_fetch) +GEN_ATOMIC_HELPER_ALL(sub_fetch) +GEN_ATOMIC_HELPER_ALL(and_fetch) +GEN_ATOMIC_HELPER_ALL(or_fetch) +GEN_ATOMIC_HELPER_ALL(xor_fetch) +GEN_ATOMIC_HELPER_ALL(smin_fetch) +GEN_ATOMIC_HELPER_ALL(umin_fetch) +GEN_ATOMIC_HELPER_ALL(smax_fetch) +GEN_ATOMIC_HELPER_ALL(umax_fetch) + +GEN_ATOMIC_HELPER_ALL(xchg) + +#undef GEN_ATOMIC_HELPER_ALL +#undef GEN_ATOMIC_HELPER + +Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, target_ulong addr, + Int128 cmpv, Int128 newv, + MemOpIdx oi, uintptr_t retaddr); +Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, target_ulong addr, + Int128 cmpv, Int128 newv, + MemOpIdx oi, uintptr_t retaddr); + +Int128 cpu_atomic_ldo_le_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +Int128 cpu_atomic_ldo_be_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +void cpu_atomic_sto_le_mmu(CPUArchState *env, target_ulong addr, Int128 val, + MemOpIdx oi, uintptr_t retaddr); +void cpu_atomic_sto_be_mmu(CPUArchState *env, target_ulong addr, Int128 val, + MemOpIdx oi, uintptr_t retaddr); + #if defined(CONFIG_USER_ONLY) extern __thread uintptr_t helper_retaddr; diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 9f398b9afe..83e38487cf 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -33,7 +33,6 @@ #include "qemu/queue.h" #include "tcg/tcg-mo.h" #include "tcg-target.h" -#include "qemu/int128.h" #include "tcg/tcg-cond.h" /* XXX: make safe guess about sizes */ @@ -1312,92 +1311,6 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, #endif #endif /* CONFIG_SOFTMMU */ -uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, target_ulong addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, target_ulong addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, target_ulong addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, target_ulong addr, - uint64_t cmpv, uint64_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, target_ulong addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, target_ulong addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, target_ulong addr, - uint64_t cmpv, uint64_t newv, - MemOpIdx oi, uintptr_t retaddr); - -#define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ -TYPE cpu_atomic_ ## NAME ## SUFFIX ## _mmu \ - (CPUArchState *env, target_ulong addr, TYPE val, \ - MemOpIdx oi, uintptr_t retaddr); - -#ifdef CONFIG_ATOMIC64 -#define GEN_ATOMIC_HELPER_ALL(NAME) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) \ - GEN_ATOMIC_HELPER(NAME, uint64_t, q_le) \ - GEN_ATOMIC_HELPER(NAME, uint64_t, q_be) -#else -#define GEN_ATOMIC_HELPER_ALL(NAME) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) -#endif - -GEN_ATOMIC_HELPER_ALL(fetch_add) -GEN_ATOMIC_HELPER_ALL(fetch_sub) -GEN_ATOMIC_HELPER_ALL(fetch_and) -GEN_ATOMIC_HELPER_ALL(fetch_or) -GEN_ATOMIC_HELPER_ALL(fetch_xor) -GEN_ATOMIC_HELPER_ALL(fetch_smin) -GEN_ATOMIC_HELPER_ALL(fetch_umin) -GEN_ATOMIC_HELPER_ALL(fetch_smax) -GEN_ATOMIC_HELPER_ALL(fetch_umax) - -GEN_ATOMIC_HELPER_ALL(add_fetch) -GEN_ATOMIC_HELPER_ALL(sub_fetch) -GEN_ATOMIC_HELPER_ALL(and_fetch) -GEN_ATOMIC_HELPER_ALL(or_fetch) -GEN_ATOMIC_HELPER_ALL(xor_fetch) -GEN_ATOMIC_HELPER_ALL(smin_fetch) -GEN_ATOMIC_HELPER_ALL(umin_fetch) -GEN_ATOMIC_HELPER_ALL(smax_fetch) -GEN_ATOMIC_HELPER_ALL(umax_fetch) - -GEN_ATOMIC_HELPER_ALL(xchg) - -#undef GEN_ATOMIC_HELPER_ALL -#undef GEN_ATOMIC_HELPER - -Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, target_ulong addr, - Int128 cmpv, Int128 newv, - MemOpIdx oi, uintptr_t retaddr); -Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, target_ulong addr, - Int128 cmpv, Int128 newv, - MemOpIdx oi, uintptr_t retaddr); - -Int128 cpu_atomic_ldo_le_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -Int128 cpu_atomic_ldo_be_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -void cpu_atomic_sto_le_mmu(CPUArchState *env, target_ulong addr, Int128 val, - MemOpIdx oi, uintptr_t retaddr); -void cpu_atomic_sto_be_mmu(CPUArchState *env, target_ulong addr, Int128 val, - MemOpIdx oi, uintptr_t retaddr); - #ifdef CONFIG_DEBUG_TCG void tcg_assert_listed_vecop(TCGOpcode); #else diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 4cafd3c11a..b110c57956 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -32,7 +32,6 @@ #include "exec/cpu_ldst.h" #include "qemu/int128.h" #include "qemu/atomic128.h" -#include "tcg/tcg.h" #include "fpu/softfloat.h" #include /* For crc32 */ diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index c1bf73b6f9..cfbc987ba6 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -22,7 +22,6 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "semihosting/semihost.h" -#include "tcg/tcg.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index e2282baa8d..39945d9ea5 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -25,7 +25,6 @@ #include "exec/helper-proto.h" #include "helper_regs.h" #include "exec/cpu_ldst.h" -#include "tcg/tcg.h" #include "internal.h" #include "qemu/atomic128.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index e64d1bc725..251d4acf55 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -27,7 +27,6 @@ #include "exec/cpu_ldst.h" #include "qemu/int128.h" #include "qemu/atomic128.h" -#include "tcg/tcg.h" #include "trace.h" #if !defined(CONFIG_USER_ONLY) From 948f88661c68307162dc4241144d87dc7772ca8d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 08:57:05 -1000 Subject: [PATCH 0475/1334] target/mips: Use cpu_*_data_ra for msa load/store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should not have been using the helper_ret_* set of functions, as they are supposed to be private to tcg. Nor should we have been using the plain cpu_*_data set of functions, as they do not handle unwinding properly. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/tcg/msa_helper.c | 420 +++++++++++------------------------ 1 file changed, 135 insertions(+), 285 deletions(-) diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index 167d9a591c..a8880ce81c 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -8222,79 +8222,42 @@ void helper_msa_ld_b(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - MEMOP_IDX(DF_BYTE) -#if !defined(CONFIG_USER_ONLY) + uintptr_t ra = GETPC(); + #if !defined(HOST_WORDS_BIGENDIAN) - pwd->b[0] = helper_ret_ldub_mmu(env, addr + (0 << DF_BYTE), oi, GETPC()); - pwd->b[1] = helper_ret_ldub_mmu(env, addr + (1 << DF_BYTE), oi, GETPC()); - pwd->b[2] = helper_ret_ldub_mmu(env, addr + (2 << DF_BYTE), oi, GETPC()); - pwd->b[3] = helper_ret_ldub_mmu(env, addr + (3 << DF_BYTE), oi, GETPC()); - pwd->b[4] = helper_ret_ldub_mmu(env, addr + (4 << DF_BYTE), oi, GETPC()); - pwd->b[5] = helper_ret_ldub_mmu(env, addr + (5 << DF_BYTE), oi, GETPC()); - pwd->b[6] = helper_ret_ldub_mmu(env, addr + (6 << DF_BYTE), oi, GETPC()); - pwd->b[7] = helper_ret_ldub_mmu(env, addr + (7 << DF_BYTE), oi, GETPC()); - pwd->b[8] = helper_ret_ldub_mmu(env, addr + (8 << DF_BYTE), oi, GETPC()); - pwd->b[9] = helper_ret_ldub_mmu(env, addr + (9 << DF_BYTE), oi, GETPC()); - pwd->b[10] = helper_ret_ldub_mmu(env, addr + (10 << DF_BYTE), oi, GETPC()); - pwd->b[11] = helper_ret_ldub_mmu(env, addr + (11 << DF_BYTE), oi, GETPC()); - pwd->b[12] = helper_ret_ldub_mmu(env, addr + (12 << DF_BYTE), oi, GETPC()); - pwd->b[13] = helper_ret_ldub_mmu(env, addr + (13 << DF_BYTE), oi, GETPC()); - pwd->b[14] = helper_ret_ldub_mmu(env, addr + (14 << DF_BYTE), oi, GETPC()); - pwd->b[15] = helper_ret_ldub_mmu(env, addr + (15 << DF_BYTE), oi, GETPC()); + pwd->b[0] = cpu_ldub_data_ra(env, addr + (0 << DF_BYTE), ra); + pwd->b[1] = cpu_ldub_data_ra(env, addr + (1 << DF_BYTE), ra); + pwd->b[2] = cpu_ldub_data_ra(env, addr + (2 << DF_BYTE), ra); + pwd->b[3] = cpu_ldub_data_ra(env, addr + (3 << DF_BYTE), ra); + pwd->b[4] = cpu_ldub_data_ra(env, addr + (4 << DF_BYTE), ra); + pwd->b[5] = cpu_ldub_data_ra(env, addr + (5 << DF_BYTE), ra); + pwd->b[6] = cpu_ldub_data_ra(env, addr + (6 << DF_BYTE), ra); + pwd->b[7] = cpu_ldub_data_ra(env, addr + (7 << DF_BYTE), ra); + pwd->b[8] = cpu_ldub_data_ra(env, addr + (8 << DF_BYTE), ra); + pwd->b[9] = cpu_ldub_data_ra(env, addr + (9 << DF_BYTE), ra); + pwd->b[10] = cpu_ldub_data_ra(env, addr + (10 << DF_BYTE), ra); + pwd->b[11] = cpu_ldub_data_ra(env, addr + (11 << DF_BYTE), ra); + pwd->b[12] = cpu_ldub_data_ra(env, addr + (12 << DF_BYTE), ra); + pwd->b[13] = cpu_ldub_data_ra(env, addr + (13 << DF_BYTE), ra); + pwd->b[14] = cpu_ldub_data_ra(env, addr + (14 << DF_BYTE), ra); + pwd->b[15] = cpu_ldub_data_ra(env, addr + (15 << DF_BYTE), ra); #else - pwd->b[0] = helper_ret_ldub_mmu(env, addr + (7 << DF_BYTE), oi, GETPC()); - pwd->b[1] = helper_ret_ldub_mmu(env, addr + (6 << DF_BYTE), oi, GETPC()); - pwd->b[2] = helper_ret_ldub_mmu(env, addr + (5 << DF_BYTE), oi, GETPC()); - pwd->b[3] = helper_ret_ldub_mmu(env, addr + (4 << DF_BYTE), oi, GETPC()); - pwd->b[4] = helper_ret_ldub_mmu(env, addr + (3 << DF_BYTE), oi, GETPC()); - pwd->b[5] = helper_ret_ldub_mmu(env, addr + (2 << DF_BYTE), oi, GETPC()); - pwd->b[6] = helper_ret_ldub_mmu(env, addr + (1 << DF_BYTE), oi, GETPC()); - pwd->b[7] = helper_ret_ldub_mmu(env, addr + (0 << DF_BYTE), oi, GETPC()); - pwd->b[8] = helper_ret_ldub_mmu(env, addr + (15 << DF_BYTE), oi, GETPC()); - pwd->b[9] = helper_ret_ldub_mmu(env, addr + (14 << DF_BYTE), oi, GETPC()); - pwd->b[10] = helper_ret_ldub_mmu(env, addr + (13 << DF_BYTE), oi, GETPC()); - pwd->b[11] = helper_ret_ldub_mmu(env, addr + (12 << DF_BYTE), oi, GETPC()); - pwd->b[12] = helper_ret_ldub_mmu(env, addr + (11 << DF_BYTE), oi, GETPC()); - pwd->b[13] = helper_ret_ldub_mmu(env, addr + (10 << DF_BYTE), oi, GETPC()); - pwd->b[14] = helper_ret_ldub_mmu(env, addr + (9 << DF_BYTE), oi, GETPC()); - pwd->b[15] = helper_ret_ldub_mmu(env, addr + (8 << DF_BYTE), oi, GETPC()); -#endif -#else -#if !defined(HOST_WORDS_BIGENDIAN) - pwd->b[0] = cpu_ldub_data(env, addr + (0 << DF_BYTE)); - pwd->b[1] = cpu_ldub_data(env, addr + (1 << DF_BYTE)); - pwd->b[2] = cpu_ldub_data(env, addr + (2 << DF_BYTE)); - pwd->b[3] = cpu_ldub_data(env, addr + (3 << DF_BYTE)); - pwd->b[4] = cpu_ldub_data(env, addr + (4 << DF_BYTE)); - pwd->b[5] = cpu_ldub_data(env, addr + (5 << DF_BYTE)); - pwd->b[6] = cpu_ldub_data(env, addr + (6 << DF_BYTE)); - pwd->b[7] = cpu_ldub_data(env, addr + (7 << DF_BYTE)); - pwd->b[8] = cpu_ldub_data(env, addr + (8 << DF_BYTE)); - pwd->b[9] = cpu_ldub_data(env, addr + (9 << DF_BYTE)); - pwd->b[10] = cpu_ldub_data(env, addr + (10 << DF_BYTE)); - pwd->b[11] = cpu_ldub_data(env, addr + (11 << DF_BYTE)); - pwd->b[12] = cpu_ldub_data(env, addr + (12 << DF_BYTE)); - pwd->b[13] = cpu_ldub_data(env, addr + (13 << DF_BYTE)); - pwd->b[14] = cpu_ldub_data(env, addr + (14 << DF_BYTE)); - pwd->b[15] = cpu_ldub_data(env, addr + (15 << DF_BYTE)); -#else - pwd->b[0] = cpu_ldub_data(env, addr + (7 << DF_BYTE)); - pwd->b[1] = cpu_ldub_data(env, addr + (6 << DF_BYTE)); - pwd->b[2] = cpu_ldub_data(env, addr + (5 << DF_BYTE)); - pwd->b[3] = cpu_ldub_data(env, addr + (4 << DF_BYTE)); - pwd->b[4] = cpu_ldub_data(env, addr + (3 << DF_BYTE)); - pwd->b[5] = cpu_ldub_data(env, addr + (2 << DF_BYTE)); - pwd->b[6] = cpu_ldub_data(env, addr + (1 << DF_BYTE)); - pwd->b[7] = cpu_ldub_data(env, addr + (0 << DF_BYTE)); - pwd->b[8] = cpu_ldub_data(env, addr + (15 << DF_BYTE)); - pwd->b[9] = cpu_ldub_data(env, addr + (14 << DF_BYTE)); - pwd->b[10] = cpu_ldub_data(env, addr + (13 << DF_BYTE)); - pwd->b[11] = cpu_ldub_data(env, addr + (12 << DF_BYTE)); - pwd->b[12] = cpu_ldub_data(env, addr + (11 << DF_BYTE)); - pwd->b[13] = cpu_ldub_data(env, addr + (10 << DF_BYTE)); - pwd->b[14] = cpu_ldub_data(env, addr + (9 << DF_BYTE)); - pwd->b[15] = cpu_ldub_data(env, addr + (8 << DF_BYTE)); -#endif + pwd->b[0] = cpu_ldub_data_ra(env, addr + (7 << DF_BYTE), ra); + pwd->b[1] = cpu_ldub_data_ra(env, addr + (6 << DF_BYTE), ra); + pwd->b[2] = cpu_ldub_data_ra(env, addr + (5 << DF_BYTE), ra); + pwd->b[3] = cpu_ldub_data_ra(env, addr + (4 << DF_BYTE), ra); + pwd->b[4] = cpu_ldub_data_ra(env, addr + (3 << DF_BYTE), ra); + pwd->b[5] = cpu_ldub_data_ra(env, addr + (2 << DF_BYTE), ra); + pwd->b[6] = cpu_ldub_data_ra(env, addr + (1 << DF_BYTE), ra); + pwd->b[7] = cpu_ldub_data_ra(env, addr + (0 << DF_BYTE), ra); + pwd->b[8] = cpu_ldub_data_ra(env, addr + (15 << DF_BYTE), ra); + pwd->b[9] = cpu_ldub_data_ra(env, addr + (14 << DF_BYTE), ra); + pwd->b[10] = cpu_ldub_data_ra(env, addr + (13 << DF_BYTE), ra); + pwd->b[11] = cpu_ldub_data_ra(env, addr + (12 << DF_BYTE), ra); + pwd->b[12] = cpu_ldub_data_ra(env, addr + (11 << DF_BYTE), ra); + pwd->b[13] = cpu_ldub_data_ra(env, addr + (10 << DF_BYTE), ra); + pwd->b[14] = cpu_ldub_data_ra(env, addr + (9 << DF_BYTE), ra); + pwd->b[15] = cpu_ldub_data_ra(env, addr + (8 << DF_BYTE), ra); #endif } @@ -8302,47 +8265,26 @@ void helper_msa_ld_h(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - MEMOP_IDX(DF_HALF) -#if !defined(CONFIG_USER_ONLY) + uintptr_t ra = GETPC(); + #if !defined(HOST_WORDS_BIGENDIAN) - pwd->h[0] = helper_ret_lduw_mmu(env, addr + (0 << DF_HALF), oi, GETPC()); - pwd->h[1] = helper_ret_lduw_mmu(env, addr + (1 << DF_HALF), oi, GETPC()); - pwd->h[2] = helper_ret_lduw_mmu(env, addr + (2 << DF_HALF), oi, GETPC()); - pwd->h[3] = helper_ret_lduw_mmu(env, addr + (3 << DF_HALF), oi, GETPC()); - pwd->h[4] = helper_ret_lduw_mmu(env, addr + (4 << DF_HALF), oi, GETPC()); - pwd->h[5] = helper_ret_lduw_mmu(env, addr + (5 << DF_HALF), oi, GETPC()); - pwd->h[6] = helper_ret_lduw_mmu(env, addr + (6 << DF_HALF), oi, GETPC()); - pwd->h[7] = helper_ret_lduw_mmu(env, addr + (7 << DF_HALF), oi, GETPC()); + pwd->h[0] = cpu_lduw_data_ra(env, addr + (0 << DF_HALF), ra); + pwd->h[1] = cpu_lduw_data_ra(env, addr + (1 << DF_HALF), ra); + pwd->h[2] = cpu_lduw_data_ra(env, addr + (2 << DF_HALF), ra); + pwd->h[3] = cpu_lduw_data_ra(env, addr + (3 << DF_HALF), ra); + pwd->h[4] = cpu_lduw_data_ra(env, addr + (4 << DF_HALF), ra); + pwd->h[5] = cpu_lduw_data_ra(env, addr + (5 << DF_HALF), ra); + pwd->h[6] = cpu_lduw_data_ra(env, addr + (6 << DF_HALF), ra); + pwd->h[7] = cpu_lduw_data_ra(env, addr + (7 << DF_HALF), ra); #else - pwd->h[0] = helper_ret_lduw_mmu(env, addr + (3 << DF_HALF), oi, GETPC()); - pwd->h[1] = helper_ret_lduw_mmu(env, addr + (2 << DF_HALF), oi, GETPC()); - pwd->h[2] = helper_ret_lduw_mmu(env, addr + (1 << DF_HALF), oi, GETPC()); - pwd->h[3] = helper_ret_lduw_mmu(env, addr + (0 << DF_HALF), oi, GETPC()); - pwd->h[4] = helper_ret_lduw_mmu(env, addr + (7 << DF_HALF), oi, GETPC()); - pwd->h[5] = helper_ret_lduw_mmu(env, addr + (6 << DF_HALF), oi, GETPC()); - pwd->h[6] = helper_ret_lduw_mmu(env, addr + (5 << DF_HALF), oi, GETPC()); - pwd->h[7] = helper_ret_lduw_mmu(env, addr + (4 << DF_HALF), oi, GETPC()); -#endif -#else -#if !defined(HOST_WORDS_BIGENDIAN) - pwd->h[0] = cpu_lduw_data(env, addr + (0 << DF_HALF)); - pwd->h[1] = cpu_lduw_data(env, addr + (1 << DF_HALF)); - pwd->h[2] = cpu_lduw_data(env, addr + (2 << DF_HALF)); - pwd->h[3] = cpu_lduw_data(env, addr + (3 << DF_HALF)); - pwd->h[4] = cpu_lduw_data(env, addr + (4 << DF_HALF)); - pwd->h[5] = cpu_lduw_data(env, addr + (5 << DF_HALF)); - pwd->h[6] = cpu_lduw_data(env, addr + (6 << DF_HALF)); - pwd->h[7] = cpu_lduw_data(env, addr + (7 << DF_HALF)); -#else - pwd->h[0] = cpu_lduw_data(env, addr + (3 << DF_HALF)); - pwd->h[1] = cpu_lduw_data(env, addr + (2 << DF_HALF)); - pwd->h[2] = cpu_lduw_data(env, addr + (1 << DF_HALF)); - pwd->h[3] = cpu_lduw_data(env, addr + (0 << DF_HALF)); - pwd->h[4] = cpu_lduw_data(env, addr + (7 << DF_HALF)); - pwd->h[5] = cpu_lduw_data(env, addr + (6 << DF_HALF)); - pwd->h[6] = cpu_lduw_data(env, addr + (5 << DF_HALF)); - pwd->h[7] = cpu_lduw_data(env, addr + (4 << DF_HALF)); -#endif + pwd->h[0] = cpu_lduw_data_ra(env, addr + (3 << DF_HALF), ra); + pwd->h[1] = cpu_lduw_data_ra(env, addr + (2 << DF_HALF), ra); + pwd->h[2] = cpu_lduw_data_ra(env, addr + (1 << DF_HALF), ra); + pwd->h[3] = cpu_lduw_data_ra(env, addr + (0 << DF_HALF), ra); + pwd->h[4] = cpu_lduw_data_ra(env, addr + (7 << DF_HALF), ra); + pwd->h[5] = cpu_lduw_data_ra(env, addr + (6 << DF_HALF), ra); + pwd->h[6] = cpu_lduw_data_ra(env, addr + (5 << DF_HALF), ra); + pwd->h[7] = cpu_lduw_data_ra(env, addr + (4 << DF_HALF), ra); #endif } @@ -8350,31 +8292,18 @@ void helper_msa_ld_w(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - MEMOP_IDX(DF_WORD) -#if !defined(CONFIG_USER_ONLY) + uintptr_t ra = GETPC(); + #if !defined(HOST_WORDS_BIGENDIAN) - pwd->w[0] = helper_ret_ldul_mmu(env, addr + (0 << DF_WORD), oi, GETPC()); - pwd->w[1] = helper_ret_ldul_mmu(env, addr + (1 << DF_WORD), oi, GETPC()); - pwd->w[2] = helper_ret_ldul_mmu(env, addr + (2 << DF_WORD), oi, GETPC()); - pwd->w[3] = helper_ret_ldul_mmu(env, addr + (3 << DF_WORD), oi, GETPC()); + pwd->w[0] = cpu_ldl_data_ra(env, addr + (0 << DF_WORD), ra); + pwd->w[1] = cpu_ldl_data_ra(env, addr + (1 << DF_WORD), ra); + pwd->w[2] = cpu_ldl_data_ra(env, addr + (2 << DF_WORD), ra); + pwd->w[3] = cpu_ldl_data_ra(env, addr + (3 << DF_WORD), ra); #else - pwd->w[0] = helper_ret_ldul_mmu(env, addr + (1 << DF_WORD), oi, GETPC()); - pwd->w[1] = helper_ret_ldul_mmu(env, addr + (0 << DF_WORD), oi, GETPC()); - pwd->w[2] = helper_ret_ldul_mmu(env, addr + (3 << DF_WORD), oi, GETPC()); - pwd->w[3] = helper_ret_ldul_mmu(env, addr + (2 << DF_WORD), oi, GETPC()); -#endif -#else -#if !defined(HOST_WORDS_BIGENDIAN) - pwd->w[0] = cpu_ldl_data(env, addr + (0 << DF_WORD)); - pwd->w[1] = cpu_ldl_data(env, addr + (1 << DF_WORD)); - pwd->w[2] = cpu_ldl_data(env, addr + (2 << DF_WORD)); - pwd->w[3] = cpu_ldl_data(env, addr + (3 << DF_WORD)); -#else - pwd->w[0] = cpu_ldl_data(env, addr + (1 << DF_WORD)); - pwd->w[1] = cpu_ldl_data(env, addr + (0 << DF_WORD)); - pwd->w[2] = cpu_ldl_data(env, addr + (3 << DF_WORD)); - pwd->w[3] = cpu_ldl_data(env, addr + (2 << DF_WORD)); -#endif + pwd->w[0] = cpu_ldl_data_ra(env, addr + (1 << DF_WORD), ra); + pwd->w[1] = cpu_ldl_data_ra(env, addr + (0 << DF_WORD), ra); + pwd->w[2] = cpu_ldl_data_ra(env, addr + (3 << DF_WORD), ra); + pwd->w[3] = cpu_ldl_data_ra(env, addr + (2 << DF_WORD), ra); #endif } @@ -8382,14 +8311,10 @@ void helper_msa_ld_d(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - MEMOP_IDX(DF_DOUBLE) -#if !defined(CONFIG_USER_ONLY) - pwd->d[0] = helper_ret_ldq_mmu(env, addr + (0 << DF_DOUBLE), oi, GETPC()); - pwd->d[1] = helper_ret_ldq_mmu(env, addr + (1 << DF_DOUBLE), oi, GETPC()); -#else - pwd->d[0] = cpu_ldq_data(env, addr + (0 << DF_DOUBLE)); - pwd->d[1] = cpu_ldq_data(env, addr + (1 << DF_DOUBLE)); -#endif + uintptr_t ra = GETPC(); + + pwd->d[0] = cpu_ldq_data_ra(env, addr + (0 << DF_DOUBLE), ra); + pwd->d[1] = cpu_ldq_data_ra(env, addr + (1 << DF_DOUBLE), ra); } #define MSA_PAGESPAN(x) \ @@ -8415,81 +8340,44 @@ void helper_msa_st_b(CPUMIPSState *env, uint32_t wd, { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); int mmu_idx = cpu_mmu_index(env, false); + uintptr_t ra = GETPC(); + + ensure_writable_pages(env, addr, mmu_idx, ra); - MEMOP_IDX(DF_BYTE) - ensure_writable_pages(env, addr, mmu_idx, GETPC()); -#if !defined(CONFIG_USER_ONLY) #if !defined(HOST_WORDS_BIGENDIAN) - helper_ret_stb_mmu(env, addr + (0 << DF_BYTE), pwd->b[0], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (1 << DF_BYTE), pwd->b[1], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (2 << DF_BYTE), pwd->b[2], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (3 << DF_BYTE), pwd->b[3], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (4 << DF_BYTE), pwd->b[4], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (5 << DF_BYTE), pwd->b[5], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (6 << DF_BYTE), pwd->b[6], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (7 << DF_BYTE), pwd->b[7], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (8 << DF_BYTE), pwd->b[8], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (9 << DF_BYTE), pwd->b[9], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (10 << DF_BYTE), pwd->b[10], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (11 << DF_BYTE), pwd->b[11], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (12 << DF_BYTE), pwd->b[12], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (13 << DF_BYTE), pwd->b[13], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (14 << DF_BYTE), pwd->b[14], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (15 << DF_BYTE), pwd->b[15], oi, GETPC()); + cpu_stb_data_ra(env, addr + (0 << DF_BYTE), pwd->b[0], ra); + cpu_stb_data_ra(env, addr + (1 << DF_BYTE), pwd->b[1], ra); + cpu_stb_data_ra(env, addr + (2 << DF_BYTE), pwd->b[2], ra); + cpu_stb_data_ra(env, addr + (3 << DF_BYTE), pwd->b[3], ra); + cpu_stb_data_ra(env, addr + (4 << DF_BYTE), pwd->b[4], ra); + cpu_stb_data_ra(env, addr + (5 << DF_BYTE), pwd->b[5], ra); + cpu_stb_data_ra(env, addr + (6 << DF_BYTE), pwd->b[6], ra); + cpu_stb_data_ra(env, addr + (7 << DF_BYTE), pwd->b[7], ra); + cpu_stb_data_ra(env, addr + (8 << DF_BYTE), pwd->b[8], ra); + cpu_stb_data_ra(env, addr + (9 << DF_BYTE), pwd->b[9], ra); + cpu_stb_data_ra(env, addr + (10 << DF_BYTE), pwd->b[10], ra); + cpu_stb_data_ra(env, addr + (11 << DF_BYTE), pwd->b[11], ra); + cpu_stb_data_ra(env, addr + (12 << DF_BYTE), pwd->b[12], ra); + cpu_stb_data_ra(env, addr + (13 << DF_BYTE), pwd->b[13], ra); + cpu_stb_data_ra(env, addr + (14 << DF_BYTE), pwd->b[14], ra); + cpu_stb_data_ra(env, addr + (15 << DF_BYTE), pwd->b[15], ra); #else - helper_ret_stb_mmu(env, addr + (7 << DF_BYTE), pwd->b[0], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (6 << DF_BYTE), pwd->b[1], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (5 << DF_BYTE), pwd->b[2], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (4 << DF_BYTE), pwd->b[3], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (3 << DF_BYTE), pwd->b[4], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (2 << DF_BYTE), pwd->b[5], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (1 << DF_BYTE), pwd->b[6], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (0 << DF_BYTE), pwd->b[7], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (15 << DF_BYTE), pwd->b[8], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (14 << DF_BYTE), pwd->b[9], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (13 << DF_BYTE), pwd->b[10], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (12 << DF_BYTE), pwd->b[11], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (11 << DF_BYTE), pwd->b[12], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (10 << DF_BYTE), pwd->b[13], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (9 << DF_BYTE), pwd->b[14], oi, GETPC()); - helper_ret_stb_mmu(env, addr + (8 << DF_BYTE), pwd->b[15], oi, GETPC()); -#endif -#else -#if !defined(HOST_WORDS_BIGENDIAN) - cpu_stb_data(env, addr + (0 << DF_BYTE), pwd->b[0]); - cpu_stb_data(env, addr + (1 << DF_BYTE), pwd->b[1]); - cpu_stb_data(env, addr + (2 << DF_BYTE), pwd->b[2]); - cpu_stb_data(env, addr + (3 << DF_BYTE), pwd->b[3]); - cpu_stb_data(env, addr + (4 << DF_BYTE), pwd->b[4]); - cpu_stb_data(env, addr + (5 << DF_BYTE), pwd->b[5]); - cpu_stb_data(env, addr + (6 << DF_BYTE), pwd->b[6]); - cpu_stb_data(env, addr + (7 << DF_BYTE), pwd->b[7]); - cpu_stb_data(env, addr + (8 << DF_BYTE), pwd->b[8]); - cpu_stb_data(env, addr + (9 << DF_BYTE), pwd->b[9]); - cpu_stb_data(env, addr + (10 << DF_BYTE), pwd->b[10]); - cpu_stb_data(env, addr + (11 << DF_BYTE), pwd->b[11]); - cpu_stb_data(env, addr + (12 << DF_BYTE), pwd->b[12]); - cpu_stb_data(env, addr + (13 << DF_BYTE), pwd->b[13]); - cpu_stb_data(env, addr + (14 << DF_BYTE), pwd->b[14]); - cpu_stb_data(env, addr + (15 << DF_BYTE), pwd->b[15]); -#else - cpu_stb_data(env, addr + (7 << DF_BYTE), pwd->b[0]); - cpu_stb_data(env, addr + (6 << DF_BYTE), pwd->b[1]); - cpu_stb_data(env, addr + (5 << DF_BYTE), pwd->b[2]); - cpu_stb_data(env, addr + (4 << DF_BYTE), pwd->b[3]); - cpu_stb_data(env, addr + (3 << DF_BYTE), pwd->b[4]); - cpu_stb_data(env, addr + (2 << DF_BYTE), pwd->b[5]); - cpu_stb_data(env, addr + (1 << DF_BYTE), pwd->b[6]); - cpu_stb_data(env, addr + (0 << DF_BYTE), pwd->b[7]); - cpu_stb_data(env, addr + (15 << DF_BYTE), pwd->b[8]); - cpu_stb_data(env, addr + (14 << DF_BYTE), pwd->b[9]); - cpu_stb_data(env, addr + (13 << DF_BYTE), pwd->b[10]); - cpu_stb_data(env, addr + (12 << DF_BYTE), pwd->b[11]); - cpu_stb_data(env, addr + (11 << DF_BYTE), pwd->b[12]); - cpu_stb_data(env, addr + (10 << DF_BYTE), pwd->b[13]); - cpu_stb_data(env, addr + (9 << DF_BYTE), pwd->b[14]); - cpu_stb_data(env, addr + (8 << DF_BYTE), pwd->b[15]); -#endif + cpu_stb_data_ra(env, addr + (7 << DF_BYTE), pwd->b[0], ra); + cpu_stb_data_ra(env, addr + (6 << DF_BYTE), pwd->b[1], ra); + cpu_stb_data_ra(env, addr + (5 << DF_BYTE), pwd->b[2], ra); + cpu_stb_data_ra(env, addr + (4 << DF_BYTE), pwd->b[3], ra); + cpu_stb_data_ra(env, addr + (3 << DF_BYTE), pwd->b[4], ra); + cpu_stb_data_ra(env, addr + (2 << DF_BYTE), pwd->b[5], ra); + cpu_stb_data_ra(env, addr + (1 << DF_BYTE), pwd->b[6], ra); + cpu_stb_data_ra(env, addr + (0 << DF_BYTE), pwd->b[7], ra); + cpu_stb_data_ra(env, addr + (15 << DF_BYTE), pwd->b[8], ra); + cpu_stb_data_ra(env, addr + (14 << DF_BYTE), pwd->b[9], ra); + cpu_stb_data_ra(env, addr + (13 << DF_BYTE), pwd->b[10], ra); + cpu_stb_data_ra(env, addr + (12 << DF_BYTE), pwd->b[11], ra); + cpu_stb_data_ra(env, addr + (11 << DF_BYTE), pwd->b[12], ra); + cpu_stb_data_ra(env, addr + (10 << DF_BYTE), pwd->b[13], ra); + cpu_stb_data_ra(env, addr + (9 << DF_BYTE), pwd->b[14], ra); + cpu_stb_data_ra(env, addr + (8 << DF_BYTE), pwd->b[15], ra); #endif } @@ -8498,49 +8386,28 @@ void helper_msa_st_h(CPUMIPSState *env, uint32_t wd, { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); int mmu_idx = cpu_mmu_index(env, false); + uintptr_t ra = GETPC(); + + ensure_writable_pages(env, addr, mmu_idx, ra); - MEMOP_IDX(DF_HALF) - ensure_writable_pages(env, addr, mmu_idx, GETPC()); -#if !defined(CONFIG_USER_ONLY) #if !defined(HOST_WORDS_BIGENDIAN) - helper_ret_stw_mmu(env, addr + (0 << DF_HALF), pwd->h[0], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (1 << DF_HALF), pwd->h[1], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (2 << DF_HALF), pwd->h[2], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (3 << DF_HALF), pwd->h[3], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (4 << DF_HALF), pwd->h[4], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (5 << DF_HALF), pwd->h[5], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (6 << DF_HALF), pwd->h[6], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (7 << DF_HALF), pwd->h[7], oi, GETPC()); + cpu_stw_data_ra(env, addr + (0 << DF_HALF), pwd->h[0], ra); + cpu_stw_data_ra(env, addr + (1 << DF_HALF), pwd->h[1], ra); + cpu_stw_data_ra(env, addr + (2 << DF_HALF), pwd->h[2], ra); + cpu_stw_data_ra(env, addr + (3 << DF_HALF), pwd->h[3], ra); + cpu_stw_data_ra(env, addr + (4 << DF_HALF), pwd->h[4], ra); + cpu_stw_data_ra(env, addr + (5 << DF_HALF), pwd->h[5], ra); + cpu_stw_data_ra(env, addr + (6 << DF_HALF), pwd->h[6], ra); + cpu_stw_data_ra(env, addr + (7 << DF_HALF), pwd->h[7], ra); #else - helper_ret_stw_mmu(env, addr + (3 << DF_HALF), pwd->h[0], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (2 << DF_HALF), pwd->h[1], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (1 << DF_HALF), pwd->h[2], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (0 << DF_HALF), pwd->h[3], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (7 << DF_HALF), pwd->h[4], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (6 << DF_HALF), pwd->h[5], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (5 << DF_HALF), pwd->h[6], oi, GETPC()); - helper_ret_stw_mmu(env, addr + (4 << DF_HALF), pwd->h[7], oi, GETPC()); -#endif -#else -#if !defined(HOST_WORDS_BIGENDIAN) - cpu_stw_data(env, addr + (0 << DF_HALF), pwd->h[0]); - cpu_stw_data(env, addr + (1 << DF_HALF), pwd->h[1]); - cpu_stw_data(env, addr + (2 << DF_HALF), pwd->h[2]); - cpu_stw_data(env, addr + (3 << DF_HALF), pwd->h[3]); - cpu_stw_data(env, addr + (4 << DF_HALF), pwd->h[4]); - cpu_stw_data(env, addr + (5 << DF_HALF), pwd->h[5]); - cpu_stw_data(env, addr + (6 << DF_HALF), pwd->h[6]); - cpu_stw_data(env, addr + (7 << DF_HALF), pwd->h[7]); -#else - cpu_stw_data(env, addr + (3 << DF_HALF), pwd->h[0]); - cpu_stw_data(env, addr + (2 << DF_HALF), pwd->h[1]); - cpu_stw_data(env, addr + (1 << DF_HALF), pwd->h[2]); - cpu_stw_data(env, addr + (0 << DF_HALF), pwd->h[3]); - cpu_stw_data(env, addr + (7 << DF_HALF), pwd->h[4]); - cpu_stw_data(env, addr + (6 << DF_HALF), pwd->h[5]); - cpu_stw_data(env, addr + (5 << DF_HALF), pwd->h[6]); - cpu_stw_data(env, addr + (4 << DF_HALF), pwd->h[7]); -#endif + cpu_stw_data_ra(env, addr + (3 << DF_HALF), pwd->h[0], ra); + cpu_stw_data_ra(env, addr + (2 << DF_HALF), pwd->h[1], ra); + cpu_stw_data_ra(env, addr + (1 << DF_HALF), pwd->h[2], ra); + cpu_stw_data_ra(env, addr + (0 << DF_HALF), pwd->h[3], ra); + cpu_stw_data_ra(env, addr + (7 << DF_HALF), pwd->h[4], ra); + cpu_stw_data_ra(env, addr + (6 << DF_HALF), pwd->h[5], ra); + cpu_stw_data_ra(env, addr + (5 << DF_HALF), pwd->h[6], ra); + cpu_stw_data_ra(env, addr + (4 << DF_HALF), pwd->h[7], ra); #endif } @@ -8549,33 +8416,20 @@ void helper_msa_st_w(CPUMIPSState *env, uint32_t wd, { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); int mmu_idx = cpu_mmu_index(env, false); + uintptr_t ra = GETPC(); + + ensure_writable_pages(env, addr, mmu_idx, ra); - MEMOP_IDX(DF_WORD) - ensure_writable_pages(env, addr, mmu_idx, GETPC()); -#if !defined(CONFIG_USER_ONLY) #if !defined(HOST_WORDS_BIGENDIAN) - helper_ret_stl_mmu(env, addr + (0 << DF_WORD), pwd->w[0], oi, GETPC()); - helper_ret_stl_mmu(env, addr + (1 << DF_WORD), pwd->w[1], oi, GETPC()); - helper_ret_stl_mmu(env, addr + (2 << DF_WORD), pwd->w[2], oi, GETPC()); - helper_ret_stl_mmu(env, addr + (3 << DF_WORD), pwd->w[3], oi, GETPC()); + cpu_stl_data_ra(env, addr + (0 << DF_WORD), pwd->w[0], ra); + cpu_stl_data_ra(env, addr + (1 << DF_WORD), pwd->w[1], ra); + cpu_stl_data_ra(env, addr + (2 << DF_WORD), pwd->w[2], ra); + cpu_stl_data_ra(env, addr + (3 << DF_WORD), pwd->w[3], ra); #else - helper_ret_stl_mmu(env, addr + (1 << DF_WORD), pwd->w[0], oi, GETPC()); - helper_ret_stl_mmu(env, addr + (0 << DF_WORD), pwd->w[1], oi, GETPC()); - helper_ret_stl_mmu(env, addr + (3 << DF_WORD), pwd->w[2], oi, GETPC()); - helper_ret_stl_mmu(env, addr + (2 << DF_WORD), pwd->w[3], oi, GETPC()); -#endif -#else -#if !defined(HOST_WORDS_BIGENDIAN) - cpu_stl_data(env, addr + (0 << DF_WORD), pwd->w[0]); - cpu_stl_data(env, addr + (1 << DF_WORD), pwd->w[1]); - cpu_stl_data(env, addr + (2 << DF_WORD), pwd->w[2]); - cpu_stl_data(env, addr + (3 << DF_WORD), pwd->w[3]); -#else - cpu_stl_data(env, addr + (1 << DF_WORD), pwd->w[0]); - cpu_stl_data(env, addr + (0 << DF_WORD), pwd->w[1]); - cpu_stl_data(env, addr + (3 << DF_WORD), pwd->w[2]); - cpu_stl_data(env, addr + (2 << DF_WORD), pwd->w[3]); -#endif + cpu_stl_data_ra(env, addr + (1 << DF_WORD), pwd->w[0], ra); + cpu_stl_data_ra(env, addr + (0 << DF_WORD), pwd->w[1], ra); + cpu_stl_data_ra(env, addr + (3 << DF_WORD), pwd->w[2], ra); + cpu_stl_data_ra(env, addr + (2 << DF_WORD), pwd->w[3], ra); #endif } @@ -8584,14 +8438,10 @@ void helper_msa_st_d(CPUMIPSState *env, uint32_t wd, { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); int mmu_idx = cpu_mmu_index(env, false); + uintptr_t ra = GETPC(); - MEMOP_IDX(DF_DOUBLE) ensure_writable_pages(env, addr, mmu_idx, GETPC()); -#if !defined(CONFIG_USER_ONLY) - helper_ret_stq_mmu(env, addr + (0 << DF_DOUBLE), pwd->d[0], oi, GETPC()); - helper_ret_stq_mmu(env, addr + (1 << DF_DOUBLE), pwd->d[1], oi, GETPC()); -#else - cpu_stq_data(env, addr + (0 << DF_DOUBLE), pwd->d[0]); - cpu_stq_data(env, addr + (1 << DF_DOUBLE), pwd->d[1]); -#endif + + cpu_stq_data_ra(env, addr + (0 << DF_DOUBLE), pwd->d[0], ra); + cpu_stq_data_ra(env, addr + (1 << DF_DOUBLE), pwd->d[1], ra); } From 68ad9260e099da7c6345140d1da1811285e05757 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 10:15:44 -1000 Subject: [PATCH 0476/1334] target/mips: Use 8-byte memory ops for msa load/store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than use 4-16 separate operations, use 2 operations plus some byte reordering as necessary. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/tcg/msa_helper.c | 201 +++++++++++++---------------------- 1 file changed, 71 insertions(+), 130 deletions(-) diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index a8880ce81c..e40c1b7057 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -8218,47 +8218,31 @@ void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MEMOP_IDX(DF) #endif +#ifdef TARGET_WORDS_BIGENDIAN +static inline uint64_t bswap16x4(uint64_t x) +{ + uint64_t m = 0x00ff00ff00ff00ffull; + return ((x & m) << 8) | ((x >> 8) & m); +} + +static inline uint64_t bswap32x2(uint64_t x) +{ + return ror64(bswap64(x), 32); +} +#endif + void helper_msa_ld_b(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); uintptr_t ra = GETPC(); + uint64_t d0, d1; -#if !defined(HOST_WORDS_BIGENDIAN) - pwd->b[0] = cpu_ldub_data_ra(env, addr + (0 << DF_BYTE), ra); - pwd->b[1] = cpu_ldub_data_ra(env, addr + (1 << DF_BYTE), ra); - pwd->b[2] = cpu_ldub_data_ra(env, addr + (2 << DF_BYTE), ra); - pwd->b[3] = cpu_ldub_data_ra(env, addr + (3 << DF_BYTE), ra); - pwd->b[4] = cpu_ldub_data_ra(env, addr + (4 << DF_BYTE), ra); - pwd->b[5] = cpu_ldub_data_ra(env, addr + (5 << DF_BYTE), ra); - pwd->b[6] = cpu_ldub_data_ra(env, addr + (6 << DF_BYTE), ra); - pwd->b[7] = cpu_ldub_data_ra(env, addr + (7 << DF_BYTE), ra); - pwd->b[8] = cpu_ldub_data_ra(env, addr + (8 << DF_BYTE), ra); - pwd->b[9] = cpu_ldub_data_ra(env, addr + (9 << DF_BYTE), ra); - pwd->b[10] = cpu_ldub_data_ra(env, addr + (10 << DF_BYTE), ra); - pwd->b[11] = cpu_ldub_data_ra(env, addr + (11 << DF_BYTE), ra); - pwd->b[12] = cpu_ldub_data_ra(env, addr + (12 << DF_BYTE), ra); - pwd->b[13] = cpu_ldub_data_ra(env, addr + (13 << DF_BYTE), ra); - pwd->b[14] = cpu_ldub_data_ra(env, addr + (14 << DF_BYTE), ra); - pwd->b[15] = cpu_ldub_data_ra(env, addr + (15 << DF_BYTE), ra); -#else - pwd->b[0] = cpu_ldub_data_ra(env, addr + (7 << DF_BYTE), ra); - pwd->b[1] = cpu_ldub_data_ra(env, addr + (6 << DF_BYTE), ra); - pwd->b[2] = cpu_ldub_data_ra(env, addr + (5 << DF_BYTE), ra); - pwd->b[3] = cpu_ldub_data_ra(env, addr + (4 << DF_BYTE), ra); - pwd->b[4] = cpu_ldub_data_ra(env, addr + (3 << DF_BYTE), ra); - pwd->b[5] = cpu_ldub_data_ra(env, addr + (2 << DF_BYTE), ra); - pwd->b[6] = cpu_ldub_data_ra(env, addr + (1 << DF_BYTE), ra); - pwd->b[7] = cpu_ldub_data_ra(env, addr + (0 << DF_BYTE), ra); - pwd->b[8] = cpu_ldub_data_ra(env, addr + (15 << DF_BYTE), ra); - pwd->b[9] = cpu_ldub_data_ra(env, addr + (14 << DF_BYTE), ra); - pwd->b[10] = cpu_ldub_data_ra(env, addr + (13 << DF_BYTE), ra); - pwd->b[11] = cpu_ldub_data_ra(env, addr + (12 << DF_BYTE), ra); - pwd->b[12] = cpu_ldub_data_ra(env, addr + (11 << DF_BYTE), ra); - pwd->b[13] = cpu_ldub_data_ra(env, addr + (10 << DF_BYTE), ra); - pwd->b[14] = cpu_ldub_data_ra(env, addr + (9 << DF_BYTE), ra); - pwd->b[15] = cpu_ldub_data_ra(env, addr + (8 << DF_BYTE), ra); -#endif + /* Load 8 bytes at a time. Vector element ordering makes this LE. */ + d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); + d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); + pwd->d[0] = d0; + pwd->d[1] = d1; } void helper_msa_ld_h(CPUMIPSState *env, uint32_t wd, @@ -8266,26 +8250,20 @@ void helper_msa_ld_h(CPUMIPSState *env, uint32_t wd, { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); uintptr_t ra = GETPC(); + uint64_t d0, d1; -#if !defined(HOST_WORDS_BIGENDIAN) - pwd->h[0] = cpu_lduw_data_ra(env, addr + (0 << DF_HALF), ra); - pwd->h[1] = cpu_lduw_data_ra(env, addr + (1 << DF_HALF), ra); - pwd->h[2] = cpu_lduw_data_ra(env, addr + (2 << DF_HALF), ra); - pwd->h[3] = cpu_lduw_data_ra(env, addr + (3 << DF_HALF), ra); - pwd->h[4] = cpu_lduw_data_ra(env, addr + (4 << DF_HALF), ra); - pwd->h[5] = cpu_lduw_data_ra(env, addr + (5 << DF_HALF), ra); - pwd->h[6] = cpu_lduw_data_ra(env, addr + (6 << DF_HALF), ra); - pwd->h[7] = cpu_lduw_data_ra(env, addr + (7 << DF_HALF), ra); -#else - pwd->h[0] = cpu_lduw_data_ra(env, addr + (3 << DF_HALF), ra); - pwd->h[1] = cpu_lduw_data_ra(env, addr + (2 << DF_HALF), ra); - pwd->h[2] = cpu_lduw_data_ra(env, addr + (1 << DF_HALF), ra); - pwd->h[3] = cpu_lduw_data_ra(env, addr + (0 << DF_HALF), ra); - pwd->h[4] = cpu_lduw_data_ra(env, addr + (7 << DF_HALF), ra); - pwd->h[5] = cpu_lduw_data_ra(env, addr + (6 << DF_HALF), ra); - pwd->h[6] = cpu_lduw_data_ra(env, addr + (5 << DF_HALF), ra); - pwd->h[7] = cpu_lduw_data_ra(env, addr + (4 << DF_HALF), ra); + /* + * Load 8 bytes at a time. Use little-endian load, then for + * big-endian target, we must then swap the four halfwords. + */ + d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); + d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); +#ifdef TARGET_WORDS_BIGENDIAN + d0 = bswap16x4(d0); + d1 = bswap16x4(d1); #endif + pwd->d[0] = d0; + pwd->d[1] = d1; } void helper_msa_ld_w(CPUMIPSState *env, uint32_t wd, @@ -8293,18 +8271,20 @@ void helper_msa_ld_w(CPUMIPSState *env, uint32_t wd, { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); uintptr_t ra = GETPC(); + uint64_t d0, d1; -#if !defined(HOST_WORDS_BIGENDIAN) - pwd->w[0] = cpu_ldl_data_ra(env, addr + (0 << DF_WORD), ra); - pwd->w[1] = cpu_ldl_data_ra(env, addr + (1 << DF_WORD), ra); - pwd->w[2] = cpu_ldl_data_ra(env, addr + (2 << DF_WORD), ra); - pwd->w[3] = cpu_ldl_data_ra(env, addr + (3 << DF_WORD), ra); -#else - pwd->w[0] = cpu_ldl_data_ra(env, addr + (1 << DF_WORD), ra); - pwd->w[1] = cpu_ldl_data_ra(env, addr + (0 << DF_WORD), ra); - pwd->w[2] = cpu_ldl_data_ra(env, addr + (3 << DF_WORD), ra); - pwd->w[3] = cpu_ldl_data_ra(env, addr + (2 << DF_WORD), ra); + /* + * Load 8 bytes at a time. Use little-endian load, then for + * big-endian target, we must then bswap the two words. + */ + d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); + d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); +#ifdef TARGET_WORDS_BIGENDIAN + d0 = bswap32x2(d0); + d1 = bswap32x2(d1); #endif + pwd->d[0] = d0; + pwd->d[1] = d1; } void helper_msa_ld_d(CPUMIPSState *env, uint32_t wd, @@ -8312,9 +8292,12 @@ void helper_msa_ld_d(CPUMIPSState *env, uint32_t wd, { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); uintptr_t ra = GETPC(); + uint64_t d0, d1; - pwd->d[0] = cpu_ldq_data_ra(env, addr + (0 << DF_DOUBLE), ra); - pwd->d[1] = cpu_ldq_data_ra(env, addr + (1 << DF_DOUBLE), ra); + d0 = cpu_ldq_data_ra(env, addr + 0, ra); + d1 = cpu_ldq_data_ra(env, addr + 8, ra); + pwd->d[0] = d0; + pwd->d[1] = d1; } #define MSA_PAGESPAN(x) \ @@ -8344,41 +8327,9 @@ void helper_msa_st_b(CPUMIPSState *env, uint32_t wd, ensure_writable_pages(env, addr, mmu_idx, ra); -#if !defined(HOST_WORDS_BIGENDIAN) - cpu_stb_data_ra(env, addr + (0 << DF_BYTE), pwd->b[0], ra); - cpu_stb_data_ra(env, addr + (1 << DF_BYTE), pwd->b[1], ra); - cpu_stb_data_ra(env, addr + (2 << DF_BYTE), pwd->b[2], ra); - cpu_stb_data_ra(env, addr + (3 << DF_BYTE), pwd->b[3], ra); - cpu_stb_data_ra(env, addr + (4 << DF_BYTE), pwd->b[4], ra); - cpu_stb_data_ra(env, addr + (5 << DF_BYTE), pwd->b[5], ra); - cpu_stb_data_ra(env, addr + (6 << DF_BYTE), pwd->b[6], ra); - cpu_stb_data_ra(env, addr + (7 << DF_BYTE), pwd->b[7], ra); - cpu_stb_data_ra(env, addr + (8 << DF_BYTE), pwd->b[8], ra); - cpu_stb_data_ra(env, addr + (9 << DF_BYTE), pwd->b[9], ra); - cpu_stb_data_ra(env, addr + (10 << DF_BYTE), pwd->b[10], ra); - cpu_stb_data_ra(env, addr + (11 << DF_BYTE), pwd->b[11], ra); - cpu_stb_data_ra(env, addr + (12 << DF_BYTE), pwd->b[12], ra); - cpu_stb_data_ra(env, addr + (13 << DF_BYTE), pwd->b[13], ra); - cpu_stb_data_ra(env, addr + (14 << DF_BYTE), pwd->b[14], ra); - cpu_stb_data_ra(env, addr + (15 << DF_BYTE), pwd->b[15], ra); -#else - cpu_stb_data_ra(env, addr + (7 << DF_BYTE), pwd->b[0], ra); - cpu_stb_data_ra(env, addr + (6 << DF_BYTE), pwd->b[1], ra); - cpu_stb_data_ra(env, addr + (5 << DF_BYTE), pwd->b[2], ra); - cpu_stb_data_ra(env, addr + (4 << DF_BYTE), pwd->b[3], ra); - cpu_stb_data_ra(env, addr + (3 << DF_BYTE), pwd->b[4], ra); - cpu_stb_data_ra(env, addr + (2 << DF_BYTE), pwd->b[5], ra); - cpu_stb_data_ra(env, addr + (1 << DF_BYTE), pwd->b[6], ra); - cpu_stb_data_ra(env, addr + (0 << DF_BYTE), pwd->b[7], ra); - cpu_stb_data_ra(env, addr + (15 << DF_BYTE), pwd->b[8], ra); - cpu_stb_data_ra(env, addr + (14 << DF_BYTE), pwd->b[9], ra); - cpu_stb_data_ra(env, addr + (13 << DF_BYTE), pwd->b[10], ra); - cpu_stb_data_ra(env, addr + (12 << DF_BYTE), pwd->b[11], ra); - cpu_stb_data_ra(env, addr + (11 << DF_BYTE), pwd->b[12], ra); - cpu_stb_data_ra(env, addr + (10 << DF_BYTE), pwd->b[13], ra); - cpu_stb_data_ra(env, addr + (9 << DF_BYTE), pwd->b[14], ra); - cpu_stb_data_ra(env, addr + (8 << DF_BYTE), pwd->b[15], ra); -#endif + /* Store 8 bytes at a time. Vector element ordering makes this LE. */ + cpu_stq_le_data_ra(env, addr + 0, pwd->d[0], ra); + cpu_stq_le_data_ra(env, addr + 0, pwd->d[1], ra); } void helper_msa_st_h(CPUMIPSState *env, uint32_t wd, @@ -8387,28 +8338,19 @@ void helper_msa_st_h(CPUMIPSState *env, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); int mmu_idx = cpu_mmu_index(env, false); uintptr_t ra = GETPC(); + uint64_t d0, d1; ensure_writable_pages(env, addr, mmu_idx, ra); -#if !defined(HOST_WORDS_BIGENDIAN) - cpu_stw_data_ra(env, addr + (0 << DF_HALF), pwd->h[0], ra); - cpu_stw_data_ra(env, addr + (1 << DF_HALF), pwd->h[1], ra); - cpu_stw_data_ra(env, addr + (2 << DF_HALF), pwd->h[2], ra); - cpu_stw_data_ra(env, addr + (3 << DF_HALF), pwd->h[3], ra); - cpu_stw_data_ra(env, addr + (4 << DF_HALF), pwd->h[4], ra); - cpu_stw_data_ra(env, addr + (5 << DF_HALF), pwd->h[5], ra); - cpu_stw_data_ra(env, addr + (6 << DF_HALF), pwd->h[6], ra); - cpu_stw_data_ra(env, addr + (7 << DF_HALF), pwd->h[7], ra); -#else - cpu_stw_data_ra(env, addr + (3 << DF_HALF), pwd->h[0], ra); - cpu_stw_data_ra(env, addr + (2 << DF_HALF), pwd->h[1], ra); - cpu_stw_data_ra(env, addr + (1 << DF_HALF), pwd->h[2], ra); - cpu_stw_data_ra(env, addr + (0 << DF_HALF), pwd->h[3], ra); - cpu_stw_data_ra(env, addr + (7 << DF_HALF), pwd->h[4], ra); - cpu_stw_data_ra(env, addr + (6 << DF_HALF), pwd->h[5], ra); - cpu_stw_data_ra(env, addr + (5 << DF_HALF), pwd->h[6], ra); - cpu_stw_data_ra(env, addr + (4 << DF_HALF), pwd->h[7], ra); + /* Store 8 bytes at a time. See helper_msa_ld_h. */ + d0 = pwd->d[0]; + d1 = pwd->d[1]; +#ifdef TARGET_WORDS_BIGENDIAN + d0 = bswap16x4(d0); + d1 = bswap16x4(d1); #endif + cpu_stq_le_data_ra(env, addr + 0, d0, ra); + cpu_stq_le_data_ra(env, addr + 8, d1, ra); } void helper_msa_st_w(CPUMIPSState *env, uint32_t wd, @@ -8417,20 +8359,19 @@ void helper_msa_st_w(CPUMIPSState *env, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); int mmu_idx = cpu_mmu_index(env, false); uintptr_t ra = GETPC(); + uint64_t d0, d1; ensure_writable_pages(env, addr, mmu_idx, ra); -#if !defined(HOST_WORDS_BIGENDIAN) - cpu_stl_data_ra(env, addr + (0 << DF_WORD), pwd->w[0], ra); - cpu_stl_data_ra(env, addr + (1 << DF_WORD), pwd->w[1], ra); - cpu_stl_data_ra(env, addr + (2 << DF_WORD), pwd->w[2], ra); - cpu_stl_data_ra(env, addr + (3 << DF_WORD), pwd->w[3], ra); -#else - cpu_stl_data_ra(env, addr + (1 << DF_WORD), pwd->w[0], ra); - cpu_stl_data_ra(env, addr + (0 << DF_WORD), pwd->w[1], ra); - cpu_stl_data_ra(env, addr + (3 << DF_WORD), pwd->w[2], ra); - cpu_stl_data_ra(env, addr + (2 << DF_WORD), pwd->w[3], ra); + /* Store 8 bytes at a time. See helper_msa_ld_w. */ + d0 = pwd->d[0]; + d1 = pwd->d[1]; +#ifdef TARGET_WORDS_BIGENDIAN + d0 = bswap32x2(d0); + d1 = bswap32x2(d1); #endif + cpu_stq_le_data_ra(env, addr + 0, d0, ra); + cpu_stq_le_data_ra(env, addr + 8, d1, ra); } void helper_msa_st_d(CPUMIPSState *env, uint32_t wd, @@ -8442,6 +8383,6 @@ void helper_msa_st_d(CPUMIPSState *env, uint32_t wd, ensure_writable_pages(env, addr, mmu_idx, GETPC()); - cpu_stq_data_ra(env, addr + (0 << DF_DOUBLE), pwd->d[0], ra); - cpu_stq_data_ra(env, addr + (1 << DF_DOUBLE), pwd->d[1], ra); + cpu_stq_data_ra(env, addr + 0, pwd->d[0], ra); + cpu_stq_data_ra(env, addr + 8, pwd->d[1], ra); } From bfe5b847af66be76c325f974017412f42e1dd62c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 10:32:40 -1000 Subject: [PATCH 0477/1334] target/s390x: Use cpu_*_mmu instead of helper_*_mmu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The helper_*_mmu functions were the only thing available when this code was written. This could have been adjusted when we added cpu_*_mmuidx_ra, but now we can most easily use the newest set of interfaces. Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/tcg/mem_helper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 251d4acf55..17e3f83641 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -249,13 +249,13 @@ static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, * page. This is especially relevant to speed up TLB_NOTDIRTY. */ g_assert(size > 0); - helper_ret_stb_mmu(env, vaddr, byte, oi, ra); + cpu_stb_mmu(env, vaddr, byte, oi, ra); haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx); if (likely(haddr)) { memset(haddr + 1, byte, size - 1); } else { for (i = 1; i < size; i++) { - helper_ret_stb_mmu(env, vaddr + i, byte, oi, ra); + cpu_stb_mmu(env, vaddr + i, byte, oi, ra); } } } @@ -291,7 +291,7 @@ static uint8_t do_access_get_byte(CPUS390XState *env, vaddr vaddr, char **haddr, * Do a single access and test if we can then get access to the * page. This is especially relevant to speed up TLB_NOTDIRTY. */ - byte = helper_ret_ldub_mmu(env, vaddr + offset, oi, ra); + byte = cpu_ldb_mmu(env, vaddr + offset, oi, ra); *haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_LOAD, mmu_idx); return byte; #endif @@ -325,7 +325,7 @@ static void do_access_set_byte(CPUS390XState *env, vaddr vaddr, char **haddr, * Do a single access and test if we can then get access to the * page. This is especially relevant to speed up TLB_NOTDIRTY. */ - helper_ret_stb_mmu(env, vaddr + offset, byte, oi, ra); + cpu_stb_mmu(env, vaddr + offset, byte, oi, ra); *haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx); #endif } From a8f84958d09bfca0a775d8000bf81b0b66ca8066 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 10:48:09 -1000 Subject: [PATCH 0478/1334] target/sparc: Use cpu_*_mmu instead of helper_*_mmu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The helper_*_mmu functions were the only thing available when this code was written. This could have been adjusted when we added cpu_*_mmuidx_ra, but now we can most easily use the newest set of interfaces. Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/ldst_helper.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index abe2889d27..bbf3601cb1 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -1333,27 +1333,27 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, oi = make_memop_idx(memop, idx); switch (size) { case 1: - ret = helper_ret_ldub_mmu(env, addr, oi, GETPC()); + ret = cpu_ldb_mmu(env, addr, oi, GETPC()); break; case 2: if (asi & 8) { - ret = helper_le_lduw_mmu(env, addr, oi, GETPC()); + ret = cpu_ldw_le_mmu(env, addr, oi, GETPC()); } else { - ret = helper_be_lduw_mmu(env, addr, oi, GETPC()); + ret = cpu_ldw_be_mmu(env, addr, oi, GETPC()); } break; case 4: if (asi & 8) { - ret = helper_le_ldul_mmu(env, addr, oi, GETPC()); + ret = cpu_ldl_le_mmu(env, addr, oi, GETPC()); } else { - ret = helper_be_ldul_mmu(env, addr, oi, GETPC()); + ret = cpu_ldl_be_mmu(env, addr, oi, GETPC()); } break; case 8: if (asi & 8) { - ret = helper_le_ldq_mmu(env, addr, oi, GETPC()); + ret = cpu_ldq_le_mmu(env, addr, oi, GETPC()); } else { - ret = helper_be_ldq_mmu(env, addr, oi, GETPC()); + ret = cpu_ldq_be_mmu(env, addr, oi, GETPC()); } break; default: From 1a2eaf9e3897b0c51ef65e676c9bbd125506dafd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 11:00:20 -1000 Subject: [PATCH 0479/1334] target/arm: Use cpu_*_mmu instead of helper_*_mmu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The helper_*_mmu functions were the only thing available when this code was written. This could have been adjusted when we added cpu_*_mmuidx_ra, but now we can most easily use the newest set of interfaces. Cc: qemu-arm@nongnu.org Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/helper-a64.c | 52 +++++++---------------------------------- target/arm/m_helper.c | 6 ++--- 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index b110c57956..5ae2ecb0f3 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -512,37 +512,19 @@ uint64_t HELPER(paired_cmpxchg64_le)(CPUARMState *env, uint64_t addr, uintptr_t ra = GETPC(); uint64_t o0, o1; bool success; - -#ifdef CONFIG_USER_ONLY - /* ??? Enforce alignment. */ - uint64_t *haddr = g2h(env_cpu(env), addr); - - set_helper_retaddr(ra); - o0 = ldq_le_p(haddr + 0); - o1 = ldq_le_p(haddr + 1); - oldv = int128_make128(o0, o1); - - success = int128_eq(oldv, cmpv); - if (success) { - stq_le_p(haddr + 0, int128_getlo(newv)); - stq_le_p(haddr + 1, int128_gethi(newv)); - } - clear_helper_retaddr(); -#else int mem_idx = cpu_mmu_index(env, false); MemOpIdx oi0 = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); MemOpIdx oi1 = make_memop_idx(MO_LEQ, mem_idx); - o0 = helper_le_ldq_mmu(env, addr + 0, oi0, ra); - o1 = helper_le_ldq_mmu(env, addr + 8, oi1, ra); + o0 = cpu_ldq_le_mmu(env, addr + 0, oi0, ra); + o1 = cpu_ldq_le_mmu(env, addr + 8, oi1, ra); oldv = int128_make128(o0, o1); success = int128_eq(oldv, cmpv); if (success) { - helper_le_stq_mmu(env, addr + 0, int128_getlo(newv), oi1, ra); - helper_le_stq_mmu(env, addr + 8, int128_gethi(newv), oi1, ra); + cpu_stq_le_mmu(env, addr + 0, int128_getlo(newv), oi1, ra); + cpu_stq_le_mmu(env, addr + 8, int128_gethi(newv), oi1, ra); } -#endif return !success; } @@ -582,37 +564,19 @@ uint64_t HELPER(paired_cmpxchg64_be)(CPUARMState *env, uint64_t addr, uintptr_t ra = GETPC(); uint64_t o0, o1; bool success; - -#ifdef CONFIG_USER_ONLY - /* ??? Enforce alignment. */ - uint64_t *haddr = g2h(env_cpu(env), addr); - - set_helper_retaddr(ra); - o1 = ldq_be_p(haddr + 0); - o0 = ldq_be_p(haddr + 1); - oldv = int128_make128(o0, o1); - - success = int128_eq(oldv, cmpv); - if (success) { - stq_be_p(haddr + 0, int128_gethi(newv)); - stq_be_p(haddr + 1, int128_getlo(newv)); - } - clear_helper_retaddr(); -#else int mem_idx = cpu_mmu_index(env, false); MemOpIdx oi0 = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); MemOpIdx oi1 = make_memop_idx(MO_BEQ, mem_idx); - o1 = helper_be_ldq_mmu(env, addr + 0, oi0, ra); - o0 = helper_be_ldq_mmu(env, addr + 8, oi1, ra); + o1 = cpu_ldq_be_mmu(env, addr + 0, oi0, ra); + o0 = cpu_ldq_be_mmu(env, addr + 8, oi1, ra); oldv = int128_make128(o0, o1); success = int128_eq(oldv, cmpv); if (success) { - helper_be_stq_mmu(env, addr + 0, int128_gethi(newv), oi1, ra); - helper_be_stq_mmu(env, addr + 8, int128_getlo(newv), oi1, ra); + cpu_stq_be_mmu(env, addr + 0, int128_gethi(newv), oi1, ra); + cpu_stq_be_mmu(env, addr + 8, int128_getlo(newv), oi1, ra); } -#endif return !success; } diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index 62aa12c9d8..2c9922dc29 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -1947,9 +1947,9 @@ static bool do_v7m_function_return(ARMCPU *cpu) * do them as secure, so work out what MMU index that is. */ mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true); - oi = make_memop_idx(MO_LE, arm_to_core_mmu_idx(mmu_idx)); - newpc = helper_le_ldul_mmu(env, frameptr, oi, 0); - newpsr = helper_le_ldul_mmu(env, frameptr + 4, oi, 0); + oi = make_memop_idx(MO_LEUL, arm_to_core_mmu_idx(mmu_idx)); + newpc = cpu_ldl_le_mmu(env, frameptr, oi, 0); + newpsr = cpu_ldl_le_mmu(env, frameptr + 4, oi, 0); /* Consistency checks on new IPSR */ newpsr_exc = newpsr & XPSR_EXCP; From d2ba80265739d1699dd33b93ed7db8d46e875ab6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 11:10:22 -1000 Subject: [PATCH 0480/1334] tcg: Move helper_*_mmu decls to tcg/tcg-ldst.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions have been replaced by cpu_*_mmu as the most proper interface to use from target code. Hide these declarations from code that should not use them. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 1 + include/tcg/tcg-ldst.h | 74 ++++++++++++++++++++++++++++++++++++++++++ include/tcg/tcg.h | 71 ---------------------------------------- tcg/tcg.c | 1 + tcg/tci.c | 1 + 5 files changed, 77 insertions(+), 71 deletions(-) create mode 100644 include/tcg/tcg-ldst.h diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b350cafa3d..b69a953447 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -39,6 +39,7 @@ #ifdef CONFIG_PLUGIN #include "qemu/plugin-memory.h" #endif +#include "tcg/tcg-ldst.h" /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ diff --git a/include/tcg/tcg-ldst.h b/include/tcg/tcg-ldst.h new file mode 100644 index 0000000000..8c86365611 --- /dev/null +++ b/include/tcg/tcg-ldst.h @@ -0,0 +1,74 @@ +/* + * Memory helpers that will be used by TCG generated code. + * + * Copyright (c) 2008 Fabrice Bellard + * + * 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. + */ + +#ifndef TCG_LDST_H +#define TCG_LDST_H 1 + +#ifdef CONFIG_SOFTMMU + +/* Value zero-extended to tcg register size. */ +tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); + +/* Value sign-extended to tcg register size. */ +tcg_target_ulong helper_ret_ldsb_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_le_ldsw_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_le_ldsl_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_be_ldsw_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr, + MemOpIdx oi, uintptr_t retaddr); + +void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr); + +#endif /* CONFIG_SOFTMMU */ +#endif /* TCG_LDST_H */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 83e38487cf..7069a401f1 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -1240,77 +1240,6 @@ uint64_t dup_const(unsigned vece, uint64_t c); : (target_long)dup_const(VECE, C)) #endif -/* - * Memory helpers that will be used by TCG generated code. - */ -#ifdef CONFIG_SOFTMMU -/* Value zero-extended to tcg register size. */ -tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); - -/* Value sign-extended to tcg register size. */ -tcg_target_ulong helper_ret_ldsb_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_le_ldsw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_le_ldsl_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_be_ldsw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); - -void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr); - -/* Temporary aliases until backends are converted. */ -#ifdef TARGET_WORDS_BIGENDIAN -# define helper_ret_ldsw_mmu helper_be_ldsw_mmu -# define helper_ret_lduw_mmu helper_be_lduw_mmu -# define helper_ret_ldsl_mmu helper_be_ldsl_mmu -# define helper_ret_ldul_mmu helper_be_ldul_mmu -# define helper_ret_ldl_mmu helper_be_ldul_mmu -# define helper_ret_ldq_mmu helper_be_ldq_mmu -# define helper_ret_stw_mmu helper_be_stw_mmu -# define helper_ret_stl_mmu helper_be_stl_mmu -# define helper_ret_stq_mmu helper_be_stq_mmu -#else -# define helper_ret_ldsw_mmu helper_le_ldsw_mmu -# define helper_ret_lduw_mmu helper_le_lduw_mmu -# define helper_ret_ldsl_mmu helper_le_ldsl_mmu -# define helper_ret_ldul_mmu helper_le_ldul_mmu -# define helper_ret_ldl_mmu helper_le_ldul_mmu -# define helper_ret_ldq_mmu helper_le_ldq_mmu -# define helper_ret_stw_mmu helper_le_stw_mmu -# define helper_ret_stl_mmu helper_le_stl_mmu -# define helper_ret_stq_mmu helper_le_stq_mmu -#endif -#endif /* CONFIG_SOFTMMU */ - #ifdef CONFIG_DEBUG_TCG void tcg_assert_listed_vecop(TCGOpcode); #else diff --git a/tcg/tcg.c b/tcg/tcg.c index 658be0c6b6..024a22cf39 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -58,6 +58,7 @@ #include "elf.h" #include "exec/log.h" +#include "tcg/tcg-ldst.h" #include "tcg-internal.h" #ifdef CONFIG_TCG_INTERPRETER diff --git a/tcg/tci.c b/tcg/tci.c index 5c08dc0a9a..e76087ccac 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -22,6 +22,7 @@ #include "tcg/tcg.h" /* MAX_OPC_PARAM_IARGS */ #include "exec/cpu_ldst.h" #include "tcg/tcg-op.h" +#include "tcg/tcg-ldst.h" #include "qemu/compiler.h" #include From 76e366e728549b3324cc2dee6745d6a4f1af18e6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Aug 2021 08:26:30 +0300 Subject: [PATCH 0481/1334] tcg: Canonicalize alignment flags in MemOp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having observed e.g. al8+leq in dumps, canonicalize to al+leq. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg-op.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index b1cfd36f29..61b492d89f 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2765,7 +2765,12 @@ void tcg_gen_lookup_and_goto_ptr(void) static inline MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) { /* Trigger the asserts within as early as possible. */ - (void)get_alignment_bits(op); + unsigned a_bits = get_alignment_bits(op); + + /* Prefer MO_ALIGN+MO_XX over MO_ALIGN_XX+MO_XX */ + if (a_bits == (op & MO_SIZE)) { + op = (op & ~MO_AMASK) | MO_ALIGN; + } switch (op & MO_SIZE) { case MO_8: From 452635318b78b98f0ef2586463334565674cb9e6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 30 Jul 2021 19:56:06 -1000 Subject: [PATCH 0482/1334] target/alpha: Reorg fp memory operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass in the context to each mini-helper, instead of an incorrectly named "flags". Separate gen_load_fp and gen_store_fp, away from the integer helpers. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/alpha/translate.c | 83 +++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index b034206688..bfdd485508 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -267,30 +267,47 @@ static inline DisasJumpType gen_invalid(DisasContext *ctx) return gen_excp(ctx, EXCP_OPCDEC, 0); } -static inline void gen_qemu_ldf(TCGv t0, TCGv t1, int flags) +static void gen_ldf(DisasContext *ctx, TCGv dest, TCGv addr) { TCGv_i32 tmp32 = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(tmp32, t1, flags, MO_LEUL); - gen_helper_memory_to_f(t0, tmp32); + tcg_gen_qemu_ld_i32(tmp32, addr, ctx->mem_idx, MO_LEUL); + gen_helper_memory_to_f(dest, tmp32); tcg_temp_free_i32(tmp32); } -static inline void gen_qemu_ldg(TCGv t0, TCGv t1, int flags) +static void gen_ldg(DisasContext *ctx, TCGv dest, TCGv addr) { TCGv tmp = tcg_temp_new(); - tcg_gen_qemu_ld_i64(tmp, t1, flags, MO_LEQ); - gen_helper_memory_to_g(t0, tmp); + tcg_gen_qemu_ld_i64(tmp, addr, ctx->mem_idx, MO_LEQ); + gen_helper_memory_to_g(dest, tmp); tcg_temp_free(tmp); } -static inline void gen_qemu_lds(TCGv t0, TCGv t1, int flags) +static void gen_lds(DisasContext *ctx, TCGv dest, TCGv addr) { TCGv_i32 tmp32 = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(tmp32, t1, flags, MO_LEUL); - gen_helper_memory_to_s(t0, tmp32); + tcg_gen_qemu_ld_i32(tmp32, addr, ctx->mem_idx, MO_LEUL); + gen_helper_memory_to_s(dest, tmp32); tcg_temp_free_i32(tmp32); } +static void gen_ldt(DisasContext *ctx, TCGv dest, TCGv addr) +{ + tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_LEQ); +} + +static void gen_load_fp(DisasContext *ctx, int ra, int rb, int32_t disp16, + void (*func)(DisasContext *, TCGv, TCGv)) +{ + /* Loads to $f31 are prefetches, which we can treat as nops. */ + if (likely(ra != 31)) { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i64(addr, load_gpr(ctx, rb), disp16); + func(ctx, cpu_fir[ra], addr); + tcg_temp_free(addr); + } +} + static inline void gen_qemu_ldl_l(TCGv t0, TCGv t1, int flags) { tcg_gen_qemu_ld_i64(t0, t1, flags, MO_LESL); @@ -338,30 +355,44 @@ static inline void gen_load_mem(DisasContext *ctx, tcg_temp_free(tmp); } -static inline void gen_qemu_stf(TCGv t0, TCGv t1, int flags) +static void gen_stf(DisasContext *ctx, TCGv src, TCGv addr) { TCGv_i32 tmp32 = tcg_temp_new_i32(); - gen_helper_f_to_memory(tmp32, t0); - tcg_gen_qemu_st_i32(tmp32, t1, flags, MO_LEUL); + gen_helper_f_to_memory(tmp32, addr); + tcg_gen_qemu_st_i32(tmp32, addr, ctx->mem_idx, MO_LEUL); tcg_temp_free_i32(tmp32); } -static inline void gen_qemu_stg(TCGv t0, TCGv t1, int flags) +static void gen_stg(DisasContext *ctx, TCGv src, TCGv addr) { TCGv tmp = tcg_temp_new(); - gen_helper_g_to_memory(tmp, t0); - tcg_gen_qemu_st_i64(tmp, t1, flags, MO_LEQ); + gen_helper_g_to_memory(tmp, src); + tcg_gen_qemu_st_i64(tmp, addr, ctx->mem_idx, MO_LEQ); tcg_temp_free(tmp); } -static inline void gen_qemu_sts(TCGv t0, TCGv t1, int flags) +static void gen_sts(DisasContext *ctx, TCGv src, TCGv addr) { TCGv_i32 tmp32 = tcg_temp_new_i32(); - gen_helper_s_to_memory(tmp32, t0); - tcg_gen_qemu_st_i32(tmp32, t1, flags, MO_LEUL); + gen_helper_s_to_memory(tmp32, src); + tcg_gen_qemu_st_i32(tmp32, addr, ctx->mem_idx, MO_LEUL); tcg_temp_free_i32(tmp32); } +static void gen_stt(DisasContext *ctx, TCGv src, TCGv addr) +{ + tcg_gen_qemu_st_i64(src, addr, ctx->mem_idx, MO_LEQ); +} + +static void gen_store_fp(DisasContext *ctx, int ra, int rb, int32_t disp16, + void (*func)(DisasContext *, TCGv, TCGv)) +{ + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i64(addr, load_gpr(ctx, rb), disp16); + func(ctx, load_fpr(ctx, ra), addr); + tcg_temp_free(addr); +} + static inline void gen_store_mem(DisasContext *ctx, void (*tcg_gen_qemu_store)(TCGv t0, TCGv t1, int flags), @@ -2776,42 +2807,42 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0x20: /* LDF */ REQUIRE_FEN; - gen_load_mem(ctx, &gen_qemu_ldf, ra, rb, disp16, 1, 0); + gen_load_fp(ctx, ra, rb, disp16, gen_ldf); break; case 0x21: /* LDG */ REQUIRE_FEN; - gen_load_mem(ctx, &gen_qemu_ldg, ra, rb, disp16, 1, 0); + gen_load_fp(ctx, ra, rb, disp16, gen_ldg); break; case 0x22: /* LDS */ REQUIRE_FEN; - gen_load_mem(ctx, &gen_qemu_lds, ra, rb, disp16, 1, 0); + gen_load_fp(ctx, ra, rb, disp16, gen_lds); break; case 0x23: /* LDT */ REQUIRE_FEN; - gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 1, 0); + gen_load_fp(ctx, ra, rb, disp16, gen_ldt); break; case 0x24: /* STF */ REQUIRE_FEN; - gen_store_mem(ctx, &gen_qemu_stf, ra, rb, disp16, 1, 0); + gen_store_fp(ctx, ra, rb, disp16, gen_stf); break; case 0x25: /* STG */ REQUIRE_FEN; - gen_store_mem(ctx, &gen_qemu_stg, ra, rb, disp16, 1, 0); + gen_store_fp(ctx, ra, rb, disp16, gen_stg); break; case 0x26: /* STS */ REQUIRE_FEN; - gen_store_mem(ctx, &gen_qemu_sts, ra, rb, disp16, 1, 0); + gen_store_fp(ctx, ra, rb, disp16, gen_sts); break; case 0x27: /* STT */ REQUIRE_FEN; - gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 1, 0); + gen_store_fp(ctx, ra, rb, disp16, gen_stt); break; case 0x28: /* LDL */ From 5ffcb33426aaa2906daea2eee7a5195662a6580f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 30 Jul 2021 20:15:31 -1000 Subject: [PATCH 0483/1334] target/alpha: Reorg integer memory operations Pass in the MemOp instead of a callback. Drop the fp argument; add a locked argument. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/alpha/translate.c | 104 +++++++++++++++------------------------ 1 file changed, 40 insertions(+), 64 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index bfdd485508..0eee3a1bcc 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -308,27 +308,10 @@ static void gen_load_fp(DisasContext *ctx, int ra, int rb, int32_t disp16, } } -static inline void gen_qemu_ldl_l(TCGv t0, TCGv t1, int flags) +static void gen_load_int(DisasContext *ctx, int ra, int rb, int32_t disp16, + MemOp op, bool clear, bool locked) { - tcg_gen_qemu_ld_i64(t0, t1, flags, MO_LESL); - tcg_gen_mov_i64(cpu_lock_addr, t1); - tcg_gen_mov_i64(cpu_lock_value, t0); -} - -static inline void gen_qemu_ldq_l(TCGv t0, TCGv t1, int flags) -{ - tcg_gen_qemu_ld_i64(t0, t1, flags, MO_LEQ); - tcg_gen_mov_i64(cpu_lock_addr, t1); - tcg_gen_mov_i64(cpu_lock_value, t0); -} - -static inline void gen_load_mem(DisasContext *ctx, - void (*tcg_gen_qemu_load)(TCGv t0, TCGv t1, - int flags), - int ra, int rb, int32_t disp16, bool fp, - bool clear) -{ - TCGv tmp, addr, va; + TCGv addr, dest; /* LDQ_U with ra $31 is UNOP. Other various loads are forms of prefetches, which we can treat as nops. No worries about @@ -337,22 +320,20 @@ static inline void gen_load_mem(DisasContext *ctx, return; } - tmp = tcg_temp_new(); - addr = load_gpr(ctx, rb); - - if (disp16) { - tcg_gen_addi_i64(tmp, addr, disp16); - addr = tmp; - } + addr = tcg_temp_new(); + tcg_gen_addi_i64(addr, load_gpr(ctx, rb), disp16); if (clear) { - tcg_gen_andi_i64(tmp, addr, ~0x7); - addr = tmp; + tcg_gen_andi_i64(addr, addr, ~0x7); } - va = (fp ? cpu_fir[ra] : ctx->ir[ra]); - tcg_gen_qemu_load(va, addr, ctx->mem_idx); + dest = ctx->ir[ra]; + tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, op); - tcg_temp_free(tmp); + if (locked) { + tcg_gen_mov_i64(cpu_lock_addr, addr); + tcg_gen_mov_i64(cpu_lock_value, dest); + } + tcg_temp_free(addr); } static void gen_stf(DisasContext *ctx, TCGv src, TCGv addr) @@ -393,30 +374,21 @@ static void gen_store_fp(DisasContext *ctx, int ra, int rb, int32_t disp16, tcg_temp_free(addr); } -static inline void gen_store_mem(DisasContext *ctx, - void (*tcg_gen_qemu_store)(TCGv t0, TCGv t1, - int flags), - int ra, int rb, int32_t disp16, bool fp, - bool clear) +static void gen_store_int(DisasContext *ctx, int ra, int rb, int32_t disp16, + MemOp op, bool clear) { - TCGv tmp, addr, va; + TCGv addr, src; - tmp = tcg_temp_new(); - addr = load_gpr(ctx, rb); - - if (disp16) { - tcg_gen_addi_i64(tmp, addr, disp16); - addr = tmp; - } + addr = tcg_temp_new(); + tcg_gen_addi_i64(addr, load_gpr(ctx, rb), disp16); if (clear) { - tcg_gen_andi_i64(tmp, addr, ~0x7); - addr = tmp; + tcg_gen_andi_i64(addr, addr, ~0x7); } - va = (fp ? load_fpr(ctx, ra) : load_gpr(ctx, ra)); - tcg_gen_qemu_store(va, addr, ctx->mem_idx); + src = load_gpr(ctx, ra); + tcg_gen_qemu_st_i64(src, addr, ctx->mem_idx, op); - tcg_temp_free(tmp); + tcg_temp_free(addr); } static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, @@ -1511,30 +1483,30 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0x0A: /* LDBU */ REQUIRE_AMASK(BWX); - gen_load_mem(ctx, &tcg_gen_qemu_ld8u, ra, rb, disp16, 0, 0); + gen_load_int(ctx, ra, rb, disp16, MO_UB, 0, 0); break; case 0x0B: /* LDQ_U */ - gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 1); + gen_load_int(ctx, ra, rb, disp16, MO_LEQ, 1, 0); break; case 0x0C: /* LDWU */ REQUIRE_AMASK(BWX); - gen_load_mem(ctx, &tcg_gen_qemu_ld16u, ra, rb, disp16, 0, 0); + gen_load_int(ctx, ra, rb, disp16, MO_LEUW, 0, 0); break; case 0x0D: /* STW */ REQUIRE_AMASK(BWX); - gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0); + gen_store_int(ctx, ra, rb, disp16, MO_LEUW, 0); break; case 0x0E: /* STB */ REQUIRE_AMASK(BWX); - gen_store_mem(ctx, &tcg_gen_qemu_st8, ra, rb, disp16, 0, 0); + gen_store_int(ctx, ra, rb, disp16, MO_UB, 0); break; case 0x0F: /* STQ_U */ - gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 1); + gen_store_int(ctx, ra, rb, disp16, MO_LEQ, 1); break; case 0x10: @@ -2489,11 +2461,15 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) break; case 0x2: /* Longword physical access with lock (hw_ldl_l/p) */ - gen_qemu_ldl_l(va, addr, MMU_PHYS_IDX); + tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LESL); + tcg_gen_mov_i64(cpu_lock_addr, addr); + tcg_gen_mov_i64(cpu_lock_value, va); break; case 0x3: /* Quadword physical access with lock (hw_ldq_l/p) */ - gen_qemu_ldq_l(va, addr, MMU_PHYS_IDX); + tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LEQ); + tcg_gen_mov_i64(cpu_lock_addr, addr); + tcg_gen_mov_i64(cpu_lock_value, va); break; case 0x4: /* Longword virtual PTE fetch (hw_ldl/v) */ @@ -2846,27 +2822,27 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) break; case 0x28: /* LDL */ - gen_load_mem(ctx, &tcg_gen_qemu_ld32s, ra, rb, disp16, 0, 0); + gen_load_int(ctx, ra, rb, disp16, MO_LESL, 0, 0); break; case 0x29: /* LDQ */ - gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 0); + gen_load_int(ctx, ra, rb, disp16, MO_LEQ, 0, 0); break; case 0x2A: /* LDL_L */ - gen_load_mem(ctx, &gen_qemu_ldl_l, ra, rb, disp16, 0, 0); + gen_load_int(ctx, ra, rb, disp16, MO_LESL, 0, 1); break; case 0x2B: /* LDQ_L */ - gen_load_mem(ctx, &gen_qemu_ldq_l, ra, rb, disp16, 0, 0); + gen_load_int(ctx, ra, rb, disp16, MO_LEQ, 0, 1); break; case 0x2C: /* STL */ - gen_store_mem(ctx, &tcg_gen_qemu_st32, ra, rb, disp16, 0, 0); + gen_store_int(ctx, ra, rb, disp16, MO_LEUL, 0); break; case 0x2D: /* STQ */ - gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 0); + gen_store_int(ctx, ra, rb, disp16, MO_LEQ, 0); break; case 0x2E: /* STL_C */ From 962fde57b7d573281619cb2b7068d570470ef833 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 27 Aug 2021 14:09:01 +0200 Subject: [PATCH 0484/1334] dtc: Update to version 1.6.1 The dtc submodule is currently pointing to non-release commit. It's nicer if submodules point to release versions instead and since dtc 1.6.1 is available now, let's update to that version. Message-Id: <20210827120901.150276-4-thuth@redhat.com> Acked-by: Greg Kurz Acked-by: David Gibson Signed-off-by: Thomas Huth --- dtc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtc b/dtc index 85e5d83984..b6910bec11 160000 --- a/dtc +++ b/dtc @@ -1 +1 @@ -Subproject commit 85e5d839847af54efab170f2b1331b2a6421e647 +Subproject commit b6910bec11614980a21e46fbccc35934b671bd81 From 381123ddae754b68d55cd872e719a2ba69fe7f4a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:06:07 +0200 Subject: [PATCH 0485/1334] configure: remove --oss-lib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OSS is a kernel API, so the option should not be needed. The library is used on NetBSD, where OSS is emulated, so keep the variable. Cc: Gerd Hoffman Cc: Volker Rümelin Reviewed-by: Marc-André Lureau Reviewed-by: Thomas Huth Message-Id: <20211007130630.632028-2-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 3 --- 1 file changed, 3 deletions(-) diff --git a/configure b/configure index e2750810e2..c280c0e4b5 100755 --- a/configure +++ b/configure @@ -1007,8 +1007,6 @@ for opt do ;; --enable-gettext) gettext="enabled" ;; - --oss-lib=*) oss_lib="$optarg" - ;; --audio-drv-list=*) audio_drv_list="$optarg" ;; --block-drv-rw-whitelist=*|--block-drv-whitelist=*) block_drv_rw_whitelist=$(echo "$optarg" | sed -e 's/,/ /g') @@ -1817,7 +1815,6 @@ Advanced options (experts only): --disable-slirp disable SLIRP userspace network connectivity --enable-tcg-interpreter enable TCI (TCG with bytecode interpreter, experimental and slow) --enable-malloc-trim enable libc malloc_trim() for memory optimization - --oss-lib path to OSS library --cpu=CPU Build for host CPU [$cpu] --with-coroutine=BACKEND coroutine backend. Supported options: ucontext, sigaltstack, windows From 7e1fbe7963f2b9bb44d6aa5c1ef793894212190a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:06:08 +0200 Subject: [PATCH 0486/1334] audio: remove CONFIG_AUDIO_WIN_INT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ever since winwaveaudio was removed in 2015, CONFIG_AUDIO_WIN_INT is only set if dsound is in use, so use CONFIG_AUDIO_DSOUND directly. Cc: Gerd Hoffman Cc: Volker Rümelin Reviewed-by: Marc-André Lureau Message-Id: <20211007130630.632028-3-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- audio/meson.build | 4 ++-- configure | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/audio/meson.build b/audio/meson.build index 7d53b0f920..9a95c58f18 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -8,8 +8,8 @@ softmmu_ss.add(files( )) softmmu_ss.add(when: [coreaudio, 'CONFIG_AUDIO_COREAUDIO'], if_true: files('coreaudio.c')) -softmmu_ss.add(when: [dsound, 'CONFIG_AUDIO_DSOUND'], if_true: files('dsoundaudio.c')) -softmmu_ss.add(when: ['CONFIG_AUDIO_WIN_INT'], if_true: files('audio_win_int.c')) +softmmu_ss.add(when: [dsound, 'CONFIG_AUDIO_DSOUND'], + if_true: files('dsoundaudio.c', 'audio_win_int.c')) audio_modules = {} foreach m : [ diff --git a/configure b/configure index c280c0e4b5..174fa84b60 100755 --- a/configure +++ b/configure @@ -245,7 +245,6 @@ block_drv_rw_whitelist="" block_drv_ro_whitelist="" block_drv_whitelist_tools="no" host_cc="cc" -audio_win_int="" libs_qga="" debug_info="yes" lto="false" @@ -3077,7 +3076,6 @@ for drv in $audio_drv_list; do dsound) dsound_libs="-lole32 -ldxguid" - audio_win_int="yes" ;; oss) @@ -4562,9 +4560,6 @@ if test "$libjack" = "yes" ; then echo "CONFIG_LIBJACK=y" >> $config_host_mak fi echo "JACK_LIBS=$jack_libs" >> $config_host_mak -if test "$audio_win_int" = "yes" ; then - echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak -fi echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak if test "$block_drv_whitelist_tools" = "yes" ; then From 87430d5b13fdef0768cdb55352652ff78095e761 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:06:09 +0200 Subject: [PATCH 0487/1334] configure, meson: move audio driver detection to Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This brings a change that makes audio drivers more similar to all other modules. All drivers are built by default, while --audio-drv-list only governs the default choice of the audio driver. Meson options are added to disable the drivers, and the next patches will fix the help messages and command line options, and especially make the non-default drivers available via -audiodev. Cc: Gerd Hoffman Cc: Volker Rümelin Reviewed-by: Marc-André Lureau Message-Id: <20211007130630.632028-4-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- audio/meson.build | 23 +++--- configure | 186 +--------------------------------------------- meson.build | 137 +++++++++++++++++++++++++++++----- meson_options.txt | 18 ++++- 4 files changed, 148 insertions(+), 216 deletions(-) diff --git a/audio/meson.build b/audio/meson.build index 9a95c58f18..462533bb8c 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -7,23 +7,22 @@ softmmu_ss.add(files( 'wavcapture.c', )) -softmmu_ss.add(when: [coreaudio, 'CONFIG_AUDIO_COREAUDIO'], if_true: files('coreaudio.c')) -softmmu_ss.add(when: [dsound, 'CONFIG_AUDIO_DSOUND'], - if_true: files('dsoundaudio.c', 'audio_win_int.c')) +softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.c')) +softmmu_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c')) audio_modules = {} foreach m : [ - ['CONFIG_AUDIO_ALSA', 'alsa', alsa, 'alsaaudio.c'], - ['CONFIG_AUDIO_OSS', 'oss', oss, 'ossaudio.c'], - ['CONFIG_AUDIO_PA', 'pa', pulse, 'paaudio.c'], - ['CONFIG_AUDIO_SDL', 'sdl', sdl, 'sdlaudio.c'], - ['CONFIG_AUDIO_JACK', 'jack', jack, 'jackaudio.c'], - ['CONFIG_SPICE', 'spice', spice, 'spiceaudio.c'] + ['alsa', alsa, files('alsaaudio.c')], + ['oss', oss, files('ossaudio.c')], + ['pa', pulse, files('paaudio.c')], + ['sdl', sdl, files('sdlaudio.c')], + ['jack', jack, files('jackaudio.c')], + ['spice', spice, files('spiceaudio.c')] ] - if config_host.has_key(m[0]) + if m[1].found() module_ss = ss.source_set() - module_ss.add(when: m[2], if_true: files(m[3])) - audio_modules += {m[1] : module_ss} + module_ss.add(m[1], m[2]) + audio_modules += {m[0] : module_ss} endif endforeach diff --git a/configure b/configure index 174fa84b60..b9a457a02f 100755 --- a/configure +++ b/configure @@ -240,7 +240,7 @@ interp_prefix="/usr/gnemul/qemu-%M" static="no" cross_compile="no" cross_prefix="" -audio_drv_list="" +audio_drv_list="default" block_drv_rw_whitelist="" block_drv_ro_whitelist="" block_drv_whitelist_tools="no" @@ -299,7 +299,6 @@ fdt="auto" netmap="no" sdl="auto" sdl_image="auto" -coreaudio="auto" virtiofsd="auto" virtfs="auto" libudev="auto" @@ -356,7 +355,6 @@ module_upgrades="no" prefix="/usr/local" qemu_suffix="qemu" slirp="auto" -oss_lib="" bsd="no" linux="no" solaris="no" @@ -703,54 +701,35 @@ fi case $targetos in MINGW32*) mingw32="yes" - audio_possible_drivers="dsound sdl" - if check_include dsound.h; then - audio_drv_list="dsound" - else - audio_drv_list="" - fi supported_os="yes" plugins="no" pie="no" ;; GNU/kFreeBSD) bsd="yes" - audio_drv_list="oss try-sdl" - audio_possible_drivers="oss sdl pa" ;; FreeBSD) bsd="yes" bsd_user="yes" make="${MAKE-gmake}" - audio_drv_list="oss try-sdl" - audio_possible_drivers="oss sdl pa" # needed for kinfo_getvmmap(3) in libutil.h netmap="" # enable netmap autodetect ;; DragonFly) bsd="yes" make="${MAKE-gmake}" - audio_drv_list="oss try-sdl" - audio_possible_drivers="oss sdl pa" ;; NetBSD) bsd="yes" make="${MAKE-gmake}" - audio_drv_list="oss try-sdl" - audio_possible_drivers="oss sdl" - oss_lib="-lossaudio" ;; OpenBSD) bsd="yes" make="${MAKE-gmake}" - audio_drv_list="try-sdl" - audio_possible_drivers="sdl" ;; Darwin) bsd="yes" darwin="yes" - audio_drv_list="try-coreaudio try-sdl" - audio_possible_drivers="coreaudio sdl" # Disable attempts to use ObjectiveC features in os/object.h since they # won't work when we're compiling with gcc as a C compiler. QEMU_CFLAGS="-DOS_OBJECT_USE_OBJC=0 $QEMU_CFLAGS" @@ -759,10 +738,6 @@ SunOS) solaris="yes" make="${MAKE-gmake}" smbd="${SMBD-/usr/sfw/sbin/smbd}" - if test -f /usr/include/sys/soundcard.h ; then - audio_drv_list="oss try-sdl" - fi - audio_possible_drivers="oss sdl" # needed for CMSG_ macros in sys/socket.h QEMU_CFLAGS="-D_XOPEN_SOURCE=600 $QEMU_CFLAGS" # needed for TIOCWIN* defines in termios.h @@ -774,8 +749,6 @@ Haiku) QEMU_CFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS -D_BSD_SOURCE -fPIC $QEMU_CFLAGS" ;; Linux) - audio_drv_list="try-pa oss" - audio_possible_drivers="oss alsa sdl pa" linux="yes" linux_user="yes" vhost_user=${default_feature:-yes} @@ -1796,8 +1769,7 @@ Advanced options (experts only): --disable-strip disable stripping binaries --disable-werror disable compilation abort on warning --disable-stack-protector disable compiler-provided stack protection - --audio-drv-list=LIST set audio drivers list: - Available drivers: $audio_possible_drivers + --audio-drv-list=LIST set audio drivers list --block-drv-whitelist=L Same as --block-drv-rw-whitelist=L --block-drv-rw-whitelist=L set block driver read-write whitelist @@ -2400,12 +2372,6 @@ if test -z "$want_tools"; then fi fi -########################################## -# Disable features only meaningful for system-mode emulation -if test "$softmmu" = "no"; then - audio_drv_list="" -fi - ########################################## # L2TPV3 probe @@ -2984,130 +2950,6 @@ EOF fi fi -########################################## -# detect CoreAudio -if test "$coreaudio" != "no" ; then - coreaudio_libs="-framework CoreAudio" - cat > $TMPC << EOF -#include -int main(void) -{ - return (int)AudioGetCurrentHostTime(); -} -EOF - if compile_prog "" "$coreaudio_libs" ; then - coreaudio=yes - else - coreaudio=no - fi -fi - -########################################## -# Sound support libraries probe - -audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/,/ /g') -for drv in $audio_drv_list; do - case $drv in - alsa | try-alsa) - if $pkg_config alsa --exists; then - alsa_libs=$($pkg_config alsa --libs) - alsa_cflags=$($pkg_config alsa --cflags) - alsa=yes - if test "$drv" = "try-alsa"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-alsa/alsa/') - fi - else - if test "$drv" = "try-alsa"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-alsa//') - else - error_exit "$drv check failed" \ - "Make sure to have the $drv libs and headers installed." - fi - fi - ;; - - pa | try-pa) - if $pkg_config libpulse --exists; then - libpulse=yes - pulse_libs=$($pkg_config libpulse --libs) - pulse_cflags=$($pkg_config libpulse --cflags) - if test "$drv" = "try-pa"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-pa/pa/') - fi - else - if test "$drv" = "try-pa"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-pa//') - else - error_exit "$drv check failed" \ - "Make sure to have the $drv libs and headers installed." - fi - fi - ;; - - sdl) - if test "$sdl" = "no"; then - error_exit "sdl not found or disabled, can not use sdl audio driver" - fi - ;; - - try-sdl) - if test "$sdl" = "no"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-sdl//') - else - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-sdl/sdl/') - fi - ;; - - coreaudio | try-coreaudio) - if test "$coreaudio" = "no"; then - if test "$drv" = "try-coreaudio"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-coreaudio//') - else - error_exit "$drv check failed" \ - "Make sure to have the $drv is available." - fi - else - coreaudio_libs="-framework CoreAudio" - if test "$drv" = "try-coreaudio"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-coreaudio/coreaudio/') - fi - fi - ;; - - dsound) - dsound_libs="-lole32 -ldxguid" - ;; - - oss) - oss_libs="$oss_lib" - ;; - - jack | try-jack) - if $pkg_config jack --exists; then - libjack=yes - jack_libs=$($pkg_config jack --libs) - if test "$drv" = "try-jack"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-jack/jack/') - fi - else - if test "$drv" = "try-jack"; then - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-jack//') - else - error_exit "$drv check failed" \ - "Make sure to have the $drv libs and headers installed." - fi - fi - ;; - - *) - echo "$audio_possible_drivers" | grep -q "\<$drv\>" || { - error_exit "Unknown driver '$drv' selected" \ - "Possible drivers are: $audio_possible_drivers" - } - ;; - esac -done - ########################################## # plugin linker support probe @@ -4538,28 +4380,6 @@ fi if test "$gprof" = "yes" ; then echo "CONFIG_GPROF=y" >> $config_host_mak fi -echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak -for drv in $audio_drv_list; do - def=CONFIG_AUDIO_$(echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]') - echo "$def=y" >> $config_host_mak -done -if test "$alsa" = "yes" ; then - echo "CONFIG_ALSA=y" >> $config_host_mak -fi -echo "ALSA_LIBS=$alsa_libs" >> $config_host_mak -echo "ALSA_CFLAGS=$alsa_cflags" >> $config_host_mak -if test "$libpulse" = "yes" ; then - echo "CONFIG_LIBPULSE=y" >> $config_host_mak -fi -echo "PULSE_LIBS=$pulse_libs" >> $config_host_mak -echo "PULSE_CFLAGS=$pulse_cflags" >> $config_host_mak -echo "COREAUDIO_LIBS=$coreaudio_libs" >> $config_host_mak -echo "DSOUND_LIBS=$dsound_libs" >> $config_host_mak -echo "OSS_LIBS=$oss_libs" >> $config_host_mak -if test "$libjack" = "yes" ; then - echo "CONFIG_LIBJACK=y" >> $config_host_mak -fi -echo "JACK_LIBS=$jack_libs" >> $config_host_mak echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak if test "$block_drv_whitelist_tools" = "yes" ; then @@ -5197,7 +5017,7 @@ if test "$skip_meson" = no; then -Dvhost_user_blk_server=$vhost_user_blk_server -Dmultiprocess=$multiprocess \ -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi -Dbpf=$bpf\ $(if test "$default_feature" = no; then echo "-Dauto_features=disabled"; fi) \ - -Dtcg_interpreter=$tcg_interpreter \ + -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ $cross_arg \ "$PWD" "$source_path" diff --git a/meson.build b/meson.build index c1314baace..90d1b7ab61 100644 --- a/meson.build +++ b/meson.build @@ -428,20 +428,23 @@ vde = not_found if config_host.has_key('CONFIG_VDE') vde = declare_dependency(link_args: config_host['VDE_LIBS'].split()) endif + pulse = not_found -if 'CONFIG_LIBPULSE' in config_host - pulse = declare_dependency(compile_args: config_host['PULSE_CFLAGS'].split(), - link_args: config_host['PULSE_LIBS'].split()) +if not get_option('pa').auto() or (targetos == 'linux' and have_system) + pulse = dependency('libpulse', required: get_option('pa'), + method: 'pkg-config', kwargs: static_kwargs) endif alsa = not_found -if 'CONFIG_ALSA' in config_host - alsa = declare_dependency(compile_args: config_host['ALSA_CFLAGS'].split(), - link_args: config_host['ALSA_LIBS'].split()) +if not get_option('alsa').auto() or (targetos == 'linux' and have_system) + alsa = dependency('alsa', required: get_option('alsa'), + method: 'pkg-config', kwargs: static_kwargs) endif jack = not_found -if 'CONFIG_LIBJACK' in config_host - jack = declare_dependency(link_args: config_host['JACK_LIBS'].split()) +if not get_option('jack').auto() or have_system + jack = dependency('jack', required: get_option('jack'), + method: 'pkg-config', kwargs: static_kwargs) endif + spice = not_found spice_headers = not_found spice_protocol = not_found @@ -801,16 +804,59 @@ if liblzfse.found() and not cc.links(''' endif oss = not_found -if 'CONFIG_AUDIO_OSS' in config_host - oss = declare_dependency(link_args: config_host['OSS_LIBS'].split()) +if not get_option('oss').auto() or have_system + if not cc.has_header('sys/soundcard.h') + # not found + elif targetos == 'netbsd' + oss = cc.find_library('ossaudio', required: get_option('oss'), + kwargs: static_kwargs) + else + oss = declare_dependency() + endif + + if not oss.found() + if get_option('oss').enabled() + error('OSS not found') + else + warning('OSS not found, disabling') + endif + endif endif dsound = not_found -if 'CONFIG_AUDIO_DSOUND' in config_host - dsound = declare_dependency(link_args: config_host['DSOUND_LIBS'].split()) +if not get_option('dsound').auto() or (targetos == 'windows' and have_system) + if cc.has_header('dsound.h') + dsound = declare_dependency(link_args: ['-lole32', '-ldxguid']) + endif + + if not dsound.found() + if get_option('dsound').enabled() + error('DirectSound not found') + else + warning('DirectSound not found, disabling') + endif + endif endif + coreaudio = not_found -if 'CONFIG_AUDIO_COREAUDIO' in config_host - coreaudio = declare_dependency(link_args: config_host['COREAUDIO_LIBS'].split()) +if not get_option('coreaudio').auto() or (targetos == 'darwin' and have_system) + coreaudio = dependency('appleframeworks', modules: 'CoreAudio', + required: get_option('coreaudio')) + if coreaudio.found() and not cc.links(''' + #include + int main(void) + { + return (int)AudioGetCurrentHostTime(); + }''') + coreaudio = not_found + endif + + if not coreaudio.found() + if get_option('coreaudio').enabled() + error('CoreAudio not found') + else + warning('CoreAudio not found, disabling') + endif + endif endif opengl = not_found @@ -1156,6 +1202,49 @@ if libbpf.found() and not cc.links(''' endif endif +################# +# config-host.h # +################# + +audio_drivers_selected = [] +if have_system + audio_drivers_available = { + 'alsa': alsa.found(), + 'coreaudio': coreaudio.found(), + 'dsound': dsound.found(), + 'jack': jack.found(), + 'oss': oss.found(), + 'pa': pulse.found(), + 'sdl': sdl.found(), + } + + # Default to native drivers first, OSS second, SDL third + audio_drivers_priority = \ + [ 'pa', 'coreaudio', 'dsound', 'oss' ] + \ + (targetos == 'linux' ? [] : [ 'sdl' ]) + audio_drivers_default = [] + foreach k: audio_drivers_priority + if audio_drivers_available[k] + audio_drivers_default += k + endif + endforeach + + foreach k: get_option('audio_drv_list') + if k == 'default' + audio_drivers_selected += audio_drivers_default + elif not audio_drivers_available[k] + error('Audio driver "@0@" not available.'.format(k)) + else + audio_drivers_selected += k + endif + endforeach +endif +foreach k: audio_drivers_selected + config_host_data.set('CONFIG_AUDIO_' + k.to_upper(), true) +endforeach +config_host_data.set('CONFIG_AUDIO_DRIVERS', + '"' + '", "'.join(audio_drivers_selected) + '", ') + if get_option('cfi') cfi_flags=[] # Check for dependency on LTO @@ -1199,10 +1288,6 @@ endif have_host_block_device = (targetos != 'darwin' or cc.has_header('IOKit/storage/IOMedia.h')) -################# -# config-host.h # -################# - have_virtfs = (targetos == 'linux' and have_system and libattr.found() and @@ -1446,7 +1531,7 @@ config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target -arrays = ['CONFIG_AUDIO_DRIVERS', 'CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] +arrays = ['CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] strings = ['HOST_DSOSUF', 'CONFIG_IASL'] foreach k, v: config_host if ignored.contains(k) @@ -2880,7 +2965,7 @@ if config_host.has_key('CONFIG_MODULES') endif summary_info += {'fuzzing support': config_host.has_key('CONFIG_FUZZ')} if have_system - summary_info += {'Audio drivers': config_host['CONFIG_AUDIO_DRIVERS']} + summary_info += {'Audio drivers': ' '.join(audio_drivers_selected)} endif summary_info += {'Trace backends': config_host['TRACE_BACKENDS']} if config_host['TRACE_BACKENDS'].split().contains('simple') @@ -3068,6 +3153,18 @@ if vnc.found() summary_info += {'VNC JPEG support': jpeg} summary_info += {'VNC PNG support': png} endif +if targetos not in ['darwin', 'haiku', 'windows'] + summary_info += {'OSS support': oss} +elif targetos == 'darwin' + summary_info += {'CoreAudio support': coreaudio} +elif targetos == 'windows' + summary_info += {'DirectSound support': dsound} +endif +if targetos == 'linux' + summary_info += {'ALSA support': alsa} + summary_info += {'PulseAudio support': pulse} +endif +summary_info += {'JACK support': jack} summary_info += {'brlapi support': brlapi} summary_info += {'vde support': config_host.has_key('CONFIG_VDE')} summary_info += {'netmap support': config_host.has_key('CONFIG_NETMAP')} diff --git a/meson_options.txt b/meson_options.txt index 2c89e79e8b..052c4f088e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,9 +6,12 @@ option('qemu_firmwarepath', type : 'string', value : '', description: 'search PATH for firmware files') option('sphinx_build', type : 'string', value : '', description: 'Use specified sphinx-build [$sphinx_build] for building document (default to be empty)') - option('default_devices', type : 'boolean', value : true, description: 'Include a default selection of devices in emulators') +option('audio_drv_list', type: 'array', value: ['default'], + choices: ['alsa', 'coreaudio', 'default', 'dsound', 'jack', 'oss', 'pa', 'sdl'], + description: 'Set audio driver list') + option('docs', type : 'feature', value : 'auto', description: 'Documentations build support') option('gettext', type : 'feature', value : 'auto', @@ -139,6 +142,19 @@ option('fuse', type: 'feature', value: 'auto', option('fuse_lseek', type : 'feature', value : 'auto', description: 'SEEK_HOLE/SEEK_DATA support for FUSE exports') +option('alsa', type: 'feature', value: 'auto', + description: 'ALSA sound support') +option('coreaudio', type: 'feature', value: 'auto', + description: 'CoreAudio sound support') +option('dsound', type: 'feature', value: 'auto', + description: 'DirectSound sound support') +option('jack', type: 'feature', value: 'auto', + description: 'JACK sound support') +option('oss', type: 'feature', value: 'auto', + description: 'OSS sound support') +option('pa', type: 'feature', value: 'auto', + description: 'PulseAudio sound support') + option('vhost_user_blk_server', type: 'feature', value: 'auto', description: 'build vhost-user-blk server') option('virtfs', type: 'feature', value: 'auto', From e5424a2997bd5c6d480e80d3aa106bd4fba5fa7c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:06:10 +0200 Subject: [PATCH 0488/1334] meson: define symbols for all available audio drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All drivers are now built by default if the corresponding libraries are available, similar to how all other modules behave; --audio-drv-list only governs the default choice of the audio driver. Adjust the CONFIG_AUDIO_* preprocessor symbols so that they are based on library availability rather than --audio-drv-list, so that the tests and -audiodev help follow the new logic. Cc: Gerd Hoffman Cc: Volker Rümelin Message-Id: <20211007130630.632028-5-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 90d1b7ab61..0b9ebf9bfb 100644 --- a/meson.build +++ b/meson.build @@ -1217,6 +1217,9 @@ if have_system 'pa': pulse.found(), 'sdl': sdl.found(), } + foreach k, v: audio_drivers_available + config_host_data.set('CONFIG_AUDIO_' + k.to_upper(), v) + endforeach # Default to native drivers first, OSS second, SDL third audio_drivers_priority = \ @@ -1239,9 +1242,6 @@ if have_system endif endforeach endif -foreach k: audio_drivers_selected - config_host_data.set('CONFIG_AUDIO_' + k.to_upper(), true) -endforeach config_host_data.set('CONFIG_AUDIO_DRIVERS', '"' + '", "'.join(audio_drivers_selected) + '", ') From ca50e5231cfc120e90d264890bff59330257c361 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:06:11 +0200 Subject: [PATCH 0489/1334] configure: add command line options for audio drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle the choice of audio drivers the same as all other dependencies. Cc: Gerd Hoffman Cc: Volker Rümelin Reviewed-by: Marc-André Lureau Message-Id: <20211007130630.632028-6-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/configure b/configure index b9a457a02f..2dd9460d20 100755 --- a/configure +++ b/configure @@ -310,6 +310,12 @@ vnc_sasl="auto" vnc_jpeg="auto" vnc_png="auto" xkbcommon="auto" +alsa="auto" +coreaudio="auto" +dsound="auto" +jack="auto" +oss="auto" +pa="auto" xen=${default_feature:+disabled} xen_ctrl_version="$default_feature" xen_pci_passthrough="auto" @@ -1051,6 +1057,30 @@ for opt do ;; --enable-xen-pci-passthrough) xen_pci_passthrough="enabled" ;; + --disable-alsa) alsa="disabled" + ;; + --enable-alsa) alsa="enabled" + ;; + --disable-coreaudio) coreaudio="disabled" + ;; + --enable-coreaudio) coreaudio="enabled" + ;; + --disable-dsound) dsound="disabled" + ;; + --enable-dsound) dsound="enabled" + ;; + --disable-jack) jack="disabled" + ;; + --enable-jack) jack="enabled" + ;; + --disable-oss) oss="disabled" + ;; + --enable-oss) oss="enabled" + ;; + --disable-pa) pa="disabled" + ;; + --enable-pa) pa="enabled" + ;; --disable-brlapi) brlapi="disabled" ;; --enable-brlapi) brlapi="enabled" @@ -1851,6 +1881,12 @@ disabled with --disable-FEATURE, default is enabled if available mpath Multipath persistent reservation passthrough xen xen backend driver support xen-pci-passthrough PCI passthrough support for Xen + alsa ALSA sound support + coreaudio CoreAudio sound support + dsound DirectSound sound support + jack JACK sound support + oss OSS sound support + pa PulseAudio sound support brlapi BrlAPI (Braile) curl curl connectivity membarrier membarrier system call (for Linux 4.14+ or Windows) @@ -5017,7 +5053,8 @@ if test "$skip_meson" = no; then -Dvhost_user_blk_server=$vhost_user_blk_server -Dmultiprocess=$multiprocess \ -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi -Dbpf=$bpf\ $(if test "$default_feature" = no; then echo "-Dauto_features=disabled"; fi) \ - -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ + -Dalsa=$alsa -Dcoreaudio=$coreaudio -Ddsound=$dsound -Djack=$jack -Doss=$oss \ + -Dpa=$pa -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ $cross_arg \ "$PWD" "$source_path" From 65ce87d47669168593398a4a3a0c0b0494858c0d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:11 +0200 Subject: [PATCH 0490/1334] kconfig: split CONFIG_SPARSE_MEM from fuzzing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass CONFIG_FUZZ via host_kconfig, and use it to select the sparse-mem device. Cc: Alexander Oleinik Reviewed-by: Alexander Bulekov Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Tested-by: Alexander Bulekov Message-Id: <20211007130829.632254-1-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- Kconfig.host | 4 ++++ hw/mem/Kconfig | 3 +++ hw/mem/meson.build | 2 +- meson.build | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Kconfig.host b/Kconfig.host index 24255ef441..60b9c07b5e 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -41,3 +41,7 @@ config PVRDMA config MULTIPROCESS_ALLOWED bool imply MULTIPROCESS + +config FUZZ + bool + select SPARSE_MEM diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig index 8b19fdc49f..03dbb3c7df 100644 --- a/hw/mem/Kconfig +++ b/hw/mem/Kconfig @@ -8,3 +8,6 @@ config MEM_DEVICE config NVDIMM bool select MEM_DEVICE + +config SPARSE_MEM + bool diff --git a/hw/mem/meson.build b/hw/mem/meson.build index 3c8fdef9f9..82f86d117e 100644 --- a/hw/mem/meson.build +++ b/hw/mem/meson.build @@ -6,4 +6,4 @@ mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c')) softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss) -softmmu_ss.add(when: 'CONFIG_FUZZ', if_true: files('sparse-mem.c')) +softmmu_ss.add(when: 'CONFIG_SPARSE_MEM', if_true: files('sparse-mem.c')) diff --git a/meson.build b/meson.build index 0b9ebf9bfb..4a584760aa 100644 --- a/meson.build +++ b/meson.build @@ -1599,6 +1599,7 @@ endif have_ivshmem = config_host_data.get('CONFIG_EVENTFD') host_kconfig = \ + ('CONFIG_FUZZ' in config_host ? ['CONFIG_FUZZ=y'] : []) + \ ('CONFIG_TPM' in config_host ? ['CONFIG_TPM=y'] : []) + \ ('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ From 537b7248991b85d1fb92cce78ecf1056f40bb750 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:12 +0200 Subject: [PATCH 0491/1334] configure, meson: move fuzzing configuration to Meson Cc: Alexander Oleinik Reviewed-by: Alexander Bulekov Tested-by: Alexander Bulekov Message-Id: <20211007130829.632254-2-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 71 +++--------------------------------- meson.build | 51 +++++++++++++++++++++++--- meson_options.txt | 4 ++ tests/qtest/fuzz/meson.build | 6 ++- 4 files changed, 59 insertions(+), 73 deletions(-) diff --git a/configure b/configure index 2dd9460d20..a9a424948c 100755 --- a/configure +++ b/configure @@ -436,7 +436,7 @@ debug_mutex="no" libpmem="auto" default_devices="true" plugins="$default_feature" -fuzzing="no" +fuzzing="false" rng_none="no" secret_keyring="$default_feature" libdaxctl="auto" @@ -566,15 +566,6 @@ int main(void) { return 0; } EOF } -write_c_fuzzer_skeleton() { - cat > $TMPC < -#include -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; } -EOF -} - if check_define __linux__ ; then targetos="Linux" elif check_define _WIN32 ; then @@ -1538,9 +1529,9 @@ for opt do ;; --disable-containers) use_containers="no" ;; - --enable-fuzzing) fuzzing=yes + --enable-fuzzing) fuzzing=true ;; - --disable-fuzzing) fuzzing=no + --disable-fuzzing) fuzzing=false ;; --gdb=*) gdb_bin="$optarg" ;; @@ -4057,26 +4048,6 @@ EOF fi fi -########################################## -# checks for fuzzer -if test "$fuzzing" = "yes" ; then - write_c_fuzzer_skeleton - if test -z "${LIB_FUZZING_ENGINE+xxx}"; then - if compile_prog "$CPU_CFLAGS -Werror -fsanitize=fuzzer" ""; then - have_fuzzer=yes - else - error_exit "Your compiler doesn't support -fsanitize=fuzzer" - exit 1 - fi - fi - - have_clang_coverage_filter=no - echo > $TMPTXT - if compile_prog "$CPU_CFLAGS -Werror -fsanitize=fuzzer -fsanitize-coverage-allowlist=$TMPTXT" ""; then - have_clang_coverage_filter=yes - fi -fi - # Thread sanitizer is, for now, much noisier than the other sanitizers; # keep it separate until that is not the case. if test "$tsan" = "yes" && test "$sanitizers" = "yes"; then @@ -4702,34 +4673,6 @@ fi if test "$have_mlockall" = "yes" ; then echo "HAVE_MLOCKALL=y" >> $config_host_mak fi -if test "$fuzzing" = "yes" ; then - # If LIB_FUZZING_ENGINE is set, assume we are running on OSS-Fuzz, and the - # needed CFLAGS have already been provided - if test -z "${LIB_FUZZING_ENGINE+xxx}" ; then - # Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the - # compiled code. - QEMU_CFLAGS="$QEMU_CFLAGS -fsanitize=fuzzer-no-link" - # To build non-fuzzer binaries with --enable-fuzzing, link everything with - # fsanitize=fuzzer-no-link. Otherwise, the linker will be unable to bind - # the fuzzer-related callbacks added by instrumentation. - QEMU_LDFLAGS="$QEMU_LDFLAGS -fsanitize=fuzzer-no-link" - # For the actual fuzzer binaries, we need to link against the libfuzzer - # library. Provide the flags for doing this in FUZZ_EXE_LDFLAGS. The meson - # rule for the fuzzer adds these to the link_args. They need to be - # configurable, to support OSS-Fuzz - FUZZ_EXE_LDFLAGS="-fsanitize=fuzzer" - else - FUZZ_EXE_LDFLAGS="$LIB_FUZZING_ENGINE" - fi - - # Specify a filter to only instrument code that is directly related to - # virtual-devices. - if test "$have_clang_coverage_filter" = "yes" ; then - cp "$source_path/scripts/oss-fuzz/instrumentation-filter-template" \ - instrumentation-filter - QEMU_CFLAGS="$QEMU_CFLAGS -fsanitize-coverage-allowlist=instrumentation-filter" - fi -fi if test "$plugins" = "yes" ; then echo "CONFIG_PLUGIN=y" >> $config_host_mak @@ -4793,11 +4736,6 @@ if test "$gcov" = "yes" ; then echo "CONFIG_GCOV=y" >> $config_host_mak fi -if test "$fuzzing" != "no"; then - echo "CONFIG_FUZZ=y" >> $config_host_mak -fi -echo "FUZZ_EXE_LDFLAGS=$FUZZ_EXE_LDFLAGS" >> $config_host_mak - if test "$rng_none" = "yes"; then echo "CONFIG_RNG_NONE=y" >> $config_host_mak fi @@ -5033,7 +4971,8 @@ if test "$skip_meson" = no; then -Dstrip=$(if test "$strip_opt" = yes; then echo true; else echo false; fi) \ -Db_pie=$(if test "$pie" = yes; then echo true; else echo false; fi) \ -Db_coverage=$(if test "$gcov" = yes; then echo true; else echo false; fi) \ - -Db_lto=$lto -Dcfi=$cfi -Dcfi_debug=$cfi_debug \ + -Db_lto=$lto -Dcfi=$cfi -Dcfi_debug=$cfi_debug -Dfuzzing=$fuzzing \ + $(test -n "${LIB_FUZZING_ENGINE+xxx}" && echo "-Dfuzzing_engine=$LIB_FUZZING_ENGINE") \ -Dmalloc=$malloc -Dmalloc_trim=$malloc_trim -Dsparse=$sparse \ -Dkvm=$kvm -Dhax=$hax -Dwhpx=$whpx -Dhvf=$hvf -Dnvmm=$nvmm \ -Dxen=$xen -Dxen_pci_passthrough=$xen_pci_passthrough -Dtcg=$tcg \ diff --git a/meson.build b/meson.build index 4a584760aa..a3424bce7d 100644 --- a/meson.build +++ b/meson.build @@ -117,10 +117,37 @@ endforeach # Specify linker-script with add_project_link_arguments so that it is not placed # within a linker --start-group/--end-group pair -if 'CONFIG_FUZZ' in config_host - add_project_link_arguments(['-Wl,-T,', - (meson.current_source_dir() / 'tests/qtest/fuzz/fork_fuzz.ld')], +if get_option('fuzzing') + add_project_link_arguments(['-Wl,-T,', + (meson.current_source_dir() / 'tests/qtest/fuzz/fork_fuzz.ld')], + native: false, language: ['c', 'cpp', 'objc']) + + # Specify a filter to only instrument code that is directly related to + # virtual-devices. + configure_file(output: 'instrumentation-filter', + input: 'scripts/oss-fuzz/instrumentation-filter-template', + copy: true) + add_global_arguments( + cc.get_supported_arguments('-fsanitize-coverage-allowlist=instrumentation-filter'), + native: false, language: ['c', 'cpp', 'objc']) + + if get_option('fuzzing_engine') == '' + # Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the + # compiled code. To build non-fuzzer binaries with --enable-fuzzing, link + # everything with fsanitize=fuzzer-no-link. Otherwise, the linker will be + # unable to bind the fuzzer-related callbacks added by instrumentation. + add_global_arguments('-fsanitize=fuzzer-no-link', + native: false, language: ['c', 'cpp', 'objc']) + add_global_link_arguments('-fsanitize=fuzzer-no-link', native: false, language: ['c', 'cpp', 'objc']) + # For the actual fuzzer binaries, we need to link against the libfuzzer + # library. They need to be configurable, to support OSS-Fuzz + fuzz_exe_ldflags = ['-fsanitize=fuzzer'] + else + # LIB_FUZZING_ENGINE was set; assume we are running on OSS-Fuzz, and + # the needed CFLAGS have already been provided + fuzz_exe_ldflags = get_option('fuzzing_engine').split() + endif endif add_global_arguments(config_host['QEMU_CFLAGS'].split(), @@ -163,6 +190,17 @@ endif # Target-specific checks and dependencies # ########################################### +if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \ + not cc.links(''' + #include + #include + int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; } + ''', + args: ['-Werror', '-fsanitize=fuzzer']) + error('Your compiler does not support -fsanitize=fuzzer') +endif + if targetos != 'linux' and get_option('mpath').enabled() error('Multipath is supported only on Linux') endif @@ -1325,6 +1363,7 @@ config_host_data.set_quoted('CONFIG_SYSCONFDIR', get_option('prefix') / get_opti config_host_data.set('CONFIG_ATTR', libattr.found()) config_host_data.set('CONFIG_BRLAPI', brlapi.found()) config_host_data.set('CONFIG_COCOA', cocoa.found()) +config_host_data.set('CONFIG_FUZZ', get_option('fuzzing')) config_host_data.set('CONFIG_LIBUDEV', libudev.found()) config_host_data.set('CONFIG_LZO', lzo.found()) config_host_data.set('CONFIG_MPATH', mpathpersist.found()) @@ -1599,7 +1638,7 @@ endif have_ivshmem = config_host_data.get('CONFIG_EVENTFD') host_kconfig = \ - ('CONFIG_FUZZ' in config_host ? ['CONFIG_FUZZ=y'] : []) + \ + (get_option('fuzzing') ? ['CONFIG_FUZZ=y'] : []) + \ ('CONFIG_TPM' in config_host ? ['CONFIG_TPM=y'] : []) + \ ('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ @@ -2724,7 +2763,7 @@ foreach target : target_dirs 'dependencies': [] }] endif - if config_host.has_key('CONFIG_FUZZ') + if get_option('fuzzing') specific_fuzz = specific_fuzz_ss.apply(config_target, strict: false) execs += [{ 'name': 'qemu-fuzz-' + target_name, @@ -2964,7 +3003,7 @@ summary_info += {'module support': config_host.has_key('CONFIG_MODULES')} if config_host.has_key('CONFIG_MODULES') summary_info += {'alternative module path': config_host.has_key('CONFIG_MODULE_UPGRADES')} endif -summary_info += {'fuzzing support': config_host.has_key('CONFIG_FUZZ')} +summary_info += {'fuzzing support': get_option('fuzzing')} if have_system summary_info += {'Audio drivers': ' '.join(audio_drivers_selected)} endif diff --git a/meson_options.txt b/meson_options.txt index 052c4f088e..100c30e967 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,9 +11,13 @@ option('default_devices', type : 'boolean', value : true, option('audio_drv_list', type: 'array', value: ['default'], choices: ['alsa', 'coreaudio', 'default', 'dsound', 'jack', 'oss', 'pa', 'sdl'], description: 'Set audio driver list') +option('fuzzing_engine', type : 'string', value : '', + description: 'fuzzing engine library for OSS-Fuzz') option('docs', type : 'feature', value : 'auto', description: 'Documentations build support') +option('fuzzing', type : 'boolean', value: false, + description: 'build fuzzing targets') option('gettext', type : 'feature', value : 'auto', description: 'Localization of the GTK+ user interface') option('install_blobs', type : 'boolean', value : true, diff --git a/tests/qtest/fuzz/meson.build b/tests/qtest/fuzz/meson.build index 8af6848cd5..189901d4a2 100644 --- a/tests/qtest/fuzz/meson.build +++ b/tests/qtest/fuzz/meson.build @@ -1,3 +1,7 @@ +if not get_option('fuzzing') + subdir_done() +endif + specific_fuzz_ss.add(files('fuzz.c', 'fork_fuzz.c', 'qos_fuzz.c', 'qtest_wrappers.c'), qos) @@ -9,7 +13,7 @@ specific_fuzz_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio_blk_fuzz. specific_fuzz_ss.add(files('generic_fuzz.c')) fork_fuzz = declare_dependency( - link_args: config_host['FUZZ_EXE_LDFLAGS'].split() + + link_args: fuzz_exe_ldflags + ['-Wl,-wrap,qtest_inb', '-Wl,-wrap,qtest_inw', '-Wl,-wrap,qtest_inl', From 0955d66e65e68580021a99960581422e93c127d4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:13 +0200 Subject: [PATCH 0492/1334] trace: simple: pass trace_file unmodified to config-host.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the suffix directly in trace/simple.c, so that quoting is done properly by Meson. Cc: Stefan Hajnoczi Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-3-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 2 -- meson.build | 2 +- trace/simple.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/configure b/configure index a9a424948c..c27c4d6f1c 100755 --- a/configure +++ b/configure @@ -4580,8 +4580,6 @@ if have_backend "nop"; then fi if have_backend "simple"; then echo "CONFIG_TRACE_SIMPLE=y" >> $config_host_mak - # Set the appropriate trace file. - trace_file="\"$trace_file-\" FMT_pid" fi if have_backend "log"; then echo "CONFIG_TRACE_LOG=y" >> $config_host_mak diff --git a/meson.build b/meson.build index a3424bce7d..5418083db7 100644 --- a/meson.build +++ b/meson.build @@ -1571,7 +1571,7 @@ config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target arrays = ['CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] -strings = ['HOST_DSOSUF', 'CONFIG_IASL'] +strings = ['HOST_DSOSUF', 'CONFIG_IASL', 'CONFIG_TRACE_FILE'] foreach k, v: config_host if ignored.contains(k) # do nothing diff --git a/trace/simple.c b/trace/simple.c index ac499edee0..18af590cf7 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -364,7 +364,7 @@ void st_set_trace_file(const char *file) if (!file) { /* Type cast needed for Windows where getpid() returns an int. */ - trace_file_name = g_strdup_printf(CONFIG_TRACE_FILE, (pid_t)getpid()); + trace_file_name = g_strdup_printf(CONFIG_TRACE_FILE "-" FMT_pid, (pid_t)getpid()); } else { trace_file_name = g_strdup_printf("%s", file); } From 9c29b74100e565c448b74a2f0e4051f4e656d18c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:14 +0200 Subject: [PATCH 0493/1334] trace: move configuration from configure to Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Stefan Hajnoczi Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-4-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 91 +-------------------------------------------- docs/meson.build | 2 +- meson.build | 51 +++++++++++++++++++++---- meson_options.txt | 6 +++ scripts/meson.build | 2 +- trace/meson.build | 15 +++++--- 6 files changed, 61 insertions(+), 106 deletions(-) diff --git a/configure b/configure index c27c4d6f1c..ba508b70b9 100755 --- a/configure +++ b/configure @@ -216,10 +216,6 @@ version_ge () { done } -have_backend () { - echo "$trace_backends" | grep "$1" >/dev/null -} - glob() { eval test -z '"${1#'"$2"'}"' } @@ -3514,56 +3510,6 @@ case "$capstone" in ;; esac -########################################## -# check if we have posix_syslog - -posix_syslog=no -cat > $TMPC << EOF -#include -int main(void) { openlog("qemu", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "configure"); return 0; } -EOF -if compile_prog "" "" ; then - posix_syslog=yes -fi - -########################################## -# check if trace backend exists - -$python "$source_path/scripts/tracetool.py" "--backends=$trace_backends" --check-backends > /dev/null 2> /dev/null -if test "$?" -ne 0 ; then - error_exit "invalid trace backends" \ - "Please choose supported trace backends." -fi - -########################################## -# For 'ust' backend, test if ust headers are present -if have_backend "ust"; then - if $pkg_config lttng-ust --exists; then - lttng_ust_libs=$($pkg_config --libs lttng-ust) - else - error_exit "Trace backend 'ust' missing lttng-ust header files" - fi -fi - -########################################## -# For 'dtrace' backend, test if 'dtrace' command is present -if have_backend "dtrace"; then - if ! has 'dtrace' ; then - error_exit "dtrace command is not found in PATH $PATH" - fi - trace_backend_stap="no" - if has 'stap' ; then - trace_backend_stap="yes" - - # Workaround to avoid dtrace(1) producing a file with 'hidden' symbol - # visibility. Define STAP_SDT_V2 to produce 'default' symbol visibility - # instead. QEMU --enable-modules depends on this because the SystemTap - # semaphores are linked into the main binary and not the module's shared - # object. - QEMU_CFLAGS="$QEMU_CFLAGS -DSTAP_SDT_V2" - fi -fi - ########################################## # check and set a backend for coroutine @@ -4574,42 +4520,6 @@ if test "$tpm" = "yes"; then echo 'CONFIG_TPM=y' >> $config_host_mak fi -echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak -if have_backend "nop"; then - echo "CONFIG_TRACE_NOP=y" >> $config_host_mak -fi -if have_backend "simple"; then - echo "CONFIG_TRACE_SIMPLE=y" >> $config_host_mak -fi -if have_backend "log"; then - echo "CONFIG_TRACE_LOG=y" >> $config_host_mak -fi -if have_backend "ust"; then - echo "CONFIG_TRACE_UST=y" >> $config_host_mak - echo "LTTNG_UST_LIBS=$lttng_ust_libs" >> $config_host_mak -fi -if have_backend "dtrace"; then - echo "CONFIG_TRACE_DTRACE=y" >> $config_host_mak - if test "$trace_backend_stap" = "yes" ; then - echo "CONFIG_TRACE_SYSTEMTAP=y" >> $config_host_mak - fi -fi -if have_backend "ftrace"; then - if test "$linux" = "yes" ; then - echo "CONFIG_TRACE_FTRACE=y" >> $config_host_mak - else - feature_not_found "ftrace(trace backend)" "ftrace requires Linux" - fi -fi -if have_backend "syslog"; then - if test "$posix_syslog" = "yes" ; then - echo "CONFIG_TRACE_SYSLOG=y" >> $config_host_mak - else - feature_not_found "syslog(trace backend)" "syslog not available" - fi -fi -echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak - if test "$rdma" = "yes" ; then echo "CONFIG_RDMA=y" >> $config_host_mak echo "RDMA_LIBS=$rdma_libs" >> $config_host_mak @@ -4992,6 +4902,7 @@ if test "$skip_meson" = no; then $(if test "$default_feature" = no; then echo "-Dauto_features=disabled"; fi) \ -Dalsa=$alsa -Dcoreaudio=$coreaudio -Ddsound=$dsound -Djack=$jack -Doss=$oss \ -Dpa=$pa -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ + -Dtrace_backends=$trace_backends -Dtrace_file=$trace_file \ $cross_arg \ "$PWD" "$source_path" diff --git a/docs/meson.build b/docs/meson.build index be4dc30f39..19cce670a2 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -57,7 +57,7 @@ if build_docs 'qemu-nbd.8': (have_tools ? 'man8' : ''), 'qemu-pr-helper.8': (have_tools ? 'man8' : ''), 'qemu-storage-daemon.1': (have_tools ? 'man1' : ''), - 'qemu-trace-stap.1': (config_host.has_key('CONFIG_TRACE_SYSTEMTAP') ? 'man1' : ''), + 'qemu-trace-stap.1': (stap.found() ? 'man1' : ''), 'virtfs-proxy-helper.1': (have_virtfs_proxy_helper ? 'man1' : ''), 'virtiofsd.1': (have_virtiofsd ? 'man1' : ''), 'qemu.1': 'man1', diff --git a/meson.build b/meson.build index 5418083db7..6a5e7254ed 100644 --- a/meson.build +++ b/meson.build @@ -111,6 +111,22 @@ foreach target : edk2_targets endif endforeach +dtrace = not_found +stap = not_found +if 'dtrace' in get_option('trace_backends') + dtrace = find_program('dtrace', required: true) + stap = find_program('stap', required: false) + if stap.found() + # Workaround to avoid dtrace(1) producing a file with 'hidden' symbol + # visibility. Define STAP_SDT_V2 to produce 'default' symbol visibility + # instead. QEMU --enable-modules depends on this because the SystemTap + # semaphores are linked into the main binary and not the module's shared + # object. + add_global_arguments('-DSTAP_SDT_V2', + native: false, language: ['c', 'cpp', 'objc']) + endif +endif + ################## # Compiler flags # ################## @@ -201,6 +217,19 @@ if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \ error('Your compiler does not support -fsanitize=fuzzer') endif +if 'ftrace' in get_option('trace_backends') and targetos != 'linux' + error('ftrace is supported only on Linux') +endif +if 'syslog' in get_option('trace_backends') and not cc.compiles(''' + #include + int main(void) { + openlog("qemu", LOG_PID, LOG_DAEMON); + syslog(LOG_INFO, "configure"); + return 0; + }''') + error('syslog is not supported on this system') +endif + if targetos != 'linux' and get_option('mpath').enabled() error('Multipath is supported only on Linux') endif @@ -359,8 +388,9 @@ if 'CONFIG_GIO' in config_host link_args: config_host['GIO_LIBS'].split()) endif lttng = not_found -if 'CONFIG_TRACE_UST' in config_host - lttng = declare_dependency(link_args: config_host['LTTNG_UST_LIBS'].split()) +if 'ust' in get_option('trace_backends') + lttng = dependency('lttng-ust', required: true, method: 'pkg-config', + kwargs: static_kwargs) endif pixman = not_found if have_system or have_tools @@ -1347,6 +1377,11 @@ elif get_option('virtfs').disabled() have_virtfs = false endif +foreach k : get_option('trace_backends') + config_host_data.set('CONFIG_TRACE_' + k.to_upper(), true) +endforeach +config_host_data.set_quoted('CONFIG_TRACE_FILE', get_option('trace_file')) + config_host_data.set_quoted('CONFIG_BINDIR', get_option('prefix') / get_option('bindir')) config_host_data.set_quoted('CONFIG_PREFIX', get_option('prefix')) config_host_data.set_quoted('CONFIG_QEMU_CONFDIR', get_option('prefix') / qemu_confdir) @@ -1571,7 +1606,7 @@ config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target arrays = ['CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] -strings = ['HOST_DSOSUF', 'CONFIG_IASL', 'CONFIG_TRACE_FILE'] +strings = ['HOST_DSOSUF', 'CONFIG_IASL'] foreach k, v: config_host if ignored.contains(k) # do nothing @@ -2109,7 +2144,7 @@ qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.py', tracetool = [ python, files('scripts/tracetool.py'), - '--backend=' + config_host['TRACE_BACKENDS'] + '--backend=' + ','.join(get_option('trace_backends')) ] tracetool_depends = files( 'scripts/tracetool/backend/log.py', @@ -2826,7 +2861,7 @@ foreach target : target_dirs emulators += {exe['name']: emulator} endif - if 'CONFIG_TRACE_SYSTEMTAP' in config_host + if stap.found() foreach stp: [ {'ext': '.stp-build', 'fmt': 'stap', 'bin': meson.current_build_dir() / exe['name'], 'install': false}, {'ext': '.stp', 'fmt': 'stap', 'bin': get_option('prefix') / get_option('bindir') / exe['name'], 'install': true}, @@ -3007,9 +3042,9 @@ summary_info += {'fuzzing support': get_option('fuzzing')} if have_system summary_info += {'Audio drivers': ' '.join(audio_drivers_selected)} endif -summary_info += {'Trace backends': config_host['TRACE_BACKENDS']} -if config_host['TRACE_BACKENDS'].split().contains('simple') - summary_info += {'Trace output file': config_host['CONFIG_TRACE_FILE'] + '-'} +summary_info += {'Trace backends': ','.join(get_option('trace_backends'))} +if 'simple' in get_option('trace_backends') + summary_info += {'Trace output file': get_option('trace_file') + '-'} endif summary_info += {'QOM debugging': config_host.has_key('CONFIG_QOM_CAST_DEBUG')} summary_info += {'vhost-kernel support': config_host.has_key('CONFIG_VHOST_KERNEL')} diff --git a/meson_options.txt b/meson_options.txt index 100c30e967..8f9c3b5b17 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -13,6 +13,8 @@ option('audio_drv_list', type: 'array', value: ['default'], description: 'Set audio driver list') option('fuzzing_engine', type : 'string', value : '', description: 'fuzzing engine library for OSS-Fuzz') +option('trace_file', type: 'string', value: 'trace', + description: 'Trace file prefix for simple backend') option('docs', type : 'feature', value : 'auto', description: 'Documentations build support') @@ -146,6 +148,10 @@ option('fuse', type: 'feature', value: 'auto', option('fuse_lseek', type : 'feature', value : 'auto', description: 'SEEK_HOLE/SEEK_DATA support for FUSE exports') +option('trace_backends', type: 'array', value: ['log'], + choices: ['dtrace', 'ftrace', 'log', 'nop', 'simple', 'syslog', 'ust'], + description: 'Set available tracing backends') + option('alsa', type: 'feature', value: 'auto', description: 'ALSA sound support') option('coreaudio', type: 'feature', value: 'auto', diff --git a/scripts/meson.build b/scripts/meson.build index e8cc63896d..1c89e10a76 100644 --- a/scripts/meson.build +++ b/scripts/meson.build @@ -1,3 +1,3 @@ -if 'CONFIG_TRACE_SYSTEMTAP' in config_host +if stap.found() install_data('qemu-trace-stap', install_dir: get_option('bindir')) endif diff --git a/trace/meson.build b/trace/meson.build index b8f95de200..573dd699c6 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -2,7 +2,6 @@ specific_ss.add(files('control-target.c')) trace_events_files = [] -dtrace = find_program('dtrace', required: 'CONFIG_TRACE_DTRACE' in config_host) foreach dir : [ '.' ] + trace_events_subdirs trace_events_file = meson.project_source_root() / dir / 'trace-events' trace_events_files += [ trace_events_file ] @@ -21,7 +20,7 @@ foreach dir : [ '.' ] + trace_events_subdirs input: trace_events_file, command: [ tracetool, group, '--format=c', '@INPUT@', '@OUTPUT@' ], depend_files: tracetool_depends) - if 'CONFIG_TRACE_UST' in config_host + if 'ust' in get_option('trace_backends') trace_ust_h = custom_target(fmt.format('trace-ust', 'h'), output: fmt.format('trace-ust', 'h'), input: trace_events_file, @@ -31,7 +30,7 @@ foreach dir : [ '.' ] + trace_events_subdirs genh += trace_ust_h endif trace_ss.add(trace_h, trace_c) - if 'CONFIG_TRACE_DTRACE' in config_host + if 'dtrace' in get_option('trace_backends') trace_dtrace = custom_target(fmt.format('trace-dtrace', 'dtrace'), output: fmt.format('trace-dtrace', 'dtrace'), input: trace_events_file, @@ -76,7 +75,7 @@ foreach d : [ specific_ss.add(when: 'CONFIG_TCG', if_true: gen) endforeach -if 'CONFIG_TRACE_UST' in config_host +if 'ust' in get_option('trace_backends') trace_ust_all_h = custom_target('trace-ust-all.h', output: 'trace-ust-all.h', input: trace_events_files, @@ -91,7 +90,11 @@ if 'CONFIG_TRACE_UST' in config_host genh += trace_ust_all_h endif -trace_ss.add(when: 'CONFIG_TRACE_SIMPLE', if_true: files('simple.c')) -trace_ss.add(when: 'CONFIG_TRACE_FTRACE', if_true: files('ftrace.c')) +if 'simple' in get_option('trace_backends') + trace_ss.add(files('simple.c')) +endif +if 'ftrace' in get_option('trace_backends') + trace_ss.add(files('ftrace.c')) +endif trace_ss.add(files('control.c')) trace_ss.add(files('qmp.c')) From a630508112aebad0da19384ff42642312d9d164f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:15 +0200 Subject: [PATCH 0494/1334] configure, meson: move CONFIG_HOST_DSOSUF to Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just a constant string, there is no need to pass it in config-host.mak. Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211007130829.632254-5-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 6 ------ meson.build | 10 +++++----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/configure b/configure index ba508b70b9..a5411c834d 100755 --- a/configure +++ b/configure @@ -351,7 +351,6 @@ bigendian="no" mingw32="no" gcov="no" EXESUF="" -HOST_DSOSUF=".so" modules="no" module_upgrades="no" prefix="/usr/local" @@ -594,9 +593,6 @@ fi # cross-compiling to one of these OSes then you'll need to specify # the correct CPU with the --cpu option. case $targetos in -Darwin) - HOST_DSOSUF=".dylib" - ;; SunOS) # $(uname -m) returns i86pc even on an x86_64 box, so default based on isainfo if test -z "$cpu" && test "$(isainfo -k)" = "amd64"; then @@ -785,7 +781,6 @@ fi if test "$mingw32" = "yes" ; then EXESUF=".exe" - HOST_DSOSUF=".dll" # MinGW needs -mthreads for TLS and macro _MT. CONFIGURE_CFLAGS="-mthreads $CONFIGURE_CFLAGS" write_c_skeleton; @@ -4638,7 +4633,6 @@ echo "GLIB_LIBS=$glib_libs" >> $config_host_mak echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak -echo "HOST_DSOSUF=$HOST_DSOSUF" >> $config_host_mak echo "LIBS_QGA=$libs_qga" >> $config_host_mak if test "$gcov" = "yes" ; then echo "CONFIG_GCOV=y" >> $config_host_mak diff --git a/meson.build b/meson.build index 6a5e7254ed..94b092ce4f 100644 --- a/meson.build +++ b/meson.build @@ -250,6 +250,7 @@ iokit = [] emulator_link_args = [] nvmm =not_found hvf = not_found +host_dsosuf = '.so' if targetos == 'windows' socket = cc.find_library('ws2_32') winmm = cc.find_library('winmm') @@ -258,9 +259,11 @@ if targetos == 'windows' version_res = win.compile_resources('version.rc', depend_files: files('pc-bios/qemu-nsis.ico'), include_directories: include_directories('.')) + host_dsosuf = '.dll' elif targetos == 'darwin' coref = dependency('appleframeworks', modules: 'CoreFoundation') iokit = dependency('appleframeworks', modules: 'IOKit', required: false) + host_dsosuf = '.dylib' elif targetos == 'sunos' socket = [cc.find_library('socket'), cc.find_library('nsl'), @@ -1458,6 +1461,7 @@ config_host_data.set('QEMU_VERSION_MAJOR', meson.project_version().split('.')[0] config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1]) config_host_data.set('QEMU_VERSION_MICRO', meson.project_version().split('.')[2]) +config_host_data.set_quoted('CONFIG_HOST_DSOSUF', host_dsosuf) config_host_data.set('HAVE_HOST_BLOCK_DEVICE', have_host_block_device) # has_header @@ -1603,10 +1607,9 @@ config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' return printf("%zu", SIZE_MAX); }''', args: ['-Werror'])) - ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target arrays = ['CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] -strings = ['HOST_DSOSUF', 'CONFIG_IASL'] +strings = ['CONFIG_IASL'] foreach k, v: config_host if ignored.contains(k) # do nothing @@ -1618,9 +1621,6 @@ foreach k, v: config_host elif k == 'ARCH' config_host_data.set('HOST_' + v.to_upper(), 1) elif strings.contains(k) - if not k.startswith('CONFIG_') - k = 'CONFIG_' + k.to_upper() - endif config_host_data.set_quoted(k, v) elif k.startswith('CONFIG_') or k.startswith('HAVE_') or k.startswith('HOST_') config_host_data.set(k, v == 'y' ? 1 : v) From 269506d228d1aa48743a74d7a37d41d85ed9da59 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:16 +0200 Subject: [PATCH 0495/1334] configure, meson: get HOST_WORDS_BIGENDIAN via the machine object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to pass it in config-host.mak. Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211007130829.632254-6-pbonzini@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 4 ---- meson.build | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/configure b/configure index a5411c834d..648ba09f70 100755 --- a/configure +++ b/configure @@ -347,7 +347,6 @@ tsan="no" fortify_source="$default_feature" strip_opt="yes" tcg_interpreter="false" -bigendian="no" mingw32="no" gcov="no" EXESUF="" @@ -4264,9 +4263,6 @@ fi if test "$strip_opt" = "yes" ; then echo "STRIP=${strip}" >> $config_host_mak fi -if test "$bigendian" = "yes" ; then - echo "HOST_WORDS_BIGENDIAN=y" >> $config_host_mak -fi if test "$mingw32" = "yes" ; then echo "CONFIG_WIN32=y" >> $config_host_mak if test "$guest_agent_with_vss" = "yes" ; then diff --git a/meson.build b/meson.build index 94b092ce4f..5c3823d143 100644 --- a/meson.build +++ b/meson.build @@ -1463,6 +1463,7 @@ config_host_data.set('QEMU_VERSION_MICRO', meson.project_version().split('.')[2] config_host_data.set_quoted('CONFIG_HOST_DSOSUF', host_dsosuf) config_host_data.set('HAVE_HOST_BLOCK_DEVICE', have_host_block_device) +config_host_data.set('HOST_WORDS_BIGENDIAN', host_machine.endian() == 'big') # has_header config_host_data.set('CONFIG_EPOLL', cc.has_header('sys/epoll.h')) @@ -1622,7 +1623,7 @@ foreach k, v: config_host config_host_data.set('HOST_' + v.to_upper(), 1) elif strings.contains(k) config_host_data.set_quoted(k, v) - elif k.startswith('CONFIG_') or k.startswith('HAVE_') or k.startswith('HOST_') + elif k.startswith('CONFIG_') or k.startswith('HAVE_') config_host_data.set(k, v == 'y' ? 1 : v) endif endforeach From af2bb99bc122ecce3f1193aaf880f31b18cf6bd9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:17 +0200 Subject: [PATCH 0496/1334] configure, meson: remove CONFIG_GCOV from config-host.mak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-7-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 3 --- meson.build | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/configure b/configure index 648ba09f70..6efea3cfc6 100755 --- a/configure +++ b/configure @@ -4630,9 +4630,6 @@ echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak echo "LIBS_QGA=$libs_qga" >> $config_host_mak -if test "$gcov" = "yes" ; then - echo "CONFIG_GCOV=y" >> $config_host_mak -fi if test "$rng_none" = "yes"; then echo "CONFIG_RNG_NONE=y" >> $config_host_mak diff --git a/meson.build b/meson.build index 5c3823d143..ff19d54fc6 100644 --- a/meson.build +++ b/meson.build @@ -1402,6 +1402,7 @@ config_host_data.set('CONFIG_ATTR', libattr.found()) config_host_data.set('CONFIG_BRLAPI', brlapi.found()) config_host_data.set('CONFIG_COCOA', cocoa.found()) config_host_data.set('CONFIG_FUZZ', get_option('fuzzing')) +config_host_data.set('CONFIG_GCOV', get_option('b_coverage')) config_host_data.set('CONFIG_LIBUDEV', libudev.found()) config_host_data.set('CONFIG_LZO', lzo.found()) config_host_data.set('CONFIG_MPATH', mpathpersist.found()) From a76a1f6b764bf9076440eb1a70e8c7636793d7fe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Oct 2021 10:04:24 +0200 Subject: [PATCH 0497/1334] meson: HAVE_GDB_BIN is not used by C code It is only used by the TCG tests, remove it from config-host.h. Signed-off-by: Paolo Bonzini --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ff19d54fc6..bdcedf2eef 100644 --- a/meson.build +++ b/meson.build @@ -1609,7 +1609,8 @@ config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' return printf("%zu", SIZE_MAX); }''', args: ['-Werror'])) -ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target +ignored = ['CONFIG_QEMU_INTERP_PREFIX', # actually per-target + 'HAVE_GDB_BIN'] arrays = ['CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] strings = ['CONFIG_IASL'] foreach k, v: config_host From 96a63aeb3d6658b837523fee0df25300622e1dd0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:18 +0200 Subject: [PATCH 0498/1334] configure, meson: move remaining HAVE_* compiler tests to Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove some special cases by moving them to Meson. Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-8-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 37 ------------------------------------- meson.build | 19 ++++++++++++++++++- 2 files changed, 18 insertions(+), 38 deletions(-) diff --git a/configure b/configure index 6efea3cfc6..e78f58978f 100755 --- a/configure +++ b/configure @@ -2403,18 +2403,6 @@ else l2tpv3=no fi -cat > $TMPC < -int main(int argc, char *argv[]) { - return mlockall(MCL_FUTURE); -} -EOF -if compile_prog "" "" ; then - have_mlockall=yes -else - have_mlockall=no -fi - ######################################### # vhost interdependencies and host support @@ -3842,21 +3830,6 @@ if test "$fortify_source" != "no"; then fi fi -########################################## -# check if struct fsxattr is available via linux/fs.h - -have_fsxattr=no -cat > $TMPC << EOF -#include -struct fsxattr foo; -int main(void) { - return 0; -} -EOF -if compile_prog "" "" ; then - have_fsxattr=yes -fi - ########################################## # check for usable membarrier system call if test "$membarrier" = "yes"; then @@ -4358,13 +4331,6 @@ if test "$gdbus_codegen" != "" ; then fi echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak -# Work around a system header bug with some kernel/XFS header -# versions where they both try to define 'struct fsxattr': -# xfs headers will not try to redefine structs from linux headers -# if this macro is set. -if test "$have_fsxattr" = "yes" ; then - echo "HAVE_FSXATTR=y" >> $config_host_mak -fi if test "$xen" = "enabled" ; then echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak @@ -4569,9 +4535,6 @@ fi if test "$parallels" = "yes" ; then echo "CONFIG_PARALLELS=y" >> $config_host_mak fi -if test "$have_mlockall" = "yes" ; then - echo "HAVE_MLOCKALL=y" >> $config_host_mak -fi if test "$plugins" = "yes" ; then echo "CONFIG_PLUGIN=y" >> $config_host_mak diff --git a/meson.build b/meson.build index bdcedf2eef..e8e728bf72 100644 --- a/meson.build +++ b/meson.build @@ -1601,6 +1601,23 @@ config_host_data.set('CONFIG_SPLICE', cc.links(gnu_source_prefix + ''' return 0; }''')) +config_host_data.set('HAVE_MLOCKALL', cc.links(gnu_source_prefix + ''' + #include + int main(int argc, char *argv[]) { + return mlockall(MCL_FUTURE); + }''')) + +# Work around a system header bug with some kernel/XFS header +# versions where they both try to define 'struct fsxattr': +# xfs headers will not try to redefine structs from linux headers +# if this macro is set. +config_host_data.set('HAVE_FSXATTR', cc.links(''' + #include ' + struct fsxattr foo; + int main(void) { + return 0; + }''')) + # Some versions of Mac OS X incorrectly define SIZE_MAX config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' #include @@ -1625,7 +1642,7 @@ foreach k, v: config_host config_host_data.set('HOST_' + v.to_upper(), 1) elif strings.contains(k) config_host_data.set_quoted(k, v) - elif k.startswith('CONFIG_') or k.startswith('HAVE_') + elif k.startswith('CONFIG_') config_host_data.set(k, v == 'y' ? 1 : v) endif endforeach From 10f6b231879c94f34d3e6549c7b81b494d3cd1c2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:19 +0200 Subject: [PATCH 0499/1334] configure, meson: move pthread_setname_np checks to Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the pthreads check dead in configure, so remove it as well. Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-9-pbonzini@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 78 ---------------------------------------- meson.build | 23 ++++++++++++ util/qemu-thread-posix.c | 5 ++- 3 files changed, 25 insertions(+), 81 deletions(-) diff --git a/configure b/configure index e78f58978f..c7e95e59cc 100755 --- a/configure +++ b/configure @@ -3148,71 +3148,6 @@ if test "$modules" = yes; then fi fi -########################################## -# pthread probe -PTHREADLIBS_LIST="-pthread -lpthread -lpthreadGC2" - -pthread=no -cat > $TMPC << EOF -#include -static void *f(void *p) { return NULL; } -int main(void) { - pthread_t thread; - pthread_create(&thread, 0, f, 0); - return 0; -} -EOF -if compile_prog "" "" ; then - pthread=yes -else - for pthread_lib in $PTHREADLIBS_LIST; do - if compile_prog "" "$pthread_lib" ; then - pthread=yes - break - fi - done -fi - -if test "$mingw32" != yes && test "$pthread" = no; then - error_exit "pthread check failed" \ - "Make sure to have the pthread libs and headers installed." -fi - -# check for pthread_setname_np with thread id -pthread_setname_np_w_tid=no -cat > $TMPC << EOF -#include - -static void *f(void *p) { return NULL; } -int main(void) -{ - pthread_t thread; - pthread_create(&thread, 0, f, 0); - pthread_setname_np(thread, "QEMU"); - return 0; -} -EOF -if compile_prog "" "$pthread_lib" ; then - pthread_setname_np_w_tid=yes -fi - -# check for pthread_setname_np without thread id -pthread_setname_np_wo_tid=no -cat > $TMPC << EOF -#include - -static void *f(void *p) { pthread_setname_np("QEMU"); return NULL; } -int main(void) -{ - pthread_t thread; - pthread_create(&thread, 0, f, 0); - return 0; -} -EOF -if compile_prog "" "$pthread_lib" ; then - pthread_setname_np_wo_tid=yes -fi - ########################################## # libssh probe if test "$libssh" != "no" ; then @@ -4498,19 +4433,6 @@ if test "$debug_mutex" = "yes" ; then echo "CONFIG_DEBUG_MUTEX=y" >> $config_host_mak fi -# Hold two types of flag: -# CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on -# a thread we have a handle to -# CONFIG_PTHREAD_SETNAME_NP_W_TID - A way of doing it on a particular -# platform -if test "$pthread_setname_np_w_tid" = "yes" ; then - echo "CONFIG_THREAD_SETNAME_BYTHREAD=y" >> $config_host_mak - echo "CONFIG_PTHREAD_SETNAME_NP_W_TID=y" >> $config_host_mak -elif test "$pthread_setname_np_wo_tid" = "yes" ; then - echo "CONFIG_THREAD_SETNAME_BYTHREAD=y" >> $config_host_mak - echo "CONFIG_PTHREAD_SETNAME_NP_WO_TID=y" >> $config_host_mak -fi - if test "$bochs" = "yes" ; then echo "CONFIG_BOCHS=y" >> $config_host_mak fi diff --git a/meson.build b/meson.build index e8e728bf72..26fc4e5792 100644 --- a/meson.build +++ b/meson.build @@ -1584,6 +1584,29 @@ config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' #include #include int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }''')) + +config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_W_TID', cc.links(''' + #include + + static void *f(void *p) { return NULL; } + int main(void) + { + pthread_t thread; + pthread_create(&thread, 0, f, 0); + pthread_setname_np(thread, "QEMU"); + return 0; + }''', dependencies: threads)) +config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_WO_TID', cc.links(''' + #include + + static void *f(void *p) { pthread_setname_np("QEMU"); return NULL; } + int main(void) + { + pthread_t thread; + pthread_create(&thread, 0, f, 0); + return 0; + }''', dependencies: threads)) + config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + ''' #include #include diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 6c5004220d..e1225b63bd 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -23,7 +23,8 @@ void qemu_thread_naming(bool enable) { name_threads = enable; -#ifndef CONFIG_THREAD_SETNAME_BYTHREAD +#if !defined CONFIG_PTHREAD_SETNAME_NP_W_TID && \ + !defined CONFIG_PTHREAD_SETNAME_NP_WO_TID /* This is a debugging option, not fatal */ if (enable) { fprintf(stderr, "qemu: thread naming not supported on this host\n"); @@ -522,7 +523,6 @@ static void *qemu_thread_start(void *args) void *arg = qemu_thread_args->arg; void *r; -#ifdef CONFIG_THREAD_SETNAME_BYTHREAD /* Attempt to set the threads name; note that this is for debug, so * we're not going to fail if we can't set it. */ @@ -533,7 +533,6 @@ static void *qemu_thread_start(void *args) pthread_setname_np(qemu_thread_args->name); # endif } -#endif QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name); g_free(qemu_thread_args->name); g_free(qemu_thread_args); From ff66f3e55b5a8d95f0af1b7573bbcad5212b0ce7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:20 +0200 Subject: [PATCH 0500/1334] configure, meson: move libaio check to meson.build Message-Id: <20211007130829.632254-10-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- block/meson.build | 2 +- configure | 31 ++++--------------------------- meson.build | 10 ++++++++-- meson_options.txt | 2 ++ stubs/meson.build | 4 +++- 5 files changed, 18 insertions(+), 31 deletions(-) diff --git a/block/meson.build b/block/meson.build index 66ee11e62c..deb73ca389 100644 --- a/block/meson.build +++ b/block/meson.build @@ -65,7 +65,7 @@ block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c')) block_ss.add(when: 'CONFIG_LINUX', if_true: files('nvme.c')) block_ss.add(when: 'CONFIG_REPLICATION', if_true: files('replication.c')) -block_ss.add(when: ['CONFIG_LINUX_AIO', libaio], if_true: files('linux-aio.c')) +block_ss.add(when: libaio, if_true: files('linux-aio.c')) block_ss.add(when: linux_io_uring, if_true: files('io_uring.c')) block_modules = {} diff --git a/configure b/configure index c7e95e59cc..670d82847f 100755 --- a/configure +++ b/configure @@ -315,7 +315,7 @@ pa="auto" xen=${default_feature:+disabled} xen_ctrl_version="$default_feature" xen_pci_passthrough="auto" -linux_aio="$default_feature" +linux_aio="auto" linux_io_uring="auto" cap_ng="auto" attr="auto" @@ -1196,9 +1196,9 @@ for opt do ;; --enable-fdt=system) fdt="system" ;; - --disable-linux-aio) linux_aio="no" + --disable-linux-aio) linux_aio="disabled" ;; - --enable-linux-aio) linux_aio="yes" + --enable-linux-aio) linux_aio="enabled" ;; --disable-linux-io-uring) linux_io_uring="disabled" ;; @@ -3163,26 +3163,6 @@ if test "$libssh" != "no" ; then fi fi -########################################## -# linux-aio probe - -if test "$linux_aio" != "no" ; then - cat > $TMPC < -#include -#include -int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); return 0; } -EOF - if compile_prog "" "-laio" ; then - linux_aio=yes - else - if test "$linux_aio" = "yes" ; then - feature_not_found "linux AIO" "Install libaio devel" - fi - linux_aio=no - fi -fi - ########################################## # TPM emulation is only on POSIX @@ -4272,9 +4252,6 @@ if test "$xen" = "enabled" ; then echo "XEN_CFLAGS=$xen_cflags" >> $config_host_mak echo "XEN_LIBS=$xen_libs" >> $config_host_mak fi -if test "$linux_aio" = "yes" ; then - echo "CONFIG_LINUX_AIO=y" >> $config_host_mak -fi if test "$vhost_scsi" = "yes" ; then echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak fi @@ -4774,7 +4751,7 @@ if test "$skip_meson" = no; then $(if test "$default_feature" = no; then echo "-Dauto_features=disabled"; fi) \ -Dalsa=$alsa -Dcoreaudio=$coreaudio -Ddsound=$dsound -Djack=$jack -Doss=$oss \ -Dpa=$pa -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ - -Dtrace_backends=$trace_backends -Dtrace_file=$trace_file \ + -Dtrace_backends=$trace_backends -Dtrace_file=$trace_file -Dlinux_aio=$linux_aio \ $cross_arg \ "$PWD" "$source_path" diff --git a/meson.build b/meson.build index 26fc4e5792..0ecb220af1 100644 --- a/meson.build +++ b/meson.build @@ -400,9 +400,14 @@ if have_system or have_tools pixman = dependency('pixman-1', required: have_system, version:'>=0.21.8', method: 'pkg-config', kwargs: static_kwargs) endif -libaio = cc.find_library('aio', required: false) zlib = dependency('zlib', required: true, kwargs: static_kwargs) +libaio = not_found +if not get_option('linux_aio').auto() or have_block + libaio = cc.find_library('aio', has_headers: ['libaio.h'], + required: get_option('linux_aio'), + kwargs: static_kwargs) +endif linux_io_uring = not_found if not get_option('linux_io_uring').auto() or have_block linux_io_uring = dependency('liburing', required: get_option('linux_io_uring'), @@ -1427,6 +1432,7 @@ config_host_data.set('CONFIG_EBPF', libbpf.found()) config_host_data.set('CONFIG_LIBDAXCTL', libdaxctl.found()) config_host_data.set('CONFIG_LIBISCSI', libiscsi.found()) config_host_data.set('CONFIG_LIBNFS', libnfs.found()) +config_host_data.set('CONFIG_LINUX_AIO', libaio.found()) config_host_data.set('CONFIG_LINUX_IO_URING', linux_io_uring.found()) config_host_data.set('CONFIG_LIBPMEM', libpmem.found()) config_host_data.set('CONFIG_RBD', rbd.found()) @@ -3286,7 +3292,7 @@ summary_info += {'JACK support': jack} summary_info += {'brlapi support': brlapi} summary_info += {'vde support': config_host.has_key('CONFIG_VDE')} summary_info += {'netmap support': config_host.has_key('CONFIG_NETMAP')} -summary_info += {'Linux AIO support': config_host.has_key('CONFIG_LINUX_AIO')} +summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')} diff --git a/meson_options.txt b/meson_options.txt index 8f9c3b5b17..904e15d54c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -103,6 +103,8 @@ option('libusb', type : 'feature', value : 'auto', description: 'libusb support for USB passthrough') option('libxml2', type : 'feature', value : 'auto', description: 'libxml2 support for Parallels image format') +option('linux_aio', type : 'feature', value : 'auto', + description: 'Linux AIO support') option('linux_io_uring', type : 'feature', value : 'auto', description: 'Linux io_uring support') option('lzfse', type : 'feature', value : 'auto', diff --git a/stubs/meson.build b/stubs/meson.build index beee31ec73..f6aa3aa94f 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -20,7 +20,9 @@ endif stub_ss.add(files('iothread-lock.c')) stub_ss.add(files('isa-bus.c')) stub_ss.add(files('is-daemonized.c')) -stub_ss.add(when: 'CONFIG_LINUX_AIO', if_true: files('linux-aio.c')) +if libaio.found() + stub_ss.add(files('linux-aio.c')) +endif stub_ss.add(files('migr-blocker.c')) stub_ss.add(files('module-opts.c')) stub_ss.add(files('monitor.c')) From e17239993d0f55c6b67a0318f177ab1168817e01 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:21 +0200 Subject: [PATCH 0501/1334] configure, meson: move vde detection to meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-11-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 35 ++++------------------------------- meson.build | 26 +++++++++++++++++++++++--- meson_options.txt | 2 ++ net/meson.build | 2 +- 4 files changed, 30 insertions(+), 35 deletions(-) diff --git a/configure b/configure index 670d82847f..0c077f3f5e 100755 --- a/configure +++ b/configure @@ -301,7 +301,7 @@ libudev="auto" mpath="auto" vnc="auto" sparse="auto" -vde="$default_feature" +vde="auto" vnc_sasl="auto" vnc_jpeg="auto" vnc_png="auto" @@ -1022,9 +1022,9 @@ for opt do ;; --enable-slirp=system) slirp="system" ;; - --disable-vde) vde="no" + --disable-vde) vde="disabled" ;; - --enable-vde) vde="yes" + --enable-vde) vde="enabled" ;; --disable-netmap) netmap="no" ;; @@ -2903,30 +2903,6 @@ EOF fi fi -########################################## -# vde libraries probe -if test "$vde" != "no" ; then - vde_libs="-lvdeplug" - cat > $TMPC << EOF -#include -int main(void) -{ - struct vde_open_args a = {0, 0, 0}; - char s[] = ""; - vde_open(s, s, &a); - return 0; -} -EOF - if compile_prog "" "$vde_libs" ; then - vde=yes - else - if test "$vde" = "yes" ; then - feature_not_found "vde" "Install vde (Virtual Distributed Ethernet) devel" - fi - vde=no - fi -fi - ########################################## # netmap support probe # Apart from looking for netmap headers, we make sure that the host API version @@ -4199,10 +4175,6 @@ if test "$slirp_smbd" = "yes" ; then echo "CONFIG_SLIRP_SMBD=y" >> $config_host_mak echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak fi -if test "$vde" = "yes" ; then - echo "CONFIG_VDE=y" >> $config_host_mak - echo "VDE_LIBS=$vde_libs" >> $config_host_mak -fi if test "$netmap" = "yes" ; then echo "CONFIG_NETMAP=y" >> $config_host_mak fi @@ -4752,6 +4724,7 @@ if test "$skip_meson" = no; then -Dalsa=$alsa -Dcoreaudio=$coreaudio -Ddsound=$dsound -Djack=$jack -Doss=$oss \ -Dpa=$pa -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ -Dtrace_backends=$trace_backends -Dtrace_file=$trace_file -Dlinux_aio=$linux_aio \ + -Dvde=$vde \ $cross_arg \ "$PWD" "$source_path" diff --git a/meson.build b/meson.build index 0ecb220af1..0e948a8024 100644 --- a/meson.build +++ b/meson.build @@ -500,9 +500,28 @@ else xkbcommon = dependency('xkbcommon', required: get_option('xkbcommon'), method: 'pkg-config', kwargs: static_kwargs) endif + vde = not_found -if config_host.has_key('CONFIG_VDE') - vde = declare_dependency(link_args: config_host['VDE_LIBS'].split()) +if not get_option('vde').auto() or have_system or have_tools + vde = cc.find_library('vdeplug', has_headers: ['libvdeplug.h'], + required: get_option('vde'), + kwargs: static_kwargs) +endif +if vde.found() and not cc.links(''' + #include + int main(void) + { + struct vde_open_args a = {0, 0, 0}; + char s[] = ""; + vde_open(s, s, &a); + return 0; + }''', dependencies: vde) + vde = not_found + if get_option('cap_ng').enabled() + error('could not link libvdeplug') + else + warning('could not link libvdeplug, disabling') + endif endif pulse = not_found @@ -1441,6 +1460,7 @@ config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found()) config_host_data.set('CONFIG_SECCOMP', seccomp.found()) config_host_data.set('CONFIG_SNAPPY', snappy.found()) config_host_data.set('CONFIG_USB_LIBUSB', libusb.found()) +config_host_data.set('CONFIG_VDE', vde.found()) config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server) config_host_data.set('CONFIG_VNC', vnc.found()) config_host_data.set('CONFIG_VNC_JPEG', jpeg.found()) @@ -3290,7 +3310,7 @@ if targetos == 'linux' endif summary_info += {'JACK support': jack} summary_info += {'brlapi support': brlapi} -summary_info += {'vde support': config_host.has_key('CONFIG_VDE')} +summary_info += {'vde support': vde} summary_info += {'netmap support': config_host.has_key('CONFIG_NETMAP')} summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} diff --git a/meson_options.txt b/meson_options.txt index 904e15d54c..7d0fa1c7f4 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -129,6 +129,8 @@ option('u2f', type : 'feature', value : 'auto', description: 'U2F emulation support') option('usb_redir', type : 'feature', value : 'auto', description: 'libusbredir support') +option('vde', type : 'feature', value : 'auto', + description: 'vde network backend support') option('virglrenderer', type : 'feature', value : 'auto', description: 'virgl rendering support') option('vnc', type : 'feature', value : 'auto', diff --git a/net/meson.build b/net/meson.build index 1076b0a7ab..491144a697 100644 --- a/net/meson.build +++ b/net/meson.build @@ -20,7 +20,7 @@ softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) softmmu_ss.add(when: 'CONFIG_L2TPV3', if_true: files('l2tpv3.c')) softmmu_ss.add(when: slirp, if_true: files('slirp.c')) -softmmu_ss.add(when: ['CONFIG_VDE', vde], if_true: files('vde.c')) +softmmu_ss.add(when: vde, if_true: files('vde.c')) softmmu_ss.add(when: 'CONFIG_NETMAP', if_true: files('netmap.c')) vhost_user_ss = ss.source_set() vhost_user_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-user.c'), if_false: files('vhost-user-stub.c')) From 837b84b1c078bf3e909017ca11be7182a5df2ed0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:22 +0200 Subject: [PATCH 0502/1334] configure, meson: move netmap detection to meson Message-Id: <20211007130829.632254-12-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 40 ++++------------------------------------ meson.build | 19 ++++++++++++++++++- meson_options.txt | 2 ++ net/meson.build | 4 +++- 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/configure b/configure index 0c077f3f5e..3544024166 100755 --- a/configure +++ b/configure @@ -292,7 +292,7 @@ iconv="auto" curses="auto" docs="auto" fdt="auto" -netmap="no" +netmap="auto" sdl="auto" sdl_image="auto" virtiofsd="auto" @@ -701,7 +701,6 @@ FreeBSD) bsd_user="yes" make="${MAKE-gmake}" # needed for kinfo_getvmmap(3) in libutil.h - netmap="" # enable netmap autodetect ;; DragonFly) bsd="yes" @@ -1026,9 +1025,9 @@ for opt do ;; --enable-vde) vde="enabled" ;; - --disable-netmap) netmap="no" + --disable-netmap) netmap="disabled" ;; - --enable-netmap) netmap="yes" + --enable-netmap) netmap="enabled" ;; --disable-xen) xen="disabled" ;; @@ -2903,34 +2902,6 @@ EOF fi fi -########################################## -# netmap support probe -# Apart from looking for netmap headers, we make sure that the host API version -# supports the netmap backend (>=11). The upper bound (15) is meant to simulate -# a minor/major version number. Minor new features will be marked with values up -# to 15, and if something happens that requires a change to the backend we will -# move above 15, submit the backend fixes and modify this two bounds. -if test "$netmap" != "no" ; then - cat > $TMPC << EOF -#include -#include -#include -#include -#if (NETMAP_API < 11) || (NETMAP_API > 15) -#error -#endif -int main(void) { return 0; } -EOF - if compile_prog "" "" ; then - netmap=yes - else - if test "$netmap" = "yes" ; then - feature_not_found "netmap" - fi - netmap=no - fi -fi - ########################################## # plugin linker support probe @@ -4175,9 +4146,6 @@ if test "$slirp_smbd" = "yes" ; then echo "CONFIG_SLIRP_SMBD=y" >> $config_host_mak echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak fi -if test "$netmap" = "yes" ; then - echo "CONFIG_NETMAP=y" >> $config_host_mak -fi if test "$l2tpv3" = "yes" ; then echo "CONFIG_L2TPV3=y" >> $config_host_mak fi @@ -4724,7 +4692,7 @@ if test "$skip_meson" = no; then -Dalsa=$alsa -Dcoreaudio=$coreaudio -Ddsound=$dsound -Djack=$jack -Doss=$oss \ -Dpa=$pa -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ -Dtrace_backends=$trace_backends -Dtrace_file=$trace_file -Dlinux_aio=$linux_aio \ - -Dvde=$vde \ + -Dnetmap=$netmap -Dvde=$vde \ $cross_arg \ "$PWD" "$source_path" diff --git a/meson.build b/meson.build index 0e948a8024..40563e3c24 100644 --- a/meson.build +++ b/meson.build @@ -1656,6 +1656,23 @@ config_host_data.set('HAVE_MLOCKALL', cc.links(gnu_source_prefix + ''' return mlockall(MCL_FUTURE); }''')) +have_netmap = false +if not get_option('netmap').disabled() and have_system + have_netmap = cc.compiles(''' + #include + #include + #include + #include + #if (NETMAP_API < 11) || (NETMAP_API > 15) + #error + #endif + int main(void) { return 0; }''') + if not have_netmap and get_option('netmap').enabled() + error('Netmap headers not available') + endif +endif +config_host_data.set('CONFIG_NETMAP', have_netmap) + # Work around a system header bug with some kernel/XFS header # versions where they both try to define 'struct fsxattr': # xfs headers will not try to redefine structs from linux headers @@ -3311,7 +3328,7 @@ endif summary_info += {'JACK support': jack} summary_info += {'brlapi support': brlapi} summary_info += {'vde support': vde} -summary_info += {'netmap support': config_host.has_key('CONFIG_NETMAP')} +summary_info += {'netmap support': have_netmap} summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} diff --git a/meson_options.txt b/meson_options.txt index 7d0fa1c7f4..d8e67ae481 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -129,6 +129,8 @@ option('u2f', type : 'feature', value : 'auto', description: 'U2F emulation support') option('usb_redir', type : 'feature', value : 'auto', description: 'libusbredir support') +option('netmap', type : 'feature', value : 'auto', + description: 'netmap network backend support') option('vde', type : 'feature', value : 'auto', description: 'vde network backend support') option('virglrenderer', type : 'feature', value : 'auto', diff --git a/net/meson.build b/net/meson.build index 491144a697..94383e7460 100644 --- a/net/meson.build +++ b/net/meson.build @@ -21,7 +21,9 @@ softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) softmmu_ss.add(when: 'CONFIG_L2TPV3', if_true: files('l2tpv3.c')) softmmu_ss.add(when: slirp, if_true: files('slirp.c')) softmmu_ss.add(when: vde, if_true: files('vde.c')) -softmmu_ss.add(when: 'CONFIG_NETMAP', if_true: files('netmap.c')) +if have_netmap + softmmu_ss.add(files('netmap.c')) +endif vhost_user_ss = ss.source_set() vhost_user_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-user.c'), if_false: files('vhost-user-stub.c')) softmmu_ss.add_all(when: 'CONFIG_VHOST_NET_USER', if_true: vhost_user_ss) From 3f0a5d55ae1cd47149bf11bd984e54fc03ad6cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 7 Oct 2021 15:08:23 +0200 Subject: [PATCH 0503/1334] configure, meson: move Spice configure handling to meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add meson feature options for Spice and Spice protocol, and move detection logic out of configure. Signed-off-by: Marc-André Lureau Message-Id: <20211007102453.978041-1-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini Message-Id: <20211007130829.632254-13-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- chardev/meson.build | 2 +- configure | 47 +-------------------------------------------- meson.build | 29 +++++++++++++++++----------- meson_options.txt | 4 ++++ ui/meson.build | 4 ++-- 5 files changed, 26 insertions(+), 60 deletions(-) diff --git a/chardev/meson.build b/chardev/meson.build index 32377af383..325ba2bdb9 100644 --- a/chardev/meson.build +++ b/chardev/meson.build @@ -35,7 +35,7 @@ if brlapi.found() chardev_modules += { 'baum': module_ss } endif -if config_host.has_key('CONFIG_SPICE') +if spice.found() module_ss = ss.source_set() module_ss.add(when: [spice], if_true: files('spice.c')) chardev_modules += { 'spice': module_ss } diff --git a/configure b/configure index 3544024166..5ad1b5194e 100755 --- a/configure +++ b/configure @@ -369,7 +369,7 @@ pie="" qom_cast_debug="yes" trace_backends="log" trace_file="trace" -spice="$default_feature" +spice="auto" spice_protocol="auto" rbd="auto" smartcard="auto" @@ -3231,41 +3231,6 @@ EOF fi fi -########################################## -# spice probe -if test "$spice_protocol" != "no" ; then - spice_protocol_cflags=$($pkg_config --cflags spice-protocol 2>/dev/null) - if $pkg_config --atleast-version=0.12.3 spice-protocol; then - spice_protocol="yes" - else - if test "$spice_protocol" = "yes" ; then - feature_not_found "spice_protocol" \ - "Install spice-protocol(>=0.12.3) devel" - fi - spice_protocol="no" - fi -fi - -if test "$spice" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { spice_server_new(); return 0; } -EOF - spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null) - spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null) - if $pkg_config --atleast-version=0.12.5 spice-server && \ - test "$spice_protocol" = "yes" && \ - compile_prog "$spice_cflags" "$spice_libs" ; then - spice="yes" - else - if test "$spice" = "yes" ; then - feature_not_found "spice" \ - "Install spice-server(>=0.12.5) devel" - fi - spice="no" - fi -fi - ########################################## # check if we have VSS SDK headers for win @@ -4235,16 +4200,6 @@ if test "$tcg" = "enabled" -a "$tcg_interpreter" = "true" ; then echo "CONFIG_TCG_INTERPRETER=y" >> $config_host_mak fi -if test "$spice_protocol" = "yes" ; then - echo "CONFIG_SPICE_PROTOCOL=y" >> $config_host_mak - echo "SPICE_PROTOCOL_CFLAGS=$spice_protocol_cflags" >> $config_host_mak -fi -if test "$spice" = "yes" ; then - echo "CONFIG_SPICE=y" >> $config_host_mak - echo "SPICE_CFLAGS=$spice_cflags $spice_protocol_cflags" >> $config_host_mak - echo "SPICE_LIBS=$spice_libs" >> $config_host_mak -fi - if test "$opengl" = "yes" ; then echo "CONFIG_OPENGL=y" >> $config_host_mak echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak diff --git a/meson.build b/meson.build index 40563e3c24..6bf43e6d30 100644 --- a/meson.build +++ b/meson.build @@ -540,17 +540,20 @@ if not get_option('jack').auto() or have_system method: 'pkg-config', kwargs: static_kwargs) endif -spice = not_found -spice_headers = not_found spice_protocol = not_found -if 'CONFIG_SPICE' in config_host - spice = declare_dependency(compile_args: config_host['SPICE_CFLAGS'].split(), - link_args: config_host['SPICE_LIBS'].split()) - spice_headers = declare_dependency(compile_args: config_host['SPICE_CFLAGS'].split()) +if not get_option('spice_protocol').auto() or have_system + spice_protocol = dependency('spice-protocol', version: '>=0.12.3', + required: get_option('spice_protocol'), + method: 'pkg-config', kwargs: static_kwargs) endif -if 'CONFIG_SPICE_PROTOCOL' in config_host - spice_protocol = declare_dependency(compile_args: config_host['SPICE_PROTOCOL_CFLAGS'].split()) +spice = not_found +if not get_option('spice').auto() or have_system + spice = dependency('spice-server', version: '>=0.12.5', + required: get_option('spice'), + method: 'pkg-config', kwargs: static_kwargs) endif +spice_headers = spice.partial_dependency(compile_args: true, includes: true) + rt = cc.find_library('rt', required: false) libdl = not_found if 'CONFIG_PLUGIN' in config_host @@ -1481,6 +1484,8 @@ config_host_data.set('CONFIG_STATX', has_statx) config_host_data.set('CONFIG_ZSTD', zstd.found()) config_host_data.set('CONFIG_FUSE', fuse.found()) config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found()) +config_host_data.set('CONFIG_SPICE_PROTOCOL', spice_protocol.found()) +config_host_data.set('CONFIG_SPICE', spice.found()) config_host_data.set('CONFIG_X11', x11.found()) config_host_data.set('CONFIG_CFI', get_option('cfi')) config_host_data.set('QEMU_VERSION', '"@0@"'.format(meson.project_version())) @@ -1761,7 +1766,7 @@ have_ivshmem = config_host_data.get('CONFIG_EVENTFD') host_kconfig = \ (get_option('fuzzing') ? ['CONFIG_FUZZ=y'] : []) + \ ('CONFIG_TPM' in config_host ? ['CONFIG_TPM=y'] : []) + \ - ('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \ + (spice.found() ? ['CONFIG_SPICE=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ ('CONFIG_OPENGL' in config_host ? ['CONFIG_OPENGL=y'] : []) + \ (x11.found() ? ['CONFIG_X11=y'] : []) + \ @@ -3337,8 +3342,10 @@ summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')} summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} summary_info += {'libcap-ng support': libcap_ng} summary_info += {'bpf support': libbpf} -# TODO: add back protocol and server version -summary_info += {'spice support': config_host.has_key('CONFIG_SPICE')} +summary_info += {'spice protocol support': spice_protocol} +if spice_protocol.found() + summary_info += {' spice server support': spice} +endif summary_info += {'rbd support': rbd} summary_info += {'xfsctl support': config_host.has_key('CONFIG_XFS')} summary_info += {'smartcard support': cacard} diff --git a/meson_options.txt b/meson_options.txt index d8e67ae481..5a140af7f7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -125,6 +125,10 @@ option('smartcard', type : 'feature', value : 'auto', description: 'CA smartcard emulation support') option('snappy', type : 'feature', value : 'auto', description: 'snappy compression support') +option('spice', type : 'feature', value : 'auto', + description: 'Spice server support') +option('spice_protocol', type : 'feature', value : 'auto', + description: 'Spice protocol support') option('u2f', type : 'feature', value : 'auto', description: 'U2F emulation support') option('usb_redir', type : 'feature', value : 'auto', diff --git a/ui/meson.build b/ui/meson.build index a73beb0e54..ee8ef27714 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -89,7 +89,7 @@ if sdl.found() ui_modules += {'sdl' : sdl_ss} endif -if config_host.has_key('CONFIG_SPICE') +if spice.found() spice_core_ss = ss.source_set() spice_core_ss.add(spice, pixman, files( 'spice-core.c', @@ -99,7 +99,7 @@ if config_host.has_key('CONFIG_SPICE') ui_modules += {'spice-core' : spice_core_ss} endif -if config_host.has_key('CONFIG_SPICE') and config_host.has_key('CONFIG_GIO') +if spice.found() and config_host.has_key('CONFIG_GIO') spice_ss = ss.source_set() spice_ss.add(spice, gio, pixman, files('spice-app.c')) ui_modules += {'spice-app': spice_ss} From b524e44fa4a6c0aeeb537fc704856002723c8d54 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:24 +0200 Subject: [PATCH 0504/1334] configure: remove obsolete Solaris ar check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Meson already has its own logic to find the "ar" binary, so remove the Solaris specific check. Reviewed-by: Marc-André Lureau Reviewed-by: Thomas Huth Message-Id: <20211007130829.632254-14-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/configure b/configure index 5ad1b5194e..9b59dce2f8 100755 --- a/configure +++ b/configure @@ -2286,21 +2286,6 @@ EOF fi fi -######################################### -# Solaris specific configure tool chain decisions - -if test "$solaris" = "yes" ; then - if has ar; then - : - else - if test -f /usr/ccs/bin/ar ; then - error_exit "No path includes ar" \ - "Add /usr/ccs/bin to your path and rerun configure" - fi - error_exit "No path includes ar" - fi -fi - if test "$tcg" = "enabled"; then git_submodules="$git_submodules tests/fp/berkeley-testfloat-3" git_submodules="$git_submodules tests/fp/berkeley-softfloat-3" From bd87a36709096034bb8e9d12d2bf6ee1d003b6d7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:25 +0200 Subject: [PATCH 0505/1334] configure, meson: move more compiler checks to Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-15-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 91 ------------------------------------------------ meson.build | 44 +++++++++++++++++++++++ util/meson.build | 4 ++- 3 files changed, 47 insertions(+), 92 deletions(-) diff --git a/configure b/configure index 9b59dce2f8..7c58c7c28d 100755 --- a/configure +++ b/configure @@ -3110,19 +3110,6 @@ elif test "$tpm" = "yes"; then fi fi -########################################## -# iovec probe -cat > $TMPC < -#include -#include -int main(void) { return sizeof(struct iovec); } -EOF -iovec=no -if compile_prog "" "" ; then - iovec=yes -fi - ########################################## # fdt probe @@ -3564,42 +3551,6 @@ EOF fi fi -######################################### -# See if 64-bit atomic operations are supported. -# Note that without __atomic builtins, we can only -# assume atomic loads/stores max at pointer size. - -cat > $TMPC << EOF -#include -int main(void) -{ - uint64_t x = 0, y = 0; - y = __atomic_load_n(&x, __ATOMIC_RELAXED); - __atomic_store_n(&x, y, __ATOMIC_RELAXED); - __atomic_compare_exchange_n(&x, &y, x, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); - __atomic_exchange_n(&x, y, __ATOMIC_RELAXED); - __atomic_fetch_add(&x, y, __ATOMIC_RELAXED); - return 0; -} -EOF -if compile_prog "" "" ; then - atomic64=yes -fi - -######################################## -# check if getauxval is available. - -getauxval=no -cat > $TMPC << EOF -#include -int main(void) { - return getauxval(AT_HWCAP) == 0; -} -EOF -if compile_prog "" "" ; then - getauxval=yes -fi - ######################################## # check if ccache is interfering with # semantic analysis of macros @@ -3673,33 +3624,6 @@ else membarrier=no fi -########################################## -# check for usable AF_VSOCK environment -have_af_vsock=no -cat > $TMPC << EOF -#include -#include -#include -#if !defined(AF_VSOCK) -# error missing AF_VSOCK flag -#endif -#include -int main(void) { - int sock, ret; - struct sockaddr_vm svm; - socklen_t len = sizeof(svm); - sock = socket(AF_VSOCK, SOCK_STREAM, 0); - ret = getpeername(sock, (struct sockaddr *)&svm, &len); - if ((ret == -1) && (errno == ENOTCONN)) { - return 0; - } - return -1; -} -EOF -if compile_prog "" "" ; then - have_af_vsock=yes -fi - ########################################## # check for usable AF_ALG environment have_afalg=no @@ -4175,9 +4099,6 @@ fi if test "$vhost_user_fs" = "yes" ; then echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak fi -if test "$iovec" = "yes" ; then - echo "CONFIG_IOVEC=y" >> $config_host_mak -fi if test "$membarrier" = "yes" ; then echo "CONFIG_MEMBARRIER=y" >> $config_host_mak fi @@ -4247,14 +4168,6 @@ if test "$cmpxchg128" = "yes" ; then echo "CONFIG_CMPXCHG128=y" >> $config_host_mak fi -if test "$atomic64" = "yes" ; then - echo "CONFIG_ATOMIC64=y" >> $config_host_mak -fi - -if test "$getauxval" = "yes" ; then - echo "CONFIG_GETAUXVAL=y" >> $config_host_mak -fi - if test "$libssh" = "yes" ; then echo "CONFIG_LIBSSH=y" >> $config_host_mak echo "LIBSSH_CFLAGS=$libssh_cflags" >> $config_host_mak @@ -4282,10 +4195,6 @@ if test "$replication" = "yes" ; then echo "CONFIG_REPLICATION=y" >> $config_host_mak fi -if test "$have_af_vsock" = "yes" ; then - echo "CONFIG_AF_VSOCK=y" >> $config_host_mak -fi - if test "$debug_mutex" = "yes" ; then echo "CONFIG_DEBUG_MUTEX=y" >> $config_host_mak fi diff --git a/meson.build b/meson.build index 6bf43e6d30..6b7487b725 100644 --- a/meson.build +++ b/meson.build @@ -1550,6 +1550,8 @@ config_host_data.set('CONFIG_INOTIFY', cc.has_header_symbol('sys/inotify.h', 'inotify_init')) config_host_data.set('CONFIG_INOTIFY1', cc.has_header_symbol('sys/inotify.h', 'inotify_init1')) +config_host_data.set('CONFIG_IOVEC', + cc.has_header_symbol('sys/uio.h', 'struct iovec')) config_host_data.set('CONFIG_MACHINE_BSWAP_H', cc.has_header_symbol('machine/bswap.h', 'bswap32', prefix: '''#include @@ -1697,6 +1699,48 @@ config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' return printf("%zu", SIZE_MAX); }''', args: ['-Werror'])) +# See if 64-bit atomic operations are supported. +# Note that without __atomic builtins, we can only +# assume atomic loads/stores max at pointer size. +config_host_data.set('CONFIG_ATOMIC64', cc.links(''' + #include + int main(void) + { + uint64_t x = 0, y = 0; + y = __atomic_load_n(&x, __ATOMIC_RELAXED); + __atomic_store_n(&x, y, __ATOMIC_RELAXED); + __atomic_compare_exchange_n(&x, &y, x, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); + __atomic_exchange_n(&x, y, __ATOMIC_RELAXED); + __atomic_fetch_add(&x, y, __ATOMIC_RELAXED); + return 0; + }''')) + +config_host_data.set('CONFIG_GETAUXVAL', cc.links(gnu_source_prefix + ''' + #include + int main(void) { + return getauxval(AT_HWCAP) == 0; + }''')) + +config_host_data.set('CONFIG_AF_VSOCK', cc.compiles(gnu_source_prefix + ''' + #include + #include + #include + #if !defined(AF_VSOCK) + # error missing AF_VSOCK flag + #endif + #include + int main(void) { + int sock, ret; + struct sockaddr_vm svm; + socklen_t len = sizeof(svm); + sock = socket(AF_VSOCK, SOCK_STREAM, 0); + ret = getpeername(sock, (struct sockaddr *)&svm, &len); + if ((ret == -1) && (errno == ENOTCONN)) { + return 0; + } + return -1; + }''')) + ignored = ['CONFIG_QEMU_INTERP_PREFIX', # actually per-target 'HAVE_GDB_BIN'] arrays = ['CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] diff --git a/util/meson.build b/util/meson.build index 779f413c86..05b593055a 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,5 +1,7 @@ util_ss.add(files('osdep.c', 'cutils.c', 'unicode.c', 'qemu-timer-common.c')) -util_ss.add(when: 'CONFIG_ATOMIC64', if_false: files('atomic64.c')) +if not config_host_data.get('CONFIG_ATOMIC64') + util_ss.add(files('atomic64.c')) +endif util_ss.add(when: 'CONFIG_POSIX', if_true: files('aio-posix.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('fdmon-poll.c')) if config_host_data.get('CONFIG_EPOLL_CREATE1') From 7bdf1f5a19132b1765482d73ee6e0782ffd374f3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:26 +0200 Subject: [PATCH 0506/1334] configure: remove deprecated --{enable, disable}-git-update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The options were deprecated in 6.0. That said, we do not really have a formal deprecation cycle for build-time changes, since they do not affect users. Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-16-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 8 -------- 1 file changed, 8 deletions(-) diff --git a/configure b/configure index 7c58c7c28d..b12b5f26b0 100755 --- a/configure +++ b/configure @@ -1483,14 +1483,6 @@ for opt do ;; --with-git=*) git="$optarg" ;; - --enable-git-update) - git_submodules_action="update" - echo "--enable-git-update deprecated, use --with-git-submodules=update" - ;; - --disable-git-update) - git_submodules_action="validate" - echo "--disable-git-update deprecated, use --with-git-submodules=validate" - ;; --with-git-submodules=*) git_submodules_action="$optarg" ;; From 03a3c0b3c59e93beeb7ca72cba632f78eb060253 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:27 +0200 Subject: [PATCH 0507/1334] configure: accept "internal" for --enable-capstone/slirp/fdt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Options such as "--enable-capstone=git" do not make much sense when building from a tarball. Accept "internal" for consistency with the meson options. Reviewed-by: Marc-André Lureau Message-Id: <20211007130829.632254-17-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure b/configure index b12b5f26b0..2ec410378e 100755 --- a/configure +++ b/configure @@ -1019,7 +1019,7 @@ for opt do ;; --enable-slirp=git) slirp="internal" ;; - --enable-slirp=system) slirp="system" + --enable-slirp=*) slirp="$optarg" ;; --disable-vde) vde="disabled" ;; @@ -1193,7 +1193,7 @@ for opt do ;; --enable-fdt=git) fdt="internal" ;; - --enable-fdt=system) fdt="system" + --enable-fdt=*) fdt="$optarg" ;; --disable-linux-aio) linux_aio="disabled" ;; @@ -1479,7 +1479,7 @@ for opt do ;; --enable-capstone=git) capstone="internal" ;; - --enable-capstone=system) capstone="system" + --enable-capstone=*) capstone="$optarg" ;; --with-git=*) git="$optarg" ;; From 61d63097bec3a11f64e14a05a81401f9af7cea11 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:28 +0200 Subject: [PATCH 0508/1334] configure: prepare for auto-generated option parsing Prepare the configure script and Makefile for automatically generated help and parsing. Because we need to run the script to generate the full help, we cannot rely on the user supplying the path to a Python interpreter with --python; therefore, the introspection output is parsed into shell functions and stored in scripts/. The converter is written in Python as standard for QEMU, and this commit contains a stub. Tested-by: Thomas Huth Message-Id: <20211007130829.632254-18-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- Makefile | 8 ++++- configure | 31 ++++++++++++----- scripts/meson-buildoptions.py | 64 +++++++++++++++++++++++++++++++++++ scripts/meson-buildoptions.sh | 13 +++++++ 4 files changed, 106 insertions(+), 10 deletions(-) create mode 100755 scripts/meson-buildoptions.py create mode 100644 scripts/meson-buildoptions.sh diff --git a/Makefile b/Makefile index 401c623a65..fe9415ac64 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ x := $(shell rm -rf meson-private meson-info meson-logs) endif # 1. ensure config-host.mak is up-to-date -config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION +config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION @echo config-host.mak is out-of-date, running configure @if test -f meson-private/coredata.dat; then \ ./config.status --skip-meson; \ @@ -124,6 +124,12 @@ ifneq ($(MESON),) Makefile.mtest: build.ninja scripts/mtest2make.py $(MESON) introspect --targets --tests --benchmarks | $(PYTHON) scripts/mtest2make.py > $@ -include Makefile.mtest + +.PHONY: update-buildoptions +all update-buildoptions: $(SRC_PATH)/scripts/meson-buildoptions.sh +$(SRC_PATH)/scripts/meson-buildoptions.sh: $(SRC_PATH)/meson_options.txt + $(MESON) introspect --buildoptions $(SRC_PATH)/meson.build | $(PYTHON) \ + scripts/meson-buildoptions.py > $@.tmp && mv $@.tmp $@ endif # 4. Rules to bridge to other makefiles diff --git a/configure b/configure index 2ec410378e..8eb0ef160f 100755 --- a/configure +++ b/configure @@ -789,6 +789,18 @@ fi werror="" +. $source_path/scripts/meson-buildoptions.sh + +meson_options= +meson_option_parse() { + meson_options="$meson_options $(_meson_option_parse "$@")" + if test $? -eq 1; then + echo "ERROR: unknown option $1" + echo "Try '$0 --help' for more information" + exit 1 + fi +} + for opt do optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') case "$opt" in @@ -1548,6 +1560,8 @@ for opt do ;; --disable-slirp-smbd) slirp_smbd=no ;; + --enable-* | --disable-*) meson_option_parse "$opt" "$optarg" + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1804,11 +1818,9 @@ Advanced options (experts only): enable plugins via shared library loading --disable-containers don't use containers for cross-building --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin] - -Optional features, enabled with --enable-FEATURE and -disabled with --disable-FEATURE, default is enabled if available -(unless built with --without-default-features): - +EOF + meson_options_help +cat << EOF system all system emulation targets user supported user emulation targets linux-user all linux usermode emulation targets @@ -4489,7 +4501,8 @@ if test "$skip_meson" = no; then mv $cross config-meson.cross rm -rf meson-private meson-info meson-logs - NINJA=$ninja $meson setup \ + run_meson() { + NINJA=$ninja $meson setup \ --prefix "$prefix" \ --libdir "$libdir" \ --libexecdir "$libexecdir" \ @@ -4534,9 +4547,9 @@ if test "$skip_meson" = no; then -Dpa=$pa -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ -Dtrace_backends=$trace_backends -Dtrace_file=$trace_file -Dlinux_aio=$linux_aio \ -Dnetmap=$netmap -Dvde=$vde \ - $cross_arg \ - "$PWD" "$source_path" - + "$@" $cross_arg "$PWD" "$source_path" + } + eval run_meson $meson_options if test "$?" -ne 0 ; then error_exit "meson setup failed" fi diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py new file mode 100755 index 0000000000..71ee56bd22 --- /dev/null +++ b/scripts/meson-buildoptions.py @@ -0,0 +1,64 @@ +#! /usr/bin/env python3 + +# Generate configure command line options handling code, based on Meson's +# user build options introspection data +# +# Copyright (C) 2021 Red Hat, Inc. +# +# Author: Paolo Bonzini +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import json +import textwrap +import shlex +import sys + +def sh_print(line=""): + print(' printf "%s\\n"', shlex.quote(line)) + + +def load_options(json): + json = [ + x + for x in json + if x["section"] == "user" + and ":" not in x["name"] + and x["name"] not in SKIP_OPTIONS + ] + return sorted(json, key=lambda x: x["name"]) + + +def print_help(options): + print("meson_options_help() {") + sh_print() + sh_print("Optional features, enabled with --enable-FEATURE and") + sh_print("disabled with --disable-FEATURE, default is enabled if available") + sh_print("(unless built with --without-default-features):") + sh_print() + print("}") + + +def print_parse(options): + print("_meson_option_parse() {") + print(" case $1 in") + print(" *) return 1 ;;") + print(" esac") + print("}") + + +options = load_options(json.load(sys.stdin)) +print("# This file is generated by meson-buildoptions.py, do not edit!") +print_help(options) +print_parse(options) diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh new file mode 100644 index 0000000000..c8ae205601 --- /dev/null +++ b/scripts/meson-buildoptions.sh @@ -0,0 +1,13 @@ +# This file is generated by meson-buildoptions.py, do not edit! +meson_options_help() { + printf "%s\n" '' + printf "%s\n" 'Optional features, enabled with --enable-FEATURE and' + printf "%s\n" 'disabled with --disable-FEATURE, default is enabled if available' + printf "%s\n" '(unless built with --without-default-features):' + printf "%s\n" '' +} +_meson_option_parse() { + case $1 in + *) return 1 ;; + esac +} From 8b5fb29842fdb586485dcfc4f24a9997c07cc9de Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Oct 2021 09:51:47 +0200 Subject: [PATCH 0509/1334] meson-buildoptions: include list of tracing backends Manually patch the introspection data to include the tracing backends. This works around a deficiency in Meson that will be fixed by https://github.com/mesonbuild/meson/pull/9395. Signed-off-by: Paolo Bonzini --- scripts/meson-buildoptions.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py index 71ee56bd22..d48af99aaf 100755 --- a/scripts/meson-buildoptions.py +++ b/scripts/meson-buildoptions.py @@ -58,7 +58,23 @@ def print_parse(options): print("}") +def fixup_options(options): + # Meson <= 0.60 does not include the choices in array options, fix that up + for opt in options: + if opt["name"] == "trace_backends": + opt["choices"] = [ + "dtrace", + "ftrace", + "log", + "nop", + "simple", + "syslog", + "ust", + ] + + options = load_options(json.load(sys.stdin)) +fixup_options(options) print("# This file is generated by meson-buildoptions.py, do not edit!") print_help(options) print_parse(options) From 3b4da13293482134b81d71be656ec76beff73a76 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Oct 2021 15:08:29 +0200 Subject: [PATCH 0510/1334] configure: automatically parse command line for meson -D options Right now meson_options.txt lists about 90 options. Each option needs code in configure to parse it and pass the option down to Meson as a -D command-line argument; in addition the default must be duplicated between configure and meson_options.txt. This series tries to remove the code duplication by generating the case statement for those --enable and --disable options, as well as the corresponding help text. About 80% of the options can be handled completely by the new mechanism. Eight meson options are not of the --enable/--disable kind. Six more need to be parsed in configure for various reasons documented in the patch, but they still have their help automatically generated. The advantages are: - less code in configure - parsing and help is more consistent (for example --enable-blobs was not supported) - options are described entirely in one place, meson_options.txt. This make it more attractive to use Meson options instead of hand-crafted configure options and config-host.mak A few options change name: --enable-tcmalloc and --enable-jemalloc become --enable-malloc={tcmalloc,jemalloc}; --disable-blobs becomes --disable-install-blobs; --enable-trace-backend becomes --enable-trace-backends. However, the old names are allowed for backwards compatibility. Message-Id: <20211007130829.632254-19-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 556 +++------------------------------- docs/devel/build-system.rst | 132 ++++---- meson_options.txt | 8 + scripts/meson-buildoptions.py | 92 ++++++ scripts/meson-buildoptions.sh | 257 ++++++++++++++++ 5 files changed, 459 insertions(+), 586 deletions(-) diff --git a/configure b/configure index 8eb0ef160f..039467c04b 100755 --- a/configure +++ b/configure @@ -286,41 +286,8 @@ for opt do esac done -brlapi="auto" -curl="auto" -iconv="auto" -curses="auto" -docs="auto" -fdt="auto" -netmap="auto" -sdl="auto" -sdl_image="auto" -virtiofsd="auto" -virtfs="auto" -libudev="auto" -mpath="auto" -vnc="auto" -sparse="auto" -vde="auto" -vnc_sasl="auto" -vnc_jpeg="auto" -vnc_png="auto" -xkbcommon="auto" -alsa="auto" -coreaudio="auto" -dsound="auto" -jack="auto" -oss="auto" -pa="auto" -xen=${default_feature:+disabled} xen_ctrl_version="$default_feature" -xen_pci_passthrough="auto" -linux_aio="auto" -linux_io_uring="auto" -cap_ng="auto" -attr="auto" xfs="$default_feature" -tcg="enabled" membarrier="$default_feature" vhost_kernel="$default_feature" vhost_net="$default_feature" @@ -328,15 +295,8 @@ vhost_crypto="$default_feature" vhost_scsi="$default_feature" vhost_vsock="$default_feature" vhost_user="no" -vhost_user_blk_server="auto" vhost_user_fs="$default_feature" vhost_vdpa="$default_feature" -bpf="auto" -kvm="auto" -hax="auto" -hvf="auto" -whpx="auto" -nvmm="auto" rdma="$default_feature" pvrdma="$default_feature" gprof="no" @@ -346,7 +306,6 @@ sanitizers="no" tsan="no" fortify_source="$default_feature" strip_opt="yes" -tcg_interpreter="false" mingw32="no" gcov="no" EXESUF="" @@ -354,68 +313,36 @@ modules="no" module_upgrades="no" prefix="/usr/local" qemu_suffix="qemu" -slirp="auto" bsd="no" linux="no" solaris="no" profiler="no" -cocoa="auto" softmmu="yes" linux_user="no" bsd_user="no" -blobs="true" pkgversion="" pie="" qom_cast_debug="yes" trace_backends="log" trace_file="trace" -spice="auto" -spice_protocol="auto" -rbd="auto" -smartcard="auto" -u2f="auto" -libusb="auto" -usb_redir="auto" opengl="$default_feature" cpuid_h="no" avx2_opt="$default_feature" -capstone="auto" -lzo="auto" -snappy="auto" -bzip2="auto" -lzfse="auto" -zstd="auto" guest_agent="$default_feature" guest_agent_with_vss="no" guest_agent_ntddscsi="no" -guest_agent_msi="auto" vss_win32_sdk="$default_feature" win_sdk="no" want_tools="$default_feature" -libiscsi="auto" -libnfs="auto" coroutine="" coroutine_pool="$default_feature" debug_stack_usage="no" crypto_afalg="no" -cfi="false" -cfi_debug="false" -seccomp="auto" -glusterfs="auto" -gtk="auto" tls_priority="NORMAL" -gnutls="auto" -nettle="auto" -gcrypt="auto" -auth_pam="auto" -vte="auto" -virglrenderer="auto" tpm="$default_feature" libssh="$default_feature" live_block_migration=${default_feature:-yes} numa="$default_feature" -tcmalloc="no" -jemalloc="no" replication=${default_feature:-yes} bochs=${default_feature:-yes} cloop=${default_feature:-yes} @@ -425,26 +352,34 @@ vdi=${default_feature:-yes} vvfat=${default_feature:-yes} qed=${default_feature:-yes} parallels=${default_feature:-yes} -libxml2="auto" debug_mutex="no" -libpmem="auto" -default_devices="true" plugins="$default_feature" -fuzzing="false" rng_none="no" secret_keyring="$default_feature" -libdaxctl="auto" meson="" +meson_args="" ninja="" +gio="$default_feature" skip_meson=no -gettext="auto" -fuse="auto" -fuse_lseek="auto" -multiprocess="auto" slirp_smbd="$default_feature" -malloc_trim="auto" -gio="$default_feature" +# The following Meson options are handled manually (still they +# are included in the automatically generated help message) + +# 1. Track which submodules are needed +capstone="auto" +fdt="auto" +slirp="auto" + +# 2. Support --with/--without option +default_devices="true" + +# 3. Automatically enable/disable other options +tcg="enabled" +cfi="false" + +# 4. Detection partly done in configure +xen=${default_feature:+disabled} # parse CC options second for opt do @@ -874,11 +809,6 @@ for opt do error_exit "Can't mix --target-list-exclude with --target-list" fi ;; - --enable-trace-backends=*) trace_backends="$optarg" - ;; - # XXX: backwards compatibility - --enable-trace-backend=*) trace_backends="$optarg" - ;; --with-trace-file=*) trace_file="$optarg" ;; --with-default-devices) default_devices="true" @@ -941,42 +871,10 @@ for opt do # configure to be used by RPM and similar macros that set # lots of directory switches by default. ;; - --disable-sdl) sdl="disabled" - ;; - --enable-sdl) sdl="enabled" - ;; - --disable-sdl-image) sdl_image="disabled" - ;; - --enable-sdl-image) sdl_image="enabled" - ;; --disable-qom-cast-debug) qom_cast_debug="no" ;; --enable-qom-cast-debug) qom_cast_debug="yes" ;; - --disable-virtfs) virtfs="disabled" - ;; - --enable-virtfs) virtfs="enabled" - ;; - --disable-libudev) libudev="disabled" - ;; - --enable-libudev) libudev="enabled" - ;; - --disable-virtiofsd) virtiofsd="disabled" - ;; - --enable-virtiofsd) virtiofsd="enabled" - ;; - --disable-mpath) mpath="disabled" - ;; - --enable-mpath) mpath="enabled" - ;; - --disable-vnc) vnc="disabled" - ;; - --enable-vnc) vnc="enabled" - ;; - --disable-gettext) gettext="disabled" - ;; - --enable-gettext) gettext="enabled" - ;; --audio-drv-list=*) audio_drv_list="$optarg" ;; --block-drv-rw-whitelist=*|--block-drv-whitelist=*) block_drv_rw_whitelist=$(echo "$optarg" | sed -e 's/,/ /g') @@ -1007,24 +905,8 @@ for opt do ;; --disable-tsan) tsan="no" ;; - --enable-sparse) sparse="enabled" - ;; - --disable-sparse) sparse="disabled" - ;; --disable-strip) strip_opt="no" ;; - --disable-vnc-sasl) vnc_sasl="disabled" - ;; - --enable-vnc-sasl) vnc_sasl="enabled" - ;; - --disable-vnc-jpeg) vnc_jpeg="disabled" - ;; - --enable-vnc-jpeg) vnc_jpeg="enabled" - ;; - --disable-vnc-png) vnc_png="disabled" - ;; - --enable-vnc-png) vnc_png="enabled" - ;; --disable-slirp) slirp="disabled" ;; --enable-slirp) slirp="enabled" @@ -1033,113 +915,17 @@ for opt do ;; --enable-slirp=*) slirp="$optarg" ;; - --disable-vde) vde="disabled" - ;; - --enable-vde) vde="enabled" - ;; - --disable-netmap) netmap="disabled" - ;; - --enable-netmap) netmap="enabled" - ;; --disable-xen) xen="disabled" ;; --enable-xen) xen="enabled" ;; - --disable-xen-pci-passthrough) xen_pci_passthrough="disabled" - ;; - --enable-xen-pci-passthrough) xen_pci_passthrough="enabled" - ;; - --disable-alsa) alsa="disabled" - ;; - --enable-alsa) alsa="enabled" - ;; - --disable-coreaudio) coreaudio="disabled" - ;; - --enable-coreaudio) coreaudio="enabled" - ;; - --disable-dsound) dsound="disabled" - ;; - --enable-dsound) dsound="enabled" - ;; - --disable-jack) jack="disabled" - ;; - --enable-jack) jack="enabled" - ;; - --disable-oss) oss="disabled" - ;; - --enable-oss) oss="enabled" - ;; - --disable-pa) pa="disabled" - ;; - --enable-pa) pa="enabled" - ;; - --disable-brlapi) brlapi="disabled" - ;; - --enable-brlapi) brlapi="enabled" - ;; - --disable-kvm) kvm="disabled" - ;; - --enable-kvm) kvm="enabled" - ;; - --disable-hax) hax="disabled" - ;; - --enable-hax) hax="enabled" - ;; - --disable-hvf) hvf="disabled" - ;; - --enable-hvf) hvf="enabled" - ;; - --disable-nvmm) nvmm="disabled" - ;; - --enable-nvmm) nvmm="enabled" - ;; - --disable-whpx) whpx="disabled" - ;; - --enable-whpx) whpx="enabled" - ;; - --disable-tcg-interpreter) tcg_interpreter="false" - ;; - --enable-tcg-interpreter) tcg_interpreter="true" - ;; - --disable-cap-ng) cap_ng="disabled" - ;; - --enable-cap-ng) cap_ng="enabled" - ;; --disable-tcg) tcg="disabled" plugins="no" ;; --enable-tcg) tcg="enabled" ;; - --disable-malloc-trim) malloc_trim="disabled" - ;; - --enable-malloc-trim) malloc_trim="enabled" - ;; - --disable-spice) spice="no" - ;; - --enable-spice) - spice_protocol="yes" - spice="yes" - ;; - --disable-spice-protocol) - spice_protocol="no" - spice="no" - ;; - --enable-spice-protocol) spice_protocol="yes" - ;; - --disable-libiscsi) libiscsi="disabled" - ;; - --enable-libiscsi) libiscsi="enabled" - ;; - --disable-libnfs) libnfs="disabled" - ;; - --enable-libnfs) libnfs="enabled" - ;; --enable-profiler) profiler="yes" ;; - --disable-cocoa) cocoa="disabled" - ;; - --enable-cocoa) cocoa="enabled" - ;; --disable-system) softmmu="no" ;; --enable-system) softmmu="yes" @@ -1183,22 +969,6 @@ for opt do ;; --disable-cfi) cfi="false" ;; - --enable-cfi-debug) cfi_debug="true" - ;; - --disable-cfi-debug) cfi_debug="false" - ;; - --disable-curses) curses="disabled" - ;; - --enable-curses) curses="enabled" - ;; - --disable-iconv) iconv="disabled" - ;; - --enable-iconv) iconv="enabled" - ;; - --disable-curl) curl="disabled" - ;; - --enable-curl) curl="enabled" - ;; --disable-fdt) fdt="disabled" ;; --enable-fdt) fdt="enabled" @@ -1207,28 +977,10 @@ for opt do ;; --enable-fdt=*) fdt="$optarg" ;; - --disable-linux-aio) linux_aio="disabled" - ;; - --enable-linux-aio) linux_aio="enabled" - ;; - --disable-linux-io-uring) linux_io_uring="disabled" - ;; - --enable-linux-io-uring) linux_io_uring="enabled" - ;; - --disable-attr) attr="disabled" - ;; - --enable-attr) attr="enabled" - ;; --disable-membarrier) membarrier="no" ;; --enable-membarrier) membarrier="yes" ;; - --disable-bpf) bpf="disabled" - ;; - --enable-bpf) bpf="enabled" - ;; - --disable-blobs) blobs="false" - ;; --with-pkgversion=*) pkgversion="$optarg" ;; --with-coroutine=*) coroutine="$optarg" @@ -1243,10 +995,6 @@ for opt do ;; --disable-crypto-afalg) crypto_afalg="no" ;; - --disable-docs) docs="disabled" - ;; - --enable-docs) docs="enabled" - ;; --disable-vhost-net) vhost_net="no" ;; --enable-vhost-net) vhost_net="yes" @@ -1263,10 +1011,6 @@ for opt do ;; --enable-vhost-vsock) vhost_vsock="yes" ;; - --disable-vhost-user-blk-server) vhost_user_blk_server="disabled" - ;; - --enable-vhost-user-blk-server) vhost_user_blk_server="enabled" - ;; --disable-vhost-user-fs) vhost_user_fs="no" ;; --enable-vhost-user-fs) vhost_user_fs="yes" @@ -1275,60 +1019,16 @@ for opt do ;; --enable-opengl) opengl="yes" ;; - --disable-rbd) rbd="disabled" - ;; - --enable-rbd) rbd="enabled" - ;; --disable-xfsctl) xfs="no" ;; --enable-xfsctl) xfs="yes" ;; - --disable-smartcard) smartcard="disabled" - ;; - --enable-smartcard) smartcard="enabled" - ;; - --disable-u2f) u2f="disabled" - ;; - --enable-u2f) u2f="enabled" - ;; - --disable-libusb) libusb="disabled" - ;; - --enable-libusb) libusb="enabled" - ;; - --disable-usb-redir) usb_redir="disabled" - ;; - --enable-usb-redir) usb_redir="enabled" - ;; --disable-zlib-test) ;; - --disable-lzo) lzo="disabled" - ;; - --enable-lzo) lzo="enabled" - ;; - --disable-snappy) snappy="disabled" - ;; - --enable-snappy) snappy="enabled" - ;; - --disable-bzip2) bzip2="disabled" - ;; - --enable-bzip2) bzip2="enabled" - ;; - --enable-lzfse) lzfse="enabled" - ;; - --disable-lzfse) lzfse="disabled" - ;; - --disable-zstd) zstd="disabled" - ;; - --enable-zstd) zstd="enabled" - ;; --enable-guest-agent) guest_agent="yes" ;; --disable-guest-agent) guest_agent="no" ;; - --enable-guest-agent-msi) guest_agent_msi="enabled" - ;; - --disable-guest-agent-msi) guest_agent_msi="disabled" - ;; --with-vss-sdk) vss_win32_sdk="" ;; --with-vss-sdk=*) vss_win32_sdk="$optarg" @@ -1345,12 +1045,6 @@ for opt do ;; --disable-tools) want_tools="no" ;; - --enable-seccomp) seccomp="enabled" - ;; - --disable-seccomp) seccomp="disabled" - ;; - --disable-glusterfs) glusterfs="disabled" - ;; --disable-avx2) avx2_opt="no" ;; --enable-avx2) avx2_opt="yes" @@ -1359,9 +1053,6 @@ for opt do ;; --enable-avx512f) avx512f_opt="yes" ;; - - --enable-glusterfs) glusterfs="enabled" - ;; --disable-virtio-blk-data-plane|--enable-virtio-blk-data-plane) echo "$0: $opt is obsolete, virtio-blk data-plane is always on" >&2 ;; @@ -1371,28 +1062,8 @@ for opt do --enable-uuid|--disable-uuid) echo "$0: $opt is obsolete, UUID support is always built" >&2 ;; - --disable-gtk) gtk="disabled" - ;; - --enable-gtk) gtk="enabled" - ;; --tls-priority=*) tls_priority="$optarg" ;; - --disable-gnutls) gnutls="disabled" - ;; - --enable-gnutls) gnutls="enabled" - ;; - --disable-nettle) nettle="disabled" - ;; - --enable-nettle) nettle="enabled" - ;; - --disable-gcrypt) gcrypt="disabled" - ;; - --enable-gcrypt) gcrypt="enabled" - ;; - --disable-auth-pam) auth_pam="disabled" - ;; - --enable-auth-pam) auth_pam="enabled" - ;; --enable-rdma) rdma="yes" ;; --disable-rdma) rdma="no" @@ -1401,14 +1072,6 @@ for opt do ;; --disable-pvrdma) pvrdma="no" ;; - --disable-vte) vte="disabled" - ;; - --enable-vte) vte="enabled" - ;; - --disable-virglrenderer) virglrenderer="disabled" - ;; - --enable-virglrenderer) virglrenderer="enabled" - ;; --disable-tpm) tpm="no" ;; --enable-tpm) tpm="yes" @@ -1425,18 +1088,6 @@ for opt do ;; --enable-numa) numa="yes" ;; - --disable-libxml2) libxml2="disabled" - ;; - --enable-libxml2) libxml2="enabled" - ;; - --disable-tcmalloc) tcmalloc="no" - ;; - --enable-tcmalloc) tcmalloc="yes" - ;; - --disable-jemalloc) jemalloc="no" - ;; - --enable-jemalloc) jemalloc="yes" - ;; --disable-replication) replication="no" ;; --enable-replication) replication="yes" @@ -1502,14 +1153,6 @@ for opt do ;; --disable-debug-mutex) debug_mutex=no ;; - --enable-libpmem) libpmem="enabled" - ;; - --disable-libpmem) libpmem="disabled" - ;; - --enable-xkbcommon) xkbcommon="enabled" - ;; - --disable-xkbcommon) xkbcommon="disabled" - ;; --enable-plugins) if test "$mingw32" = "yes"; then error_exit "TCG plugins not currently supported on Windows platforms" else @@ -1522,10 +1165,6 @@ for opt do ;; --disable-containers) use_containers="no" ;; - --enable-fuzzing) fuzzing=true - ;; - --disable-fuzzing) fuzzing=false - ;; --gdb=*) gdb_bin="$optarg" ;; --enable-rng-none) rng_none=yes @@ -1536,22 +1175,6 @@ for opt do ;; --disable-keyring) secret_keyring="no" ;; - --enable-libdaxctl) libdaxctl="enabled" - ;; - --disable-libdaxctl) libdaxctl="disabled" - ;; - --enable-fuse) fuse="enabled" - ;; - --disable-fuse) fuse="disabled" - ;; - --enable-fuse-lseek) fuse_lseek="enabled" - ;; - --disable-fuse-lseek) fuse_lseek="disabled" - ;; - --enable-multiprocess) multiprocess="enabled" - ;; - --disable-multiprocess) multiprocess="disabled" - ;; --enable-gio) gio=yes ;; --disable-gio) gio=no @@ -1560,6 +1183,16 @@ for opt do ;; --disable-slirp-smbd) slirp_smbd=no ;; + # backwards compatibility options + --enable-trace-backend=*) meson_option_parse "--enable-trace-backends=$optarg" "$optarg" + ;; + --disable-blobs) meson_option_parse --disable-install-blobs "" + ;; + --enable-tcmalloc) meson_option_parse --enable-malloc=tcmalloc tcmalloc + ;; + --enable-jemalloc) meson_option_parse --enable-malloc=jemalloc jemalloc + ;; + # everything else has the same name in configure and meson --enable-* | --disable-*) meson_option_parse "$opt" "$optarg" ;; *) @@ -1716,9 +1349,6 @@ for config in $mak_wilds; do fi done -# Enumerate public trace backends for --help output -trace_backend_list=$(echo $(grep -le '^PUBLIC = True$' "$source_path"/scripts/tracetool/backend/*.py | sed -e 's/^.*\/\(.*\)\.py$/\1/')) - if test x"$show_help" = x"yes" ; then cat << EOF @@ -1795,18 +1425,12 @@ Advanced options (experts only): (by default affects only QEMU, not tools like qemu-img) --enable-block-drv-whitelist-in-tools use block whitelist also in tools instead of only QEMU - --enable-trace-backends=B Set trace backend - Available backends: $trace_backend_list --with-trace-file=NAME Full PATH,NAME of file to store traces Default:trace- - --disable-slirp disable SLIRP userspace network connectivity - --enable-tcg-interpreter enable TCI (TCG with bytecode interpreter, experimental and slow) - --enable-malloc-trim enable libc malloc_trim() for memory optimization --cpu=CPU Build for host CPU [$cpu] --with-coroutine=BACKEND coroutine backend. Supported options: ucontext, sigaltstack, windows --enable-gcov enable test coverage analysis with gcov - --disable-blobs disable installing provided firmware blobs --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb) --tls-priority default TLS protocol/cipher priority string @@ -1825,110 +1449,34 @@ cat << EOF user supported user emulation targets linux-user all linux usermode emulation targets bsd-user all BSD usermode emulation targets - docs build documentation guest-agent build the QEMU Guest Agent - guest-agent-msi build guest agent Windows MSI installation package pie Position Independent Executables modules modules support (non-Windows) module-upgrades try to load modules from alternate paths for upgrades debug-tcg TCG debugging (default is disabled) debug-info debugging information lto Enable Link-Time Optimization. - sparse sparse checker safe-stack SafeStack Stack Smash Protection. Depends on clang/llvm >= 3.7 and requires coroutine backend ucontext. - cfi Enable Control-Flow Integrity for indirect function calls. - In case of a cfi violation, QEMU is terminated with SIGILL - Depends on lto and is incompatible with modules - Automatically enables Link-Time Optimization (lto) - cfi-debug In case of a cfi violation, a message containing the line that - triggered the error is written to stderr. After the error, - QEMU is still terminated with SIGILL - gnutls GNUTLS cryptography support - nettle nettle cryptography support - gcrypt libgcrypt cryptography support - auth-pam PAM access control - sdl SDL UI - sdl-image SDL Image support for icons - gtk gtk UI - vte vte support for the gtk UI - curses curses UI - iconv font glyph conversion support - vnc VNC UI support - vnc-sasl SASL encryption for VNC server - vnc-jpeg JPEG lossy compression for VNC server - vnc-png PNG compression for VNC server - cocoa Cocoa UI (Mac OS X only) - virtfs VirtFS - virtiofsd build virtiofs daemon (virtiofsd) - libudev Use libudev to enumerate host devices - mpath Multipath persistent reservation passthrough - xen xen backend driver support - xen-pci-passthrough PCI passthrough support for Xen - alsa ALSA sound support - coreaudio CoreAudio sound support - dsound DirectSound sound support - jack JACK sound support - oss OSS sound support - pa PulseAudio sound support - brlapi BrlAPI (Braile) - curl curl connectivity membarrier membarrier system call (for Linux 4.14+ or Windows) - fdt fdt device tree - kvm KVM acceleration support - hax HAX acceleration support - hvf Hypervisor.framework acceleration support - nvmm NVMM acceleration support - whpx Windows Hypervisor Platform acceleration support rdma Enable RDMA-based migration pvrdma Enable PVRDMA support - vde support for vde network - netmap support for netmap network - linux-aio Linux AIO support - linux-io-uring Linux io_uring support - cap-ng libcap-ng support - attr attr and xattr support vhost-net vhost-net kernel acceleration support vhost-vsock virtio sockets device support vhost-scsi vhost-scsi kernel target support vhost-crypto vhost-user-crypto backend support vhost-kernel vhost kernel backend support vhost-user vhost-user backend support - vhost-user-blk-server vhost-user-blk server support vhost-vdpa vhost-vdpa kernel backend support - bpf BPF kernel support - spice spice - spice-protocol spice-protocol - rbd rados block device (rbd) - libiscsi iscsi support - libnfs nfs support - smartcard smartcard support (libcacard) - u2f U2F support (u2f-emu) - libusb libusb (for usb passthrough) live-block-migration Block migration in the main migration stream - usb-redir usb network redirection support - lzo support of lzo compression library - snappy support of snappy compression library - bzip2 support of bzip2 compression library - (for reading bzip2-compressed dmg images) - lzfse support of lzfse compression library - (for reading lzfse-compressed dmg images) - zstd support for zstd compression library - (for migration compression and qcow2 cluster compression) - seccomp seccomp support coroutine-pool coroutine freelist (better performance) - glusterfs GlusterFS backend tpm TPM support libssh ssh block device support numa libnuma support - libxml2 for Parallels image format - tcmalloc tcmalloc support - jemalloc jemalloc support avx2 AVX2 optimization support avx512f AVX512F optimization support replication replication support opengl opengl support - virglrenderer virgl rendering support xfsctl xfsctl support qom-cast-debug cast debugging support tools build qemu-io, qemu-nbd and qemu-img tools @@ -1941,15 +1489,8 @@ cat << EOF qed qed image format support parallels parallels image format support crypto-afalg Linux AF_ALG crypto backend driver - capstone capstone disassembler support debug-mutex mutex debugging support - libpmem libpmem support - xkbcommon xkbcommon support rng-none dummy RNG, avoid using /dev/(u)random and getrandom() - libdaxctl libdaxctl support - fuse FUSE block device export - fuse-lseek SEEK_HOLE/SEEK_DATA support for FUSE exports - multiprocess Out of process device emulation support gio libgio support slirp-smbd use smbd (at path --smbd=*) in slirp networking @@ -3173,16 +2714,6 @@ EOF fi fi -malloc=system -if test "$tcmalloc" = "yes" && test "$jemalloc" = "yes" ; then - echo "ERROR: tcmalloc && jemalloc can't be used at the same time" - exit 1 -elif test "$tcmalloc" = "yes" ; then - malloc=tcmalloc -elif test "$jemalloc" = "yes" ; then - malloc=jemalloc -fi - # check for usbfs have_usbfs=no if test "$linux_user" = "yes"; then @@ -4513,40 +4044,23 @@ if test "$skip_meson" = no; then --sysconfdir "$sysconfdir" \ --localedir "$localedir" \ --localstatedir "$local_statedir" \ + -Daudio_drv_list=$audio_drv_list \ + -Ddefault_devices=$default_devices \ -Ddocdir="$docdir" \ -Dqemu_firmwarepath="$firmwarepath" \ -Dqemu_suffix="$qemu_suffix" \ + -Dsphinx_build="$sphinx_build" \ + -Dtrace_file="$trace_file" \ -Doptimization=$(if test "$debug" = yes; then echo 0; else echo 2; fi) \ -Ddebug=$(if test "$debug_info" = yes; then echo true; else echo false; fi) \ -Dwerror=$(if test "$werror" = yes; then echo true; else echo false; fi) \ -Dstrip=$(if test "$strip_opt" = yes; then echo true; else echo false; fi) \ -Db_pie=$(if test "$pie" = yes; then echo true; else echo false; fi) \ -Db_coverage=$(if test "$gcov" = yes; then echo true; else echo false; fi) \ - -Db_lto=$lto -Dcfi=$cfi -Dcfi_debug=$cfi_debug -Dfuzzing=$fuzzing \ + -Db_lto=$lto -Dcfi=$cfi -Dtcg=$tcg -Dxen=$xen \ + -Dcapstone=$capstone -Dfdt=$fdt -Dslirp=$slirp \ $(test -n "${LIB_FUZZING_ENGINE+xxx}" && echo "-Dfuzzing_engine=$LIB_FUZZING_ENGINE") \ - -Dmalloc=$malloc -Dmalloc_trim=$malloc_trim -Dsparse=$sparse \ - -Dkvm=$kvm -Dhax=$hax -Dwhpx=$whpx -Dhvf=$hvf -Dnvmm=$nvmm \ - -Dxen=$xen -Dxen_pci_passthrough=$xen_pci_passthrough -Dtcg=$tcg \ - -Dcocoa=$cocoa -Dgtk=$gtk -Dmpath=$mpath -Dsdl=$sdl -Dsdl_image=$sdl_image \ - -Dlibusb=$libusb -Dsmartcard=$smartcard -Dusb_redir=$usb_redir -Dvte=$vte \ - -Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg -Dvnc_png=$vnc_png \ - -Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f -Dvirtiofsd=$virtiofsd \ - -Dcapstone=$capstone -Dslirp=$slirp -Dfdt=$fdt -Dbrlapi=$brlapi \ - -Dcurl=$curl -Dglusterfs=$glusterfs -Dbzip2=$bzip2 -Dlibiscsi=$libiscsi \ - -Dlibnfs=$libnfs -Diconv=$iconv -Dcurses=$curses -Dlibudev=$libudev\ - -Drbd=$rbd -Dlzo=$lzo -Dsnappy=$snappy -Dlzfse=$lzfse -Dlibxml2=$libxml2 \ - -Dlibdaxctl=$libdaxctl -Dlibpmem=$libpmem -Dlinux_io_uring=$linux_io_uring \ - -Dgnutls=$gnutls -Dnettle=$nettle -Dgcrypt=$gcrypt -Dauth_pam=$auth_pam \ - -Dzstd=$zstd -Dseccomp=$seccomp -Dvirtfs=$virtfs -Dcap_ng=$cap_ng \ - -Dattr=$attr -Ddefault_devices=$default_devices -Dvirglrenderer=$virglrenderer \ - -Ddocs=$docs -Dsphinx_build=$sphinx_build -Dinstall_blobs=$blobs \ - -Dvhost_user_blk_server=$vhost_user_blk_server -Dmultiprocess=$multiprocess \ - -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi -Dbpf=$bpf\ $(if test "$default_feature" = no; then echo "-Dauto_features=disabled"; fi) \ - -Dalsa=$alsa -Dcoreaudio=$coreaudio -Ddsound=$dsound -Djack=$jack -Doss=$oss \ - -Dpa=$pa -Daudio_drv_list=$audio_drv_list -Dtcg_interpreter=$tcg_interpreter \ - -Dtrace_backends=$trace_backends -Dtrace_file=$trace_file -Dlinux_aio=$linux_aio \ - -Dnetmap=$netmap -Dvde=$vde \ "$@" $cross_arg "$PWD" "$source_path" } eval run_meson $meson_options diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 0f636d620e..7f106d2f1c 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -42,73 +42,21 @@ perform a build: ../configure make -For now, checks on the compilation environment are found in configure -rather than meson.build, though this is expected to change. The command -line is parsed in the configure script and, whenever needed, converted -into the appropriate options to Meson. - -New checks should be added to Meson, which usually comprises the -following tasks: - - - Add a Meson build option to meson_options.txt. - - - Add support to the command line arg parser to handle any new - ``--enable-XXX``/``--disable-XXX`` flags required by the feature. - - - Add information to the help output message to report on the new - feature flag. - - - Add code to perform the actual feature check. - - - Add code to include the feature status in ``config-host.h`` - - - Add code to print out the feature status in the configure summary - upon completion. - - -Taking the probe for SDL2_Image as an example, we have the following pieces -in configure:: - - # Initial variable state - sdl_image=auto - - ..snip.. - - # Configure flag processing - --disable-sdl-image) sdl_image=disabled - ;; - --enable-sdl-image) sdl_image=enabled - ;; - - ..snip.. - - # Help output feature message - sdl-image SDL Image support for icons - - ..snip.. - - # Meson invocation - -Dsdl_image=$sdl_image - -In meson_options.txt:: - - option('sdl', type : 'feature', value : 'auto', - description: 'SDL Image support for icons') - -In meson.build:: - - # Detect dependency - sdl_image = dependency('SDL2_image', required: get_option('sdl_image'), - method: 'pkg-config', - kwargs: static_kwargs) - - # Create config-host.h (if applicable) - config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found()) - - # Summary - summary_info += {'SDL image support': sdl_image.found()} +The configure script automatically recognizes +command line options for which a same-named Meson option exists; +dashes in the command line are replaced with underscores. +Many checks on the compilation environment are still found in configure +rather than `meson.build`, but new checks should be added directly to +`meson.build`. +Patches are also welcome to move existing checks from the configure +phase to `meson.build`. When doing so, ensure that `meson.build` does +not use anymore the keys that you have removed from `config-host.mak`. +Typically these will be replaced in `meson.build` by boolean variables, +``get_option('optname')`` invocations, or `dep.found()` expressions. +In general, the remaining checks have little or no interdependencies, +so they can be moved one by one. Helper functions ---------------- @@ -335,6 +283,60 @@ new target, or enabling new devices or hardware for a particular system/userspace emulation target +Adding checks +------------- + +New checks should be added to Meson. Compiler checks can be as simple as +the following:: + + config_host_data.set('HAVE_BTRFS_H', cc.has_header('linux/btrfs.h')) + +A more complex task such as adding a new dependency usually +comprises the following tasks: + + - Add a Meson build option to meson_options.txt. + + - Add code to perform the actual feature check. + + - Add code to include the feature status in `config-host.h` + + - Add code to print out the feature status in the configure summary + upon completion. + +Taking the probe for SDL2_Image as an example, we have the following +in ``meson_options.txt``:: + + option('sdl_image', type : 'feature', value : 'auto', + description: 'SDL Image support for icons') + +Unless the option was given a non-``auto`` value (on the configure +command line), the detection code must be performed only if the +dependency will be used:: + + sdl_image = not_found + if not get_option('sdl_image').auto() or have_system + sdl_image = dependency('SDL2_image', required: get_option('sdl_image'), + method: 'pkg-config', + static: enable_static) + endif + +This avoids warnings on static builds of user-mode emulators, for example. +Most of the libraries used by system-mode emulators are not available for +static linking. + +The other supporting code is generally simple:: + + # Create config-host.h (if applicable) + config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found()) + + # Summary + summary_info += {'SDL image support': sdl_image.found()} + +For the configure script to parse the new option, the +``scripts/meson-buildoptions.sh`` file must be up-to-date; ``make +update-buildoptions`` (or just `make`) will take care of updating it. + + Support scripts --------------- diff --git a/meson_options.txt b/meson_options.txt index 5a140af7f7..4ab4570125 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,7 @@ +# These options do not correspond to a --enable/--disable-* option +# on the configure script command line. If you add more, list them in +# scripts/meson-buildoptions.py's SKIP_OPTIONS constant too. + option('qemu_suffix', type : 'string', value: 'qemu', description: 'Suffix for QEMU data/modules/config directories (can be empty)') option('docdir', type : 'string', value : 'doc', @@ -16,6 +20,10 @@ option('fuzzing_engine', type : 'string', value : '', option('trace_file', type: 'string', value: 'trace', description: 'Trace file prefix for simple backend') +# Everything else can be set via --enable/--disable-* option +# on the configure script command line. After adding an option +# here make sure to run "make update-buildoptions". + option('docs', type : 'feature', value : 'auto', description: 'Documentations build support') option('fuzzing', type : 'boolean', value: false, diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py index d48af99aaf..256523c09d 100755 --- a/scripts/meson-buildoptions.py +++ b/scripts/meson-buildoptions.py @@ -25,10 +25,71 @@ import textwrap import shlex import sys +SKIP_OPTIONS = { + "audio_drv_list", + "default_devices", + "docdir", + "fuzzing_engine", + "qemu_firmwarepath", + "qemu_suffix", + "sphinx_build", + "trace_file", +} + +LINE_WIDTH = 76 + + +# Convert the default value of an option to the string used in +# the help message +def value_to_help(value): + if isinstance(value, list): + return ",".join(value) + if isinstance(value, bool): + return "enabled" if value else "disabled" + return str(value) + + +def wrap(left, text, indent): + spaces = " " * indent + if len(left) >= indent: + yield left + left = spaces + else: + left = (left + spaces)[0:indent] + yield from textwrap.wrap( + text, width=LINE_WIDTH, initial_indent=left, subsequent_indent=spaces + ) + + def sh_print(line=""): print(' printf "%s\\n"', shlex.quote(line)) +def help_line(left, opt, indent, long): + right = f'{opt["description"]}' + if long: + value = value_to_help(opt["value"]) + if value != "auto": + right += f" [{value}]" + if "choices" in opt and long: + choices = "/".join(sorted(opt["choices"])) + right += f" (choices: {choices})" + for x in wrap(" " + left, right, indent): + sh_print(x) + + +# Return whether the option (a dictionary) can be used with +# arguments. Booleans can never be used with arguments; +# combos allow an argument only if they accept other values +# than "auto", "enabled", and "disabled". +def allow_arg(opt): + if opt["type"] == "boolean": + return False + if opt["type"] != "combo": + return True + return not (set(opt["choices"]) <= {"auto", "disabled", "enabled"}) + + def load_options(json): json = [ x @@ -42,17 +103,48 @@ def load_options(json): def print_help(options): print("meson_options_help() {") + for opt in options: + key = opt["name"].replace("_", "-") + # The first section includes options that have an arguments, + # and booleans (i.e., only one of enable/disable makes sense) + if opt["type"] == "boolean": + left = f"--disable-{key}" if opt["value"] else f"--enable-{key}" + help_line(left, opt, 27, False) + elif allow_arg(opt): + if opt["type"] == "combo" and "enabled" in opt["choices"]: + left = f"--enable-{key}[=CHOICE]" + else: + left = f"--enable-{key}=CHOICE" + help_line(left, opt, 27, True) + sh_print() sh_print("Optional features, enabled with --enable-FEATURE and") sh_print("disabled with --disable-FEATURE, default is enabled if available") sh_print("(unless built with --without-default-features):") sh_print() + for opt in options: + key = opt["name"].replace("_", "-") + if opt["type"] != "boolean" and not allow_arg(opt): + help_line(key, opt, 18, False) print("}") def print_parse(options): print("_meson_option_parse() {") print(" case $1 in") + for opt in options: + key = opt["name"].replace("_", "-") + name = opt["name"] + if opt["type"] == "boolean": + print(f' --enable-{key}) printf "%s" -D{name}=true ;;') + print(f' --disable-{key}) printf "%s" -D{name}=false ;;') + else: + if opt["type"] == "combo" and "enabled" in opt["choices"]: + print(f' --enable-{key}) printf "%s" -D{name}=enabled ;;') + if opt["type"] == "combo" and "disabled" in opt["choices"]: + print(f' --disable-{key}) printf "%s" -D{name}=disabled ;;') + if allow_arg(opt): + print(f' --enable-{key}=*) quote_sh "-D{name}=$2" ;;') print(" *) return 1 ;;") print(" esac") print("}") diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index c8ae205601..c795a13020 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -1,13 +1,270 @@ # This file is generated by meson-buildoptions.py, do not edit! meson_options_help() { + printf "%s\n" ' --enable-capstone[=CHOICE]' + printf "%s\n" ' Whether and how to find the capstone library' + printf "%s\n" ' (choices: auto/disabled/enabled/internal/system)' + printf "%s\n" ' --enable-cfi Control-Flow Integrity (CFI)' + printf "%s\n" ' --enable-cfi-debug Verbose errors in case of CFI violation' + printf "%s\n" ' --enable-fdt[=CHOICE] Whether and how to find the libfdt library' + printf "%s\n" ' (choices: auto/disabled/enabled/internal/system)' + printf "%s\n" ' --enable-fuzzing build fuzzing targets' + printf "%s\n" ' --disable-install-blobs install provided firmware blobs' + printf "%s\n" ' --enable-malloc=CHOICE choose memory allocator to use [system] (choices:' + printf "%s\n" ' jemalloc/system/tcmalloc)' + printf "%s\n" ' --enable-slirp[=CHOICE] Whether and how to find the slirp library' + printf "%s\n" ' (choices: auto/disabled/enabled/internal/system)' + printf "%s\n" ' --enable-tcg-interpreter TCG with bytecode interpreter (experimental and' + printf "%s\n" ' slow)' + printf "%s\n" ' --enable-trace-backends=CHOICE' + printf "%s\n" ' Set available tracing backends [log] (choices:' + printf "%s\n" ' dtrace/ftrace/log/nop/simple/syslog/ust)' printf "%s\n" '' printf "%s\n" 'Optional features, enabled with --enable-FEATURE and' printf "%s\n" 'disabled with --disable-FEATURE, default is enabled if available' printf "%s\n" '(unless built with --without-default-features):' printf "%s\n" '' + printf "%s\n" ' alsa ALSA sound support' + printf "%s\n" ' attr attr/xattr support' + printf "%s\n" ' auth-pam PAM access control' + printf "%s\n" ' bpf eBPF support' + printf "%s\n" ' brlapi brlapi character device driver' + printf "%s\n" ' bzip2 bzip2 support for DMG images' + printf "%s\n" ' cap-ng cap_ng support' + printf "%s\n" ' cocoa Cocoa user interface (macOS only)' + printf "%s\n" ' coreaudio CoreAudio sound support' + printf "%s\n" ' curl CURL block device driver' + printf "%s\n" ' curses curses UI' + printf "%s\n" ' docs Documentations build support' + printf "%s\n" ' dsound DirectSound sound support' + printf "%s\n" ' fuse FUSE block device export' + printf "%s\n" ' fuse-lseek SEEK_HOLE/SEEK_DATA support for FUSE exports' + printf "%s\n" ' gcrypt libgcrypt cryptography support' + printf "%s\n" ' gettext Localization of the GTK+ user interface' + printf "%s\n" ' glusterfs Glusterfs block device driver' + printf "%s\n" ' gnutls GNUTLS cryptography support' + printf "%s\n" ' gtk GTK+ user interface' + printf "%s\n" ' guest-agent-msi Build MSI package for the QEMU Guest Agent' + printf "%s\n" ' hax HAX acceleration support' + printf "%s\n" ' hvf HVF acceleration support' + printf "%s\n" ' iconv Font glyph conversion support' + printf "%s\n" ' jack JACK sound support' + printf "%s\n" ' kvm KVM acceleration support' + printf "%s\n" ' libdaxctl libdaxctl support' + printf "%s\n" ' libiscsi libiscsi userspace initiator' + printf "%s\n" ' libnfs libnfs block device driver' + printf "%s\n" ' libpmem libpmem support' + printf "%s\n" ' libudev Use libudev to enumerate host devices' + printf "%s\n" ' libusb libusb support for USB passthrough' + printf "%s\n" ' libxml2 libxml2 support for Parallels image format' + printf "%s\n" ' linux-aio Linux AIO support' + printf "%s\n" ' linux-io-uring Linux io_uring support' + printf "%s\n" ' lzfse lzfse support for DMG images' + printf "%s\n" ' lzo lzo compression support' + printf "%s\n" ' malloc-trim enable libc malloc_trim() for memory optimization' + printf "%s\n" ' mpath Multipath persistent reservation passthrough' + printf "%s\n" ' multiprocess Out of process device emulation support' + printf "%s\n" ' netmap netmap network backend support' + printf "%s\n" ' nettle nettle cryptography support' + printf "%s\n" ' nvmm NVMM acceleration support' + printf "%s\n" ' oss OSS sound support' + printf "%s\n" ' pa PulseAudio sound support' + printf "%s\n" ' rbd Ceph block device driver' + printf "%s\n" ' sdl SDL user interface' + printf "%s\n" ' sdl-image SDL Image support for icons' + printf "%s\n" ' seccomp seccomp support' + printf "%s\n" ' smartcard CA smartcard emulation support' + printf "%s\n" ' snappy snappy compression support' + printf "%s\n" ' sparse sparse checker' + printf "%s\n" ' spice Spice server support' + printf "%s\n" ' spice-protocol Spice protocol support' + printf "%s\n" ' tcg TCG support' + printf "%s\n" ' u2f U2F emulation support' + printf "%s\n" ' usb-redir libusbredir support' + printf "%s\n" ' vde vde network backend support' + printf "%s\n" ' vhost-user-blk-server' + printf "%s\n" ' build vhost-user-blk server' + printf "%s\n" ' virglrenderer virgl rendering support' + printf "%s\n" ' virtfs virtio-9p support' + printf "%s\n" ' virtiofsd build virtiofs daemon (virtiofsd)' + printf "%s\n" ' vnc VNC server' + printf "%s\n" ' vnc-jpeg JPEG lossy compression for VNC server' + printf "%s\n" ' vnc-png PNG compression for VNC server' + printf "%s\n" ' vnc-sasl SASL authentication for VNC server' + printf "%s\n" ' vte vte support for the gtk UI' + printf "%s\n" ' whpx WHPX acceleration support' + printf "%s\n" ' xen Xen backend support' + printf "%s\n" ' xen-pci-passthrough' + printf "%s\n" ' Xen PCI passthrough support' + printf "%s\n" ' xkbcommon xkbcommon support' + printf "%s\n" ' zstd zstd compression support' } _meson_option_parse() { case $1 in + --enable-alsa) printf "%s" -Dalsa=enabled ;; + --disable-alsa) printf "%s" -Dalsa=disabled ;; + --enable-attr) printf "%s" -Dattr=enabled ;; + --disable-attr) printf "%s" -Dattr=disabled ;; + --enable-auth-pam) printf "%s" -Dauth_pam=enabled ;; + --disable-auth-pam) printf "%s" -Dauth_pam=disabled ;; + --enable-bpf) printf "%s" -Dbpf=enabled ;; + --disable-bpf) printf "%s" -Dbpf=disabled ;; + --enable-brlapi) printf "%s" -Dbrlapi=enabled ;; + --disable-brlapi) printf "%s" -Dbrlapi=disabled ;; + --enable-bzip2) printf "%s" -Dbzip2=enabled ;; + --disable-bzip2) printf "%s" -Dbzip2=disabled ;; + --enable-cap-ng) printf "%s" -Dcap_ng=enabled ;; + --disable-cap-ng) printf "%s" -Dcap_ng=disabled ;; + --enable-capstone) printf "%s" -Dcapstone=enabled ;; + --disable-capstone) printf "%s" -Dcapstone=disabled ;; + --enable-capstone=*) quote_sh "-Dcapstone=$2" ;; + --enable-cfi) printf "%s" -Dcfi=true ;; + --disable-cfi) printf "%s" -Dcfi=false ;; + --enable-cfi-debug) printf "%s" -Dcfi_debug=true ;; + --disable-cfi-debug) printf "%s" -Dcfi_debug=false ;; + --enable-cocoa) printf "%s" -Dcocoa=enabled ;; + --disable-cocoa) printf "%s" -Dcocoa=disabled ;; + --enable-coreaudio) printf "%s" -Dcoreaudio=enabled ;; + --disable-coreaudio) printf "%s" -Dcoreaudio=disabled ;; + --enable-curl) printf "%s" -Dcurl=enabled ;; + --disable-curl) printf "%s" -Dcurl=disabled ;; + --enable-curses) printf "%s" -Dcurses=enabled ;; + --disable-curses) printf "%s" -Dcurses=disabled ;; + --enable-docs) printf "%s" -Ddocs=enabled ;; + --disable-docs) printf "%s" -Ddocs=disabled ;; + --enable-dsound) printf "%s" -Ddsound=enabled ;; + --disable-dsound) printf "%s" -Ddsound=disabled ;; + --enable-fdt) printf "%s" -Dfdt=enabled ;; + --disable-fdt) printf "%s" -Dfdt=disabled ;; + --enable-fdt=*) quote_sh "-Dfdt=$2" ;; + --enable-fuse) printf "%s" -Dfuse=enabled ;; + --disable-fuse) printf "%s" -Dfuse=disabled ;; + --enable-fuse-lseek) printf "%s" -Dfuse_lseek=enabled ;; + --disable-fuse-lseek) printf "%s" -Dfuse_lseek=disabled ;; + --enable-fuzzing) printf "%s" -Dfuzzing=true ;; + --disable-fuzzing) printf "%s" -Dfuzzing=false ;; + --enable-gcrypt) printf "%s" -Dgcrypt=enabled ;; + --disable-gcrypt) printf "%s" -Dgcrypt=disabled ;; + --enable-gettext) printf "%s" -Dgettext=enabled ;; + --disable-gettext) printf "%s" -Dgettext=disabled ;; + --enable-glusterfs) printf "%s" -Dglusterfs=enabled ;; + --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; + --enable-gnutls) printf "%s" -Dgnutls=enabled ;; + --disable-gnutls) printf "%s" -Dgnutls=disabled ;; + --enable-gtk) printf "%s" -Dgtk=enabled ;; + --disable-gtk) printf "%s" -Dgtk=disabled ;; + --enable-guest-agent-msi) printf "%s" -Dguest_agent_msi=enabled ;; + --disable-guest-agent-msi) printf "%s" -Dguest_agent_msi=disabled ;; + --enable-hax) printf "%s" -Dhax=enabled ;; + --disable-hax) printf "%s" -Dhax=disabled ;; + --enable-hvf) printf "%s" -Dhvf=enabled ;; + --disable-hvf) printf "%s" -Dhvf=disabled ;; + --enable-iconv) printf "%s" -Diconv=enabled ;; + --disable-iconv) printf "%s" -Diconv=disabled ;; + --enable-install-blobs) printf "%s" -Dinstall_blobs=true ;; + --disable-install-blobs) printf "%s" -Dinstall_blobs=false ;; + --enable-jack) printf "%s" -Djack=enabled ;; + --disable-jack) printf "%s" -Djack=disabled ;; + --enable-kvm) printf "%s" -Dkvm=enabled ;; + --disable-kvm) printf "%s" -Dkvm=disabled ;; + --enable-libdaxctl) printf "%s" -Dlibdaxctl=enabled ;; + --disable-libdaxctl) printf "%s" -Dlibdaxctl=disabled ;; + --enable-libiscsi) printf "%s" -Dlibiscsi=enabled ;; + --disable-libiscsi) printf "%s" -Dlibiscsi=disabled ;; + --enable-libnfs) printf "%s" -Dlibnfs=enabled ;; + --disable-libnfs) printf "%s" -Dlibnfs=disabled ;; + --enable-libpmem) printf "%s" -Dlibpmem=enabled ;; + --disable-libpmem) printf "%s" -Dlibpmem=disabled ;; + --enable-libudev) printf "%s" -Dlibudev=enabled ;; + --disable-libudev) printf "%s" -Dlibudev=disabled ;; + --enable-libusb) printf "%s" -Dlibusb=enabled ;; + --disable-libusb) printf "%s" -Dlibusb=disabled ;; + --enable-libxml2) printf "%s" -Dlibxml2=enabled ;; + --disable-libxml2) printf "%s" -Dlibxml2=disabled ;; + --enable-linux-aio) printf "%s" -Dlinux_aio=enabled ;; + --disable-linux-aio) printf "%s" -Dlinux_aio=disabled ;; + --enable-linux-io-uring) printf "%s" -Dlinux_io_uring=enabled ;; + --disable-linux-io-uring) printf "%s" -Dlinux_io_uring=disabled ;; + --enable-lzfse) printf "%s" -Dlzfse=enabled ;; + --disable-lzfse) printf "%s" -Dlzfse=disabled ;; + --enable-lzo) printf "%s" -Dlzo=enabled ;; + --disable-lzo) printf "%s" -Dlzo=disabled ;; + --enable-malloc=*) quote_sh "-Dmalloc=$2" ;; + --enable-malloc-trim) printf "%s" -Dmalloc_trim=enabled ;; + --disable-malloc-trim) printf "%s" -Dmalloc_trim=disabled ;; + --enable-mpath) printf "%s" -Dmpath=enabled ;; + --disable-mpath) printf "%s" -Dmpath=disabled ;; + --enable-multiprocess) printf "%s" -Dmultiprocess=enabled ;; + --disable-multiprocess) printf "%s" -Dmultiprocess=disabled ;; + --enable-netmap) printf "%s" -Dnetmap=enabled ;; + --disable-netmap) printf "%s" -Dnetmap=disabled ;; + --enable-nettle) printf "%s" -Dnettle=enabled ;; + --disable-nettle) printf "%s" -Dnettle=disabled ;; + --enable-nvmm) printf "%s" -Dnvmm=enabled ;; + --disable-nvmm) printf "%s" -Dnvmm=disabled ;; + --enable-oss) printf "%s" -Doss=enabled ;; + --disable-oss) printf "%s" -Doss=disabled ;; + --enable-pa) printf "%s" -Dpa=enabled ;; + --disable-pa) printf "%s" -Dpa=disabled ;; + --enable-rbd) printf "%s" -Drbd=enabled ;; + --disable-rbd) printf "%s" -Drbd=disabled ;; + --enable-sdl) printf "%s" -Dsdl=enabled ;; + --disable-sdl) printf "%s" -Dsdl=disabled ;; + --enable-sdl-image) printf "%s" -Dsdl_image=enabled ;; + --disable-sdl-image) printf "%s" -Dsdl_image=disabled ;; + --enable-seccomp) printf "%s" -Dseccomp=enabled ;; + --disable-seccomp) printf "%s" -Dseccomp=disabled ;; + --enable-slirp) printf "%s" -Dslirp=enabled ;; + --disable-slirp) printf "%s" -Dslirp=disabled ;; + --enable-slirp=*) quote_sh "-Dslirp=$2" ;; + --enable-smartcard) printf "%s" -Dsmartcard=enabled ;; + --disable-smartcard) printf "%s" -Dsmartcard=disabled ;; + --enable-snappy) printf "%s" -Dsnappy=enabled ;; + --disable-snappy) printf "%s" -Dsnappy=disabled ;; + --enable-sparse) printf "%s" -Dsparse=enabled ;; + --disable-sparse) printf "%s" -Dsparse=disabled ;; + --enable-spice) printf "%s" -Dspice=enabled ;; + --disable-spice) printf "%s" -Dspice=disabled ;; + --enable-spice-protocol) printf "%s" -Dspice_protocol=enabled ;; + --disable-spice-protocol) printf "%s" -Dspice_protocol=disabled ;; + --enable-tcg) printf "%s" -Dtcg=enabled ;; + --disable-tcg) printf "%s" -Dtcg=disabled ;; + --enable-tcg-interpreter) printf "%s" -Dtcg_interpreter=true ;; + --disable-tcg-interpreter) printf "%s" -Dtcg_interpreter=false ;; + --enable-trace-backends=*) quote_sh "-Dtrace_backends=$2" ;; + --enable-u2f) printf "%s" -Du2f=enabled ;; + --disable-u2f) printf "%s" -Du2f=disabled ;; + --enable-usb-redir) printf "%s" -Dusb_redir=enabled ;; + --disable-usb-redir) printf "%s" -Dusb_redir=disabled ;; + --enable-vde) printf "%s" -Dvde=enabled ;; + --disable-vde) printf "%s" -Dvde=disabled ;; + --enable-vhost-user-blk-server) printf "%s" -Dvhost_user_blk_server=enabled ;; + --disable-vhost-user-blk-server) printf "%s" -Dvhost_user_blk_server=disabled ;; + --enable-virglrenderer) printf "%s" -Dvirglrenderer=enabled ;; + --disable-virglrenderer) printf "%s" -Dvirglrenderer=disabled ;; + --enable-virtfs) printf "%s" -Dvirtfs=enabled ;; + --disable-virtfs) printf "%s" -Dvirtfs=disabled ;; + --enable-virtiofsd) printf "%s" -Dvirtiofsd=enabled ;; + --disable-virtiofsd) printf "%s" -Dvirtiofsd=disabled ;; + --enable-vnc) printf "%s" -Dvnc=enabled ;; + --disable-vnc) printf "%s" -Dvnc=disabled ;; + --enable-vnc-jpeg) printf "%s" -Dvnc_jpeg=enabled ;; + --disable-vnc-jpeg) printf "%s" -Dvnc_jpeg=disabled ;; + --enable-vnc-png) printf "%s" -Dvnc_png=enabled ;; + --disable-vnc-png) printf "%s" -Dvnc_png=disabled ;; + --enable-vnc-sasl) printf "%s" -Dvnc_sasl=enabled ;; + --disable-vnc-sasl) printf "%s" -Dvnc_sasl=disabled ;; + --enable-vte) printf "%s" -Dvte=enabled ;; + --disable-vte) printf "%s" -Dvte=disabled ;; + --enable-whpx) printf "%s" -Dwhpx=enabled ;; + --disable-whpx) printf "%s" -Dwhpx=disabled ;; + --enable-xen) printf "%s" -Dxen=enabled ;; + --disable-xen) printf "%s" -Dxen=disabled ;; + --enable-xen-pci-passthrough) printf "%s" -Dxen_pci_passthrough=enabled ;; + --disable-xen-pci-passthrough) printf "%s" -Dxen_pci_passthrough=disabled ;; + --enable-xkbcommon) printf "%s" -Dxkbcommon=enabled ;; + --disable-xkbcommon) printf "%s" -Dxkbcommon=disabled ;; + --enable-zstd) printf "%s" -Dzstd=enabled ;; + --disable-zstd) printf "%s" -Dzstd=disabled ;; *) return 1 ;; esac } From e287bf7bb15ffd3728c000d9c5b52460ea17d713 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:28 +0200 Subject: [PATCH 0511/1334] net: Introduce NetClientInfo.check_peer_type() Some network backends (vhost-user and vhost-vdpa) work only with specific devices. At startup, they second guess what the command line option handling will do and error out if they think a non-virtio device will attach to them. This second guessing is not only ugly, it can lead to wrong error messages ('-device floppy,netdev=foo' should complain about an unknown property, not about the wrong kind of network device being attached) and completely ignores hotplugging. Add a callback where backends can check compatibility with a device when it actually tries to attach, even on hotplug. Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-2-kwolf@redhat.com> Reviewed-by: Damien Hedde Acked-by: Jason Wang Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/core/qdev-properties-system.c | 6 ++++++ include/net/net.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index e71f5d64d1..a91f60567a 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -431,6 +431,12 @@ static void set_netdev(Object *obj, Visitor *v, const char *name, goto out; } + if (peers[i]->info->check_peer_type) { + if (!peers[i]->info->check_peer_type(peers[i], obj->class, errp)) { + goto out; + } + } + ncs[i] = peers[i]; ncs[i]->queue_index = i; } diff --git a/include/net/net.h b/include/net/net.h index 5d1508081f..986288eb07 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -62,6 +62,7 @@ typedef struct SocketReadState SocketReadState; typedef void (SocketReadStateFinalize)(SocketReadState *rs); typedef void (NetAnnounce)(NetClientState *); typedef bool (SetSteeringEBPF)(NetClientState *, int); +typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **); typedef struct NetClientInfo { NetClientDriver type; @@ -84,6 +85,7 @@ typedef struct NetClientInfo { SetVnetBE *set_vnet_be; NetAnnounce *announce; SetSteeringEBPF *set_steering_ebpf; + NetCheckPeerType *check_peer_type; } NetClientInfo; struct NetClientState { From 5c485d51c4e2e8b3c78110a1a3f31f789c900d9d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:29 +0200 Subject: [PATCH 0512/1334] net/vhost-user: Fix device compatibility check vhost-user works only with specific devices. At startup, it second guesses what the command line option handling will do and error out if it thinks a non-virtio device will attach to them. This second guessing is not only ugly, it can lead to wrong error messages ('-device floppy,netdev=foo' should complain about an unknown property, not about the wrong kind of network device being attached) and completely ignores hotplugging. Drop the old checks and implement .check_peer_type() instead to fix this. As a nice side effect, it also removes one more dependency on the legacy QemuOpts infrastructure and even reduces the code size. Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-3-kwolf@redhat.com> Reviewed-by: Damien Hedde Acked-by: Jason Wang Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- net/vhost-user.c | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/net/vhost-user.c b/net/vhost-user.c index 4a939124d2..b1a0247b59 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -198,6 +198,19 @@ static bool vhost_user_has_ufo(NetClientState *nc) return true; } +static bool vhost_user_check_peer_type(NetClientState *nc, ObjectClass *oc, + Error **errp) +{ + const char *driver = object_class_get_name(oc); + + if (!g_str_has_prefix(driver, "virtio-net-")) { + error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); + return false; + } + + return true; +} + static NetClientInfo net_vhost_user_info = { .type = NET_CLIENT_DRIVER_VHOST_USER, .size = sizeof(NetVhostUserState), @@ -207,6 +220,7 @@ static NetClientInfo net_vhost_user_info = { .has_ufo = vhost_user_has_ufo, .set_vnet_be = vhost_user_set_vnet_endianness, .set_vnet_le = vhost_user_set_vnet_endianness, + .check_peer_type = vhost_user_check_peer_type, }; static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond, @@ -397,27 +411,6 @@ static Chardev *net_vhost_claim_chardev( return chr; } -static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) -{ - const char *name = opaque; - const char *driver, *netdev; - - driver = qemu_opt_get(opts, "driver"); - netdev = qemu_opt_get(opts, "netdev"); - - if (!driver || !netdev) { - return 0; - } - - if (strcmp(netdev, name) == 0 && - !g_str_has_prefix(driver, "virtio-net-")) { - error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); - return -1; - } - - return 0; -} - int net_init_vhost_user(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { @@ -433,12 +426,6 @@ int net_init_vhost_user(const Netdev *netdev, const char *name, return -1; } - /* verify net frontend */ - if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, - (char *)name, errp)) { - return -1; - } - queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1; if (queues < 1 || queues > MAX_QUEUE_NUM) { error_setg(errp, From ee8a1c63d337a893fa915dd263dfdaa272e1a76a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:30 +0200 Subject: [PATCH 0513/1334] net/vhost-vdpa: Fix device compatibility check vhost-vdpa works only with specific devices. At startup, it second guesses what the command line option handling will do and error out if it thinks a non-virtio device will attach to them. This second guessing is not only ugly, it can lead to wrong error messages ('-device floppy,netdev=foo' should complain about an unknown property, not about the wrong kind of network device being attached) and completely ignores hotplugging. Drop the old checks and implement .check_peer_type() instead to fix this. As a nice side effect, it also removes one more dependency on the legacy QemuOpts infrastructure and even reduces the code size. Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-4-kwolf@redhat.com> Reviewed-by: Damien Hedde Acked-by: Jason Wang Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- net/vhost-vdpa.c | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 912686457c..6dc68d8677 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -147,12 +147,26 @@ static bool vhost_vdpa_has_ufo(NetClientState *nc) } +static bool vhost_vdpa_check_peer_type(NetClientState *nc, ObjectClass *oc, + Error **errp) +{ + const char *driver = object_class_get_name(oc); + + if (!g_str_has_prefix(driver, "virtio-net-")) { + error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); + return false; + } + + return true; +} + static NetClientInfo net_vhost_vdpa_info = { .type = NET_CLIENT_DRIVER_VHOST_VDPA, .size = sizeof(VhostVDPAState), .cleanup = vhost_vdpa_cleanup, .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, .has_ufo = vhost_vdpa_has_ufo, + .check_peer_type = vhost_vdpa_check_peer_type, }; static int net_vhost_vdpa_init(NetClientState *peer, const char *device, @@ -179,24 +193,6 @@ static int net_vhost_vdpa_init(NetClientState *peer, const char *device, return ret; } -static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) -{ - const char *name = opaque; - const char *driver, *netdev; - - driver = qemu_opt_get(opts, "driver"); - netdev = qemu_opt_get(opts, "netdev"); - if (!driver || !netdev) { - return 0; - } - if (strcmp(netdev, name) == 0 && - !g_str_has_prefix(driver, "virtio-net-")) { - error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); - return -1; - } - return 0; -} - int net_init_vhost_vdpa(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { @@ -204,10 +200,5 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); opts = &netdev->u.vhost_vdpa; - /* verify net frontend */ - if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, - (char *)name, errp)) { - return -1; - } return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); } From dbc8221f8cf3a3004fb6b4d0675f5852758b0388 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:31 +0200 Subject: [PATCH 0514/1334] qom: Reduce use of error_propagate() ERRP_GUARD() makes debugging easier by making sure that &error_abort still fails at the real origin of the error instead of error_propagate(). Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-5-kwolf@redhat.com> Reviewed-by: Markus Armbruster Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- qom/object.c | 7 +++---- qom/object_interfaces.c | 19 ++++++------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/qom/object.c b/qom/object.c index e86cb05b84..6be710bc40 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1389,7 +1389,7 @@ bool object_property_get(Object *obj, const char *name, Visitor *v, bool object_property_set(Object *obj, const char *name, Visitor *v, Error **errp) { - Error *err = NULL; + ERRP_GUARD(); ObjectProperty *prop = object_property_find_err(obj, name, errp); if (prop == NULL) { @@ -1400,9 +1400,8 @@ bool object_property_set(Object *obj, const char *name, Visitor *v, error_setg(errp, QERR_PERMISSION_DENIED); return false; } - prop->set(obj, v, name, prop->opaque, &err); - error_propagate(errp, err); - return !err; + prop->set(obj, v, name, prop->opaque, errp); + return !*errp; } bool object_property_set_str(Object *obj, const char *name, diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index ad9b56b59a..3b61c195c5 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -46,25 +46,18 @@ static void object_set_properties_from_qdict(Object *obj, const QDict *qdict, Visitor *v, Error **errp) { const QDictEntry *e; - Error *local_err = NULL; - if (!visit_start_struct(v, NULL, NULL, 0, &local_err)) { - goto out; + if (!visit_start_struct(v, NULL, NULL, 0, errp)) { + return; } for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { - if (!object_property_set(obj, e->key, v, &local_err)) { - break; + if (!object_property_set(obj, e->key, v, errp)) { + goto out; } } - if (!local_err) { - visit_check_struct(v, &local_err); - } - visit_end_struct(v, NULL); - + visit_check_struct(v, errp); out: - if (local_err) { - error_propagate(errp, local_err); - } + visit_end_struct(v, NULL); } void object_set_properties_from_keyval(Object *obj, const QDict *qdict, From e2c8eb1454b6aee79077d6d9710ec92d66554e56 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:32 +0200 Subject: [PATCH 0515/1334] iotests/245: Fix type for iothread property iothread is a string property, so None (= JSON null) is not a valid value for it. Pass the empty string instead to get the default iothread. Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211008133442.141332-6-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- tests/qemu-iotests/245 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index bf8261eec0..9b12b42eed 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -1189,10 +1189,10 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.run_test_iothreads('iothread0', 'iothread0') def test_iothreads_switch_backing(self): - self.run_test_iothreads('iothread0', None) + self.run_test_iothreads('iothread0', '') def test_iothreads_switch_overlay(self): - self.run_test_iothreads(None, 'iothread0') + self.run_test_iothreads('', 'iothread0') if __name__ == '__main__': iotests.activate_logging() From af6400afb89f5eb3f66f841c8ee8c2f6754c8394 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:33 +0200 Subject: [PATCH 0516/1334] iotests/051: Fix typo The iothread isn't called 'iothread0', but 'thread0'. Depending on the order that properties are parsed, the error message may change from the expected one to another one saying that the iothread doesn't exist. Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211008133442.141332-7-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- tests/qemu-iotests/051 | 2 +- tests/qemu-iotests/051.pc.out | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 7bf29343d7..1d2fa93a11 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -199,7 +199,7 @@ case "$QEMU_DEFAULT_MACHINE" in # virtio-blk enables the iothread only when the driver initialises the # device, so a second virtio-blk device can't be added even with the # same iothread. virtio-scsi allows this. - run_qemu $iothread -device virtio-blk-pci,drive=disk,iothread=iothread0,share-rw=on + run_qemu $iothread -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on run_qemu $iothread -device virtio-scsi,id=virtio-scsi1,iothread=thread0 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on ;; *) diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index afe7632964..063e4fc584 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -183,9 +183,9 @@ Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on: Cannot change iothread of active block backend -Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,iothread=iothread0,share-rw=on +Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device virtio-blk-pci,drive=disk,iothread=iothread0,share-rw=on: Cannot change iothread of active block backend +(qemu) QEMU_PROG: -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on: Cannot change iothread of active block backend Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-scsi,id=virtio-scsi1,iothread=thread0 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information From c34efecedd0552ee8b830402241e19daebb22aec Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:34 +0200 Subject: [PATCH 0517/1334] qdev: Avoid using string visitor for properties The only thing the string visitor adds compared to a keyval visitor is list support. git grep for 'visit_start_list' and 'visit.*List' shows that devices don't make use of this. In a world with a QAPIfied command line interface, the keyval visitor is used to parse the command line. In order to make sure that no devices start using this feature that would make backwards compatibility harder, just switch away from object_property_parse(), which internally uses the string visitor, to a keyval visitor and object_property_set(). Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Message-Id: <20211008133442.141332-8-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- softmmu/qdev-monitor.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 3df99ce9fc..672f87ed4f 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -28,6 +28,8 @@ #include "qapi/qmp/dispatch.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qobject-input-visitor.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/help_option.h" @@ -198,16 +200,28 @@ static int set_property(void *opaque, const char *name, const char *value, Error **errp) { Object *obj = opaque; + QString *val; + Visitor *v; + int ret; if (strcmp(name, "driver") == 0) return 0; if (strcmp(name, "bus") == 0) return 0; - if (!object_property_parse(obj, name, value, errp)) { - return -1; + val = qstring_from_str(value); + v = qobject_input_visitor_new_keyval(QOBJECT(val)); + + if (!object_property_set(obj, name, v, errp)) { + ret = -1; + goto out; } - return 0; + + ret = 0; +out: + visit_free(v); + qobject_unref(val); + return ret; } static const char *find_typename_by_alias(const char *alias) From 163f384752dd9125ce7eb1b2edf00b23f0a54557 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:35 +0200 Subject: [PATCH 0518/1334] qdev: Make DeviceState.id independent of QemuOpts DeviceState.id is a pointer to a string that is stored in the QemuOpts object DeviceState.opts and freed together with it. We want to create devices without going through QemuOpts in the future, so make this a separately allocated string. Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211008133442.141332-9-kwolf@redhat.com> Reviewed-by: Damien Hedde Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/arm/virt.c | 2 +- hw/core/qdev.c | 1 + hw/pci-bridge/pci_expander_bridge.c | 2 +- hw/ppc/e500.c | 2 +- include/hw/qdev-core.h | 2 +- include/monitor/qdev.h | 2 +- softmmu/qdev-monitor.c | 5 +++-- 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 7170aaacd5..4160d49688 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1459,7 +1459,7 @@ static void create_platform_bus(VirtMachineState *vms) MemoryRegion *sysmem = get_system_memory(); dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE); - dev->id = TYPE_PLATFORM_BUS_DEVICE; + dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE); qdev_prop_set_uint32(dev, "num_irqs", PLATFORM_BUS_NUM_IRQS); qdev_prop_set_uint32(dev, "mmio_size", vms->memmap[VIRT_PLATFORM_BUS].size); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/core/qdev.c b/hw/core/qdev.c index cefc5eaa0a..d918b50a1d 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -956,6 +956,7 @@ static void device_finalize(Object *obj) } qemu_opts_del(dev->opts); + g_free(dev->id); } static void device_class_base_init(ObjectClass *class, void *data) diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 7112dc3062..10e6e7c2ab 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -245,7 +245,7 @@ static void pxb_dev_realize_common(PCIDevice *dev, bool pcie, Error **errp) } else { bus = pci_root_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); bds = qdev_new("pci-bridge"); - bds->id = dev_name; + bds->id = g_strdup(dev_name); qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); } diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 95451414dd..960e7efcd3 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -1006,7 +1006,7 @@ void ppce500_init(MachineState *machine) /* Platform Bus Device */ if (pmc->has_platform_bus) { dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE); - dev->id = TYPE_PLATFORM_BUS_DEVICE; + dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE); qdev_prop_set_uint32(dev, "num_irqs", pmc->platform_bus_num_irqs); qdev_prop_set_uint32(dev, "mmio_size", pmc->platform_bus_size); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 4ff19c714b..5a073fc368 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -176,7 +176,7 @@ struct DeviceState { Object parent_obj; /*< public >*/ - const char *id; + char *id; char *canonical_path; bool realized; bool pending_deleted_event; diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h index eaa947d73a..389287eb44 100644 --- a/include/monitor/qdev.h +++ b/include/monitor/qdev.h @@ -9,6 +9,6 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp); int qdev_device_help(QemuOpts *opts); DeviceState *qdev_device_add(QemuOpts *opts, Error **errp); -void qdev_set_id(DeviceState *dev, const char *id); +void qdev_set_id(DeviceState *dev, char *id); #endif diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 672f87ed4f..b7c2d69207 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -592,7 +592,8 @@ static BusState *qbus_find(const char *path, Error **errp) return bus; } -void qdev_set_id(DeviceState *dev, const char *id) +/* Takes ownership of @id, will be freed when deleting the device */ +void qdev_set_id(DeviceState *dev, char *id) { if (id) { dev->id = id; @@ -690,7 +691,7 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) } } - qdev_set_id(dev, qemu_opts_id(opts)); + qdev_set_id(dev, g_strdup(qemu_opts_id(opts))); /* set properties */ if (qemu_opt_foreach(opts, set_property, dev, errp)) { From 4a1d937796de0fecd8b22d7dbebf87f38e8282fd Mon Sep 17 00:00:00 2001 From: Damien Hedde Date: Fri, 8 Oct 2021 15:34:36 +0200 Subject: [PATCH 0519/1334] softmmu/qdev-monitor: add error handling in qdev_set_id qdev_set_id() is mostly used when the user adds a device (using -device cli option or device_add qmp command). This commit adds an error parameter to handle the case where the given id is already taken. Also document the function and add a return value in order to be able to capture success/failure: the function now returns the id in case of success, or NULL in case of failure. The commit modifies the 2 calling places (qdev-monitor and xen-legacy-backend) to add the error object parameter. Note that the id is, right now, guaranteed to be unique because all ids came from the "device" QemuOptsList where the id is used as key. This addition is a preparation for a future commit which will relax the uniqueness. Signed-off-by: Damien Hedde Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-10-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/xen/xen-legacy-backend.c | 3 ++- include/monitor/qdev.h | 25 +++++++++++++++++++++++- softmmu/qdev-monitor.c | 39 +++++++++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index be3cf4a195..085fd31ef7 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -276,7 +276,8 @@ static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom, xendev = g_malloc0(ops->size); object_initialize(&xendev->qdev, ops->size, TYPE_XENBACKEND); OBJECT(xendev)->free = g_free; - qdev_set_id(DEVICE(xendev), g_strdup_printf("xen-%s-%d", type, dev)); + qdev_set_id(DEVICE(xendev), g_strdup_printf("xen-%s-%d", type, dev), + &error_fatal); qdev_realize(DEVICE(xendev), xen_sysbus, &error_fatal); object_unref(OBJECT(xendev)); diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h index 389287eb44..74e6c55a2b 100644 --- a/include/monitor/qdev.h +++ b/include/monitor/qdev.h @@ -9,6 +9,29 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp); int qdev_device_help(QemuOpts *opts); DeviceState *qdev_device_add(QemuOpts *opts, Error **errp); -void qdev_set_id(DeviceState *dev, char *id); + +/** + * qdev_set_id: parent the device and set its id if provided. + * @dev: device to handle + * @id: id to be given to the device, or NULL. + * + * Returns: the id of the device in case of success; otherwise NULL. + * + * @dev must be unrealized, unparented and must not have an id. + * + * If @id is non-NULL, this function tries to setup @dev qom path as + * "/peripheral/id". If @id is already taken, it fails. If it succeeds, + * the id field of @dev is set to @id (@dev now owns the given @id + * parameter). + * + * If @id is NULL, this function generates a unique name and setups @dev + * qom path as "/peripheral-anon/name". This name is not set as the id + * of @dev. + * + * Upon success, it returns the id/name (generated or provided). The + * returned string is owned by the corresponding child property and must + * not be freed by the caller. + */ +const char *qdev_set_id(DeviceState *dev, char *id, Error **errp); #endif diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index b7c2d69207..0b6833cc57 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -593,22 +593,35 @@ static BusState *qbus_find(const char *path, Error **errp) } /* Takes ownership of @id, will be freed when deleting the device */ -void qdev_set_id(DeviceState *dev, char *id) +const char *qdev_set_id(DeviceState *dev, char *id, Error **errp) { - if (id) { - dev->id = id; - } + ObjectProperty *prop; - if (dev->id) { - object_property_add_child(qdev_get_peripheral(), dev->id, - OBJECT(dev)); + assert(!dev->id && !dev->realized); + + /* + * object_property_[try_]add_child() below will assert the device + * has no parent + */ + if (id) { + prop = object_property_try_add_child(qdev_get_peripheral(), id, + OBJECT(dev), NULL); + if (prop) { + dev->id = id; + } else { + g_free(id); + error_setg(errp, "Duplicate device ID '%s'", id); + return NULL; + } } else { static int anon_count; gchar *name = g_strdup_printf("device[%d]", anon_count++); - object_property_add_child(qdev_get_peripheral_anon(), name, - OBJECT(dev)); + prop = object_property_add_child(qdev_get_peripheral_anon(), name, + OBJECT(dev)); g_free(name); } + + return prop->name; } DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) @@ -691,7 +704,13 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) } } - qdev_set_id(dev, g_strdup(qemu_opts_id(opts))); + /* + * set dev's parent and register its id. + * If it fails it means the id is already taken. + */ + if (!qdev_set_id(dev, g_strdup(qemu_opts_id(opts)), errp)) { + goto err_del_dev; + } /* set properties */ if (qemu_opt_foreach(opts, set_property, dev, errp)) { From 30648dd5d609d111e635112d7e6014ca63f7ba13 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:37 +0200 Subject: [PATCH 0520/1334] qemu-option: Allow deleting opts during qemu_opts_foreach() Use QTAILQ_FOREACH_SAFE() so that the current QemuOpts can be deleted while iterating through the whole list. Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211008133442.141332-11-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- util/qemu-option.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/qemu-option.c b/util/qemu-option.c index 61cb4a97bd..eedd08929b 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -1126,11 +1126,11 @@ int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque, Error **errp) { Location loc; - QemuOpts *opts; + QemuOpts *opts, *next; int rc = 0; loc_push_none(&loc); - QTAILQ_FOREACH(opts, &list->head, next) { + QTAILQ_FOREACH_SAFE(opts, &list->head, next, next) { loc_restore(&opts->loc); rc = func(opaque, opts, errp); if (rc) { From 7d61808206cc651ae42024ab6def827afa2f807b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:38 +0200 Subject: [PATCH 0521/1334] qdev: Add Error parameter to hide_device() callbacks hide_device() is used for virtio-net failover, where the standby virtio device delays creation of the primary device. It only makes sense to have a single primary device for each standby device. Adding a second one should result in an error instead of hiding it and never using it afterwards. Prepare for this by adding an Error parameter to the hide_device() callback where virtio-net is informed about adding a primary device. Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-12-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/core/qdev.c | 7 +++++-- hw/net/virtio-net.c | 2 +- include/hw/qdev-core.h | 8 ++++++-- softmmu/qdev-monitor.c | 5 ++++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index d918b50a1d..c3a021c444 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -211,14 +211,17 @@ void device_listener_unregister(DeviceListener *listener) QTAILQ_REMOVE(&device_listeners, listener, link); } -bool qdev_should_hide_device(QemuOpts *opts) +bool qdev_should_hide_device(QemuOpts *opts, Error **errp) { + ERRP_GUARD(); DeviceListener *listener; QTAILQ_FOREACH(listener, &device_listeners, link) { if (listener->hide_device) { - if (listener->hide_device(listener, opts)) { + if (listener->hide_device(listener, opts, errp)) { return true; + } else if (*errp) { + return false; } } } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index f205331dcf..a17d5739fc 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3304,7 +3304,7 @@ static void virtio_net_migration_state_notifier(Notifier *notifier, void *data) } static bool failover_hide_primary_device(DeviceListener *listener, - QemuOpts *device_opts) + QemuOpts *device_opts, Error **errp) { VirtIONet *n = container_of(listener, VirtIONet, primary_listener); const char *standby_id; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 5a073fc368..74d8b614a7 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -201,8 +201,12 @@ struct DeviceListener { * informs qdev if a device should be visible or hidden. We can * hide a failover device depending for example on the device * opts. + * + * On errors, it returns false and errp is set. Device creation + * should fail in this case. */ - bool (*hide_device)(DeviceListener *listener, QemuOpts *device_opts); + bool (*hide_device)(DeviceListener *listener, QemuOpts *device_opts, + Error **errp); QTAILQ_ENTRY(DeviceListener) link; }; @@ -837,7 +841,7 @@ void device_listener_unregister(DeviceListener *listener); * When a device is added via qdev_device_add() this will be called, * and return if the device should be added now or not. */ -bool qdev_should_hide_device(QemuOpts *opts); +bool qdev_should_hide_device(QemuOpts *opts, Error **errp); typedef enum MachineInitPhase { /* current_machine is NULL. */ diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 0b6833cc57..ea737db028 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -626,6 +626,7 @@ const char *qdev_set_id(DeviceState *dev, char *id, Error **errp) DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) { + ERRP_GUARD(); DeviceClass *dc; const char *driver, *path; DeviceState *dev = NULL; @@ -669,11 +670,13 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) error_setg(errp, "Device with failover_pair_id don't have id"); return NULL; } - if (qdev_should_hide_device(opts)) { + if (qdev_should_hide_device(opts, errp)) { if (bus && !qbus_is_hotpluggable(bus)) { error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); } return NULL; + } else if (*errp) { + return NULL; } } From 259a10dbcb4f36a3489fb52f9f7a654761589448 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:39 +0200 Subject: [PATCH 0522/1334] virtio-net: Store failover primary opts pointer locally Instead of accessing the global QemuOptsList, which really belong to the command line parser and shouldn't be accessed from devices, store a pointer to the QemuOpts in a new VirtIONet field. This is not the final state, but just an intermediate step to get rid of QemuOpts in devices. It will later be replaced with an options QDict. Before this patch, two "primary" devices could be hidden for the same standby device, but only one of them would actually be enabled and the other one would be kept hidden forever, so this doesn't make sense. After this patch, configuring a second primary device is an error. Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-13-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/net/virtio-net.c | 26 ++++++++++++++++++-------- include/hw/virtio/virtio-net.h | 1 + 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index a17d5739fc..ed9a9012e9 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -858,27 +858,24 @@ static DeviceState *failover_find_primary_device(VirtIONet *n) static void failover_add_primary(VirtIONet *n, Error **errp) { Error *err = NULL; - QemuOpts *opts; - char *id; DeviceState *dev = failover_find_primary_device(n); if (dev) { return; } - id = failover_find_primary_device_id(n); - if (!id) { + if (!n->primary_opts) { error_setg(errp, "Primary device not found"); error_append_hint(errp, "Virtio-net failover will not work. Make " "sure primary device has parameter" " failover_pair_id=%s\n", n->netclient_name); return; } - opts = qemu_opts_find(qemu_find_opts("device"), id); - g_assert(opts); /* cannot be NULL because id was found using opts list */ - dev = qdev_device_add(opts, &err); + + dev = qdev_device_add(n->primary_opts, &err); if (err) { - qemu_opts_del(opts); + qemu_opts_del(n->primary_opts); + n->primary_opts = NULL; } else { object_unref(OBJECT(dev)); } @@ -3317,6 +3314,19 @@ static bool failover_hide_primary_device(DeviceListener *listener, return false; } + if (n->primary_opts) { + error_setg(errp, "Cannot attach more than one primary device to '%s'", + n->netclient_name); + return false; + } + + /* + * Having a weak reference here should be okay because a device can't be + * deleted while it's hidden. This will be replaced soon with a QDict that + * has a clearer ownership model. + */ + n->primary_opts = device_opts; + /* failover_primary_hidden is set during feature negotiation */ return qatomic_read(&n->failover_primary_hidden); } diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 824a69c23f..d118c95f69 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -209,6 +209,7 @@ struct VirtIONet { bool failover_primary_hidden; bool failover; DeviceListener primary_listener; + QemuOpts *primary_opts; Notifier migration_state; VirtioNetRssData rss_data; struct NetRxPkt *rx_pkt; From 12b2fad7dcc8d08b6a59d1b14caa392ea614c6d9 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:40 +0200 Subject: [PATCH 0523/1334] virtio-net: Avoid QemuOpts in failover_find_primary_device() Don't go through the global QemuOptsList, it is state of the legacy command line parser and we will create devices that are not contained in it. It is also just the command line configuration and not necessarily the current runtime state. Instead, look at the qdev device tree which has the current state of all existing devices. Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-14-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/net/virtio-net.c | 52 +++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index ed9a9012e9..f503f28c00 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -796,48 +796,34 @@ static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n) typedef struct { VirtIONet *n; - char *id; -} FailoverId; + DeviceState *dev; +} FailoverDevice; /** - * Set the id of the failover primary device + * Set the failover primary device * * @opaque: FailoverId to setup * @opts: opts for device we are handling * @errp: returns an error if this function fails */ -static int failover_set_primary(void *opaque, QemuOpts *opts, Error **errp) +static int failover_set_primary(DeviceState *dev, void *opaque) { - FailoverId *fid = opaque; - const char *standby_id = qemu_opt_get(opts, "failover_pair_id"); + FailoverDevice *fdev = opaque; + PCIDevice *pci_dev = (PCIDevice *) + object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE); - if (g_strcmp0(standby_id, fid->n->netclient_name) == 0) { - fid->id = g_strdup(opts->id); + if (!pci_dev) { + return 0; + } + + if (!g_strcmp0(pci_dev->failover_pair_id, fdev->n->netclient_name)) { + fdev->dev = dev; return 1; } return 0; } -/** - * Find the primary device id for this failover virtio-net - * - * @n: VirtIONet device - * @errp: returns an error if this function fails - */ -static char *failover_find_primary_device_id(VirtIONet *n) -{ - Error *err = NULL; - FailoverId fid; - - fid.n = n; - if (!qemu_opts_foreach(qemu_find_opts("device"), - failover_set_primary, &fid, &err)) { - return NULL; - } - return fid.id; -} - /** * Find the primary device for this failover virtio-net * @@ -846,13 +832,13 @@ static char *failover_find_primary_device_id(VirtIONet *n) */ static DeviceState *failover_find_primary_device(VirtIONet *n) { - char *id = failover_find_primary_device_id(n); + FailoverDevice fdev = { + .n = n, + }; - if (!id) { - return NULL; - } - - return qdev_find_recursive(sysbus_get_default(), id); + qbus_walk_children(sysbus_get_default(), failover_set_primary, NULL, + NULL, NULL, &fdev); + return fdev.dev; } static void failover_add_primary(VirtIONet *n, Error **errp) From f3558b1b763683bb877f7dd5b282469cdadc65c3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:41 +0200 Subject: [PATCH 0524/1334] qdev: Base object creation on QDict rather than QemuOpts QDicts are both what QMP natively uses and what the keyval parser produces. Going through QemuOpts isn't useful for either one, so switch the main device creation function to QDicts. By sharing more code with the -object/object-add code path, we can even reduce the code size a bit. This commit doesn't remove the detour through QemuOpts from any code path yet, but it allows the following commits to do so. Signed-off-by: Kevin Wolf Message-Id: <20211008133442.141332-15-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- hw/core/qdev.c | 7 ++-- hw/net/virtio-net.c | 23 +++++++----- hw/vfio/pci.c | 4 +- include/hw/qdev-core.h | 12 +++--- include/hw/virtio/virtio-net.h | 3 +- include/monitor/qdev.h | 2 + softmmu/qdev-monitor.c | 69 +++++++++++++++------------------- 7 files changed, 61 insertions(+), 59 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index c3a021c444..7f06403752 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -28,6 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-events-qdev.h" +#include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/visitor.h" #include "qemu/error-report.h" @@ -211,14 +212,14 @@ void device_listener_unregister(DeviceListener *listener) QTAILQ_REMOVE(&device_listeners, listener, link); } -bool qdev_should_hide_device(QemuOpts *opts, Error **errp) +bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp) { ERRP_GUARD(); DeviceListener *listener; QTAILQ_FOREACH(listener, &device_listeners, link) { if (listener->hide_device) { - if (listener->hide_device(listener, opts, errp)) { + if (listener->hide_device(listener, opts, from_json, errp)) { return true; } else if (*errp) { return false; @@ -958,7 +959,7 @@ static void device_finalize(Object *obj) dev->canonical_path = NULL; } - qemu_opts_del(dev->opts); + qobject_unref(dev->opts); g_free(dev->id); } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index f503f28c00..09e173a558 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -858,9 +858,11 @@ static void failover_add_primary(VirtIONet *n, Error **errp) return; } - dev = qdev_device_add(n->primary_opts, &err); + dev = qdev_device_add_from_qdict(n->primary_opts, + n->primary_opts_from_json, + &err); if (err) { - qemu_opts_del(n->primary_opts); + qobject_unref(n->primary_opts); n->primary_opts = NULL; } else { object_unref(OBJECT(dev)); @@ -3287,7 +3289,9 @@ static void virtio_net_migration_state_notifier(Notifier *notifier, void *data) } static bool failover_hide_primary_device(DeviceListener *listener, - QemuOpts *device_opts, Error **errp) + const QDict *device_opts, + bool from_json, + Error **errp) { VirtIONet *n = container_of(listener, VirtIONet, primary_listener); const char *standby_id; @@ -3295,7 +3299,7 @@ static bool failover_hide_primary_device(DeviceListener *listener, if (!device_opts) { return false; } - standby_id = qemu_opt_get(device_opts, "failover_pair_id"); + standby_id = qdict_get_try_str(device_opts, "failover_pair_id"); if (g_strcmp0(standby_id, n->netclient_name) != 0) { return false; } @@ -3306,12 +3310,8 @@ static bool failover_hide_primary_device(DeviceListener *listener, return false; } - /* - * Having a weak reference here should be okay because a device can't be - * deleted while it's hidden. This will be replaced soon with a QDict that - * has a clearer ownership model. - */ - n->primary_opts = device_opts; + n->primary_opts = qdict_clone_shallow(device_opts); + n->primary_opts_from_json = from_json; /* failover_primary_hidden is set during feature negotiation */ return qatomic_read(&n->failover_primary_hidden); @@ -3502,8 +3502,11 @@ static void virtio_net_device_unrealize(DeviceState *dev) g_free(n->vlans); if (n->failover) { + qobject_unref(n->primary_opts); device_listener_unregister(&n->primary_listener); remove_migration_state_change_notifier(&n->migration_state); + } else { + assert(n->primary_opts == NULL); } max_queues = n->multiqueue ? n->max_queues : 1; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4feaa1cb68..5cdf1d4298 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -29,10 +29,10 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" +#include "qapi/qmp/qdict.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" -#include "qemu/option.h" #include "qemu/range.h" #include "qemu/units.h" #include "sysemu/kvm.h" @@ -941,7 +941,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) } if (vfio_opt_rom_in_denylist(vdev)) { - if (dev->opts && qemu_opt_get(dev->opts, "rombar")) { + if (dev->opts && qdict_haskey(dev->opts, "rombar")) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 74d8b614a7..1bad07002d 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -180,7 +180,7 @@ struct DeviceState { char *canonical_path; bool realized; bool pending_deleted_event; - QemuOpts *opts; + QDict *opts; int hotplugged; bool allow_unplug_during_migration; BusState *parent_bus; @@ -205,8 +205,8 @@ struct DeviceListener { * On errors, it returns false and errp is set. Device creation * should fail in this case. */ - bool (*hide_device)(DeviceListener *listener, QemuOpts *device_opts, - Error **errp); + bool (*hide_device)(DeviceListener *listener, const QDict *device_opts, + bool from_json, Error **errp); QTAILQ_ENTRY(DeviceListener) link; }; @@ -835,13 +835,15 @@ void device_listener_unregister(DeviceListener *listener); /** * @qdev_should_hide_device: - * @opts: QemuOpts as passed on cmdline. + * @opts: options QDict + * @from_json: true if @opts entries are typed, false for all strings + * @errp: pointer to error object * * Check if a device should be added. * When a device is added via qdev_device_add() this will be called, * and return if the device should be added now or not. */ -bool qdev_should_hide_device(QemuOpts *opts, Error **errp); +bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp); typedef enum MachineInitPhase { /* current_machine is NULL. */ diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index d118c95f69..74a10ebe85 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -209,7 +209,8 @@ struct VirtIONet { bool failover_primary_hidden; bool failover; DeviceListener primary_listener; - QemuOpts *primary_opts; + QDict *primary_opts; + bool primary_opts_from_json; Notifier migration_state; VirtioNetRssData rss_data; struct NetRxPkt *rx_pkt; diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h index 74e6c55a2b..1d57bf6577 100644 --- a/include/monitor/qdev.h +++ b/include/monitor/qdev.h @@ -9,6 +9,8 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp); int qdev_device_help(QemuOpts *opts); DeviceState *qdev_device_add(QemuOpts *opts, Error **errp); +DeviceState *qdev_device_add_from_qdict(const QDict *opts, + bool from_json, Error **errp); /** * qdev_set_id: parent the device and set its id if provided. diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index ea737db028..89c473cb22 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -196,34 +196,6 @@ static void qdev_print_devinfos(bool show_no_user) g_slist_free(list); } -static int set_property(void *opaque, const char *name, const char *value, - Error **errp) -{ - Object *obj = opaque; - QString *val; - Visitor *v; - int ret; - - if (strcmp(name, "driver") == 0) - return 0; - if (strcmp(name, "bus") == 0) - return 0; - - val = qstring_from_str(value); - v = qobject_input_visitor_new_keyval(QOBJECT(val)); - - if (!object_property_set(obj, name, v, errp)) { - ret = -1; - goto out; - } - - ret = 0; -out: - visit_free(v); - qobject_unref(val); - return ret; -} - static const char *find_typename_by_alias(const char *alias) { int i; @@ -624,15 +596,17 @@ const char *qdev_set_id(DeviceState *dev, char *id, Error **errp) return prop->name; } -DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) +DeviceState *qdev_device_add_from_qdict(const QDict *opts, + bool from_json, Error **errp) { ERRP_GUARD(); DeviceClass *dc; const char *driver, *path; + char *id; DeviceState *dev = NULL; BusState *bus = NULL; - driver = qemu_opt_get(opts, "driver"); + driver = qdict_get_try_str(opts, "driver"); if (!driver) { error_setg(errp, QERR_MISSING_PARAMETER, "driver"); return NULL; @@ -645,7 +619,7 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) } /* find bus */ - path = qemu_opt_get(opts, "bus"); + path = qdict_get_try_str(opts, "bus"); if (path != NULL) { bus = qbus_find(path, errp); if (!bus) { @@ -665,12 +639,12 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) } } - if (qemu_opt_get(opts, "failover_pair_id")) { - if (!opts->id) { + if (qdict_haskey(opts, "failover_pair_id")) { + if (!qdict_haskey(opts, "id")) { error_setg(errp, "Device with failover_pair_id don't have id"); return NULL; } - if (qdev_should_hide_device(opts, errp)) { + if (qdev_should_hide_device(opts, from_json, errp)) { if (bus && !qbus_is_hotpluggable(bus)) { error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); } @@ -711,18 +685,24 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) * set dev's parent and register its id. * If it fails it means the id is already taken. */ - if (!qdev_set_id(dev, g_strdup(qemu_opts_id(opts)), errp)) { + id = g_strdup(qdict_get_try_str(opts, "id")); + if (!qdev_set_id(dev, id, errp)) { goto err_del_dev; } /* set properties */ - if (qemu_opt_foreach(opts, set_property, dev, errp)) { + dev->opts = qdict_clone_shallow(opts); + qdict_del(dev->opts, "driver"); + qdict_del(dev->opts, "bus"); + qdict_del(dev->opts, "id"); + + object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json, + errp); + if (*errp) { goto err_del_dev; } - dev->opts = opts; if (!qdev_realize(DEVICE(dev), bus, errp)) { - dev->opts = NULL; goto err_del_dev; } return dev; @@ -735,6 +715,19 @@ err_del_dev: return NULL; } +/* Takes ownership of @opts on success */ +DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) +{ + QDict *qdict = qemu_opts_to_qdict(opts, NULL); + DeviceState *ret; + + ret = qdev_device_add_from_qdict(qdict, false, errp); + if (ret) { + qemu_opts_del(opts); + } + qobject_unref(qdict); + return ret; +} #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__) static void qbus_print(Monitor *mon, BusState *bus, int indent); From 5dacda5167560b3af8eadbce5814f60ba44b467e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Oct 2021 15:34:42 +0200 Subject: [PATCH 0525/1334] vl: Enable JSON syntax for -device Like we already do for -object, introduce support for JSON syntax in -device, which can be kept stable in the long term and guarantees that a single code path with identical behaviour is used for both QMP and the command line. Compared to the QemuOpts based code, the parser contains less surprises and has support for non-scalar options (lists and structs). Switching management tools to JSON means that we can more easily change the "human" CLI syntax from QemuOpts to the keyval parser later. In the QAPI schema, a feature flag is added to the device-add command to allow management tools to detect support for this. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Message-Id: <20211008133442.141332-16-kwolf@redhat.com> Reviewed-by: Michael S. Tsirkin Tested-by: Peter Krempa Signed-off-by: Kevin Wolf --- qapi/qdev.json | 15 ++++++++---- softmmu/vl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/qapi/qdev.json b/qapi/qdev.json index d75e68908b..69656b14df 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -32,17 +32,23 @@ ## # @device_add: # +# Add a device. +# # @driver: the name of the new device's driver # # @bus: the device's parent bus (device tree path) # # @id: the device's ID, must be unique # -# Additional arguments depend on the type. -# -# Add a device. +# Features: +# @json-cli: If present, the "-device" command line option supports JSON +# syntax with a structure identical to the arguments of this +# command. # # Notes: +# +# Additional arguments depend on the type. +# # 1. For detailed information about this command, please refer to the # 'docs/qdev-device-use.txt' file. # @@ -67,7 +73,8 @@ ## { 'command': 'device_add', 'data': {'driver': 'str', '*bus': 'str', '*id': 'str'}, - 'gen': false } # so we can get the additional arguments + 'gen': false, # so we can get the additional arguments + 'features': ['json-cli'] } ## # @device_del: diff --git a/softmmu/vl.c b/softmmu/vl.c index 55ab70eb97..af0c4cbd99 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -144,6 +144,12 @@ typedef struct ObjectOption { QTAILQ_ENTRY(ObjectOption) next; } ObjectOption; +typedef struct DeviceOption { + QDict *opts; + Location loc; + QTAILQ_ENTRY(DeviceOption) next; +} DeviceOption; + static const char *cpu_option; static const char *mem_path; static const char *incoming; @@ -151,6 +157,7 @@ static const char *loadvm; static const char *accelerators; static QDict *machine_opts_dict; static QTAILQ_HEAD(, ObjectOption) object_opts = QTAILQ_HEAD_INITIALIZER(object_opts); +static QTAILQ_HEAD(, DeviceOption) device_opts = QTAILQ_HEAD_INITIALIZER(device_opts); static ram_addr_t maxram_size; static uint64_t ram_slots; static int display_remote; @@ -494,21 +501,39 @@ const char *qemu_get_vm_name(void) return qemu_name; } -static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp) +static void default_driver_disable(const char *driver) { - const char *driver = qemu_opt_get(opts, "driver"); int i; - if (!driver) - return 0; + if (!driver) { + return; + } + for (i = 0; i < ARRAY_SIZE(default_list); i++) { if (strcmp(default_list[i].driver, driver) != 0) continue; *(default_list[i].flag) = 0; } +} + +static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp) +{ + const char *driver = qemu_opt_get(opts, "driver"); + + default_driver_disable(driver); return 0; } +static void default_driver_check_json(void) +{ + DeviceOption *opt; + + QTAILQ_FOREACH(opt, &device_opts, next) { + const char *driver = qdict_get_try_str(opt->opts, "driver"); + default_driver_disable(driver); + } +} + static int parse_name(void *opaque, QemuOpts *opts, Error **errp) { const char *proc_name; @@ -1311,6 +1336,7 @@ static void qemu_disable_default_devices(void) { MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); + default_driver_check_json(); qemu_opts_foreach(qemu_find_opts("device"), default_driver_check, NULL, NULL); qemu_opts_foreach(qemu_find_opts("global"), @@ -2637,6 +2663,8 @@ static void qemu_init_board(void) static void qemu_create_cli_devices(void) { + DeviceOption *opt; + soundhw_init(); qemu_opts_foreach(qemu_find_opts("fw_cfg"), @@ -2652,6 +2680,18 @@ static void qemu_create_cli_devices(void) rom_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE); qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, &error_fatal); + QTAILQ_FOREACH(opt, &device_opts, next) { + loc_push_restore(&opt->loc); + /* + * TODO Eventually we should call qmp_device_add() here to make sure it + * behaves the same, but QMP still has to accept incorrectly typed + * options until libvirt is fixed and we want to be strict on the CLI + * from the start, so call qdev_device_add_from_qdict() directly for + * now. + */ + qdev_device_add_from_qdict(opt->opts, true, &error_fatal); + loc_pop(&opt->loc); + } rom_reset_order_override(); } @@ -3352,9 +3392,18 @@ void qemu_init(int argc, char **argv, char **envp) add_device_config(DEV_USB, optarg); break; case QEMU_OPTION_device: - if (!qemu_opts_parse_noisily(qemu_find_opts("device"), - optarg, true)) { - exit(1); + if (optarg[0] == '{') { + QObject *obj = qobject_from_json(optarg, &error_fatal); + DeviceOption *opt = g_new0(DeviceOption, 1); + opt->opts = qobject_to(QDict, obj); + loc_save(&opt->loc); + assert(opt->opts != NULL); + QTAILQ_INSERT_TAIL(&device_opts, opt, next); + } else { + if (!qemu_opts_parse_noisily(qemu_find_opts("device"), + optarg, true)) { + exit(1); + } } break; case QEMU_OPTION_smp: From e7e588d432d31ecebc26358e47201dd108db964c Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Mon, 11 Oct 2021 17:50:31 +0200 Subject: [PATCH 0526/1334] qcow2: Silence clang -m32 compiler warning With -m32, size_t is generally only a uint32_t. That makes clang complain that in the assertion assert(qiov->size <= INT64_MAX); the range of the type of qiov->size (size_t) is too small for any of its values to ever exceed INT64_MAX. Cast qiov->size to uint64_t to silence clang. Fixes: f7ef38dd1310d7d9db76d0aa16899cbc5744f36d ("block: use int64_t instead of uint64_t in driver read handlers") Signed-off-by: Hanna Reitz Message-Id: <20211011155031.149158-1-hreitz@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/qcow2-cluster.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 5727f92dcb..21884a1ab9 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -513,7 +513,8 @@ static int coroutine_fn do_perform_cow_read(BlockDriverState *bs, */ assert(src_cluster_offset <= INT64_MAX); assert(src_cluster_offset + offset_in_cluster <= INT64_MAX); - assert(qiov->size <= INT64_MAX); + /* Cast qiov->size to uint64_t to silence a compiler warning on -m32 */ + assert((uint64_t)qiov->size <= INT64_MAX); bdrv_check_qiov_request(src_cluster_offset + offset_in_cluster, qiov->size, qiov, 0, &error_abort); /* From 7242db6389261cb936bd2240351b137843d49807 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:07 +0200 Subject: [PATCH 0527/1334] block-backend: blk_check_byte_request(): int64_t bytes Rename size and make it int64_t to correspond to modern block layer, which always uses int64_t for offset and bytes (not in blk layer yet, which is a task for following commits). All callers pass int or unsigned int. So, for bytes in [0, INT_MAX] nothing is changed, for negative bytes we now fail on "bytes < 0" check instead of "bytes > INT_MAX" check. Note, that blk_check_byte_request() still doesn't allow requests exceeding INT_MAX. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-2-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/block-backend.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index ba2b5ebb10..2c62210687 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1161,11 +1161,11 @@ void blk_set_disable_request_queuing(BlockBackend *blk, bool disable) } static int blk_check_byte_request(BlockBackend *blk, int64_t offset, - size_t size) + int64_t bytes) { int64_t len; - if (size > INT_MAX) { + if (bytes < 0 || bytes > INT_MAX) { return -EIO; } @@ -1183,7 +1183,7 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, return len; } - if (offset > len || len - offset < size) { + if (offset > len || len - offset < bytes) { return -EIO; } } From 9547907705c94fa0415427ce12be418f8b68b56d Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:08 +0200 Subject: [PATCH 0528/1334] block-backend: make blk_co_preadv() 64bit For both updated functions, the type of bytes becomes wider, so all callers should be OK with it. blk_co_preadv() only passes its arguments to blk_do_preadv(). blk_do_preadv() passes bytes to: - trace_blk_co_preadv, which is updated too - blk_check_byte_request, throttle_group_co_io_limits_intercept, bdrv_co_preadv, which are already int64_t. Note that requests exceeding INT_MAX are still restricted by blk_check_byte_request(). Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-3-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweaks] Signed-off-by: Eric Blake --- block/block-backend.c | 4 ++-- block/trace-events | 2 +- include/sysemu/block-backend.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 2c62210687..3199f84e96 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1205,7 +1205,7 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) /* To be called between exactly one pair of blk_inc/dec_in_flight() */ static int coroutine_fn -blk_do_preadv(BlockBackend *blk, int64_t offset, unsigned int bytes, +blk_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret; @@ -1236,7 +1236,7 @@ blk_do_preadv(BlockBackend *blk, int64_t offset, unsigned int bytes, } int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, - unsigned int bytes, QEMUIOVector *qiov, + int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret; diff --git a/block/trace-events b/block/trace-events index f2d0a9b62a..ff397ffff4 100644 --- a/block/trace-events +++ b/block/trace-events @@ -5,7 +5,7 @@ bdrv_open_common(void *bs, const char *filename, int flags, const char *format_n bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" # block-backend.c -blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" +blk_co_preadv(void *blk, void *bs, int64_t offset, int64_t bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %" PRId64 " flags 0x%x" blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p" blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p" diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 82bae55161..442fd705bb 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -126,7 +126,7 @@ BlockBackend *blk_by_dev(void *dev); BlockBackend *blk_by_qdev_id(const char *id, Error **errp); void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque); int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, - unsigned int bytes, QEMUIOVector *qiov, + int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, unsigned int bytes, From 34460feb63230daad12b0e0f31754f7ef5bc2be9 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:09 +0200 Subject: [PATCH 0529/1334] block-backend: convert blk_co_pwritev_part to int64_t bytes We convert blk_do_pwritev_part() and some wrappers: blk_co_pwritev_part(), blk_co_pwritev(), blk_co_pwrite_zeroes(). All functions are converted so that the parameter type becomes wider, so all callers should be OK with it. Look at blk_do_pwritev_part() body: bytes is passed to: - trace_blk_co_pwritev (we update it here) - blk_check_byte_request, throttle_group_co_io_limits_intercept, bdrv_co_pwritev_part - all already have int64_t argument. Note that requests exceeding INT_MAX are still restricted by blk_check_byte_request(). Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-4-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweaks] Signed-off-by: Eric Blake --- block/block-backend.c | 8 ++++---- block/trace-events | 2 +- include/sysemu/block-backend.h | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 3199f84e96..105f0afff9 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1250,7 +1250,7 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, /* To be called between exactly one pair of blk_inc/dec_in_flight() */ static int coroutine_fn -blk_do_pwritev_part(BlockBackend *blk, int64_t offset, unsigned int bytes, +blk_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) { @@ -1286,7 +1286,7 @@ blk_do_pwritev_part(BlockBackend *blk, int64_t offset, unsigned int bytes, } int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, - unsigned int bytes, + int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) { @@ -1300,7 +1300,7 @@ int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, } int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, - unsigned int bytes, QEMUIOVector *qiov, + int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags); @@ -2214,7 +2214,7 @@ void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, } int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { return blk_co_pwritev(blk, offset, bytes, NULL, flags | BDRV_REQ_ZERO_WRITE); diff --git a/block/trace-events b/block/trace-events index ff397ffff4..ab56edacb4 100644 --- a/block/trace-events +++ b/block/trace-events @@ -6,7 +6,7 @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" # block-backend.c blk_co_preadv(void *blk, void *bs, int64_t offset, int64_t bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %" PRId64 " flags 0x%x" -blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" +blk_co_pwritev(void *blk, void *bs, int64_t offset, int64_t bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %" PRId64 " flags 0x%x" blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p" blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p" diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 442fd705bb..91457a081e 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -129,11 +129,11 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, - unsigned int bytes, + int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, - unsigned int bytes, QEMUIOVector *qiov, + int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); static inline int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, @@ -243,7 +243,7 @@ int blk_get_open_flags_from_root_state(BlockBackend *blk); void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, BlockCompletionFunc *cb, void *opaque); int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int bytes, BdrvRequestFlags flags); + int64_t bytes, BdrvRequestFlags flags); int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, int bytes); int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, From 2800637a33b0ff68c40c94a1d3d13f92feaadbc6 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:10 +0200 Subject: [PATCH 0530/1334] block-backend: convert blk_co_pdiscard to int64_t bytes We updated blk_do_pdiscard() and its wrapper blk_co_pdiscard(). Both functions are updated so that the parameter type becomes wider, so all callers should be OK with it. Look at blk_do_pdiscard(): bytes is passed only to blk_check_byte_request() and bdrv_co_pdiscard(), which already have int64_t bytes parameter, so we are OK. Note that requests exceeding INT_MAX are still restricted by blk_check_byte_request(). Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-5-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweaks] Signed-off-by: Eric Blake --- block/block-backend.c | 5 +++-- include/sysemu/block-backend.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 105f0afff9..1e21498b2c 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1626,7 +1626,7 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, /* To be called between exactly one pair of blk_inc/dec_in_flight() */ static int coroutine_fn -blk_do_pdiscard(BlockBackend *blk, int64_t offset, int bytes) +blk_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; @@ -1657,7 +1657,8 @@ BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, cb, opaque); } -int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) +int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, + int64_t bytes) { int ret; diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 91457a081e..3efa025639 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -181,7 +181,8 @@ void blk_aio_cancel_async(BlockAIOCB *acb); int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); -int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes); +int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, + int64_t bytes); int blk_co_flush(BlockBackend *blk); int blk_flush(BlockBackend *blk); int blk_commit_all(void); From 70e8775ed9e46cb4fab8013627df4dd835a1c24c Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:11 +0200 Subject: [PATCH 0531/1334] block-backend: rename _do_ helper functions to _co_do_ This is a preparation to the following commit, to use automatic coroutine wrapper generation. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-6-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/block-backend.c | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 1e21498b2c..cecac67485 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1205,8 +1205,8 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) /* To be called between exactly one pair of blk_inc/dec_in_flight() */ static int coroutine_fn -blk_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags) +blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret; BlockDriverState *bs; @@ -1242,7 +1242,7 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, int ret; blk_inc_in_flight(blk); - ret = blk_do_preadv(blk, offset, bytes, qiov, flags); + ret = blk_co_do_preadv(blk, offset, bytes, qiov, flags); blk_dec_in_flight(blk); return ret; @@ -1250,9 +1250,9 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, /* To be called between exactly one pair of blk_inc/dec_in_flight() */ static int coroutine_fn -blk_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags) +blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { int ret; BlockDriverState *bs; @@ -1293,7 +1293,7 @@ int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, int ret; blk_inc_in_flight(blk); - ret = blk_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); + ret = blk_co_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); blk_dec_in_flight(blk); return ret; @@ -1319,8 +1319,8 @@ static void blk_read_entry(void *opaque) BlkRwCo *rwco = opaque; QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_do_preadv(rwco->blk, rwco->offset, qiov->size, - qiov, rwco->flags); + rwco->ret = blk_co_do_preadv(rwco->blk, rwco->offset, qiov->size, + qiov, rwco->flags); aio_wait_kick(); } @@ -1329,8 +1329,8 @@ static void blk_write_entry(void *opaque) BlkRwCo *rwco = opaque; QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_do_pwritev_part(rwco->blk, rwco->offset, qiov->size, - qiov, 0, rwco->flags); + rwco->ret = blk_co_do_pwritev_part(rwco->blk, rwco->offset, qiov->size, + qiov, 0, rwco->flags); aio_wait_kick(); } @@ -1483,8 +1483,8 @@ static void blk_aio_read_entry(void *opaque) QEMUIOVector *qiov = rwco->iobuf; assert(qiov->size == acb->bytes); - rwco->ret = blk_do_preadv(rwco->blk, rwco->offset, acb->bytes, - qiov, rwco->flags); + rwco->ret = blk_co_do_preadv(rwco->blk, rwco->offset, acb->bytes, + qiov, rwco->flags); blk_aio_complete(acb); } @@ -1495,8 +1495,8 @@ static void blk_aio_write_entry(void *opaque) QEMUIOVector *qiov = rwco->iobuf; assert(!qiov || qiov->size == acb->bytes); - rwco->ret = blk_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes, - qiov, 0, rwco->flags); + rwco->ret = blk_co_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes, + qiov, 0, rwco->flags); blk_aio_complete(acb); } @@ -1583,7 +1583,7 @@ void blk_aio_cancel_async(BlockAIOCB *acb) /* To be called between exactly one pair of blk_inc/dec_in_flight() */ static int coroutine_fn -blk_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { blk_wait_while_drained(blk); @@ -1599,7 +1599,7 @@ static void blk_ioctl_entry(void *opaque) BlkRwCo *rwco = opaque; QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_do_ioctl(rwco->blk, rwco->offset, qiov->iov[0].iov_base); + rwco->ret = blk_co_do_ioctl(rwco->blk, rwco->offset, qiov->iov[0].iov_base); aio_wait_kick(); } @@ -1613,7 +1613,7 @@ static void blk_aio_ioctl_entry(void *opaque) BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; - rwco->ret = blk_do_ioctl(rwco->blk, rwco->offset, rwco->iobuf); + rwco->ret = blk_co_do_ioctl(rwco->blk, rwco->offset, rwco->iobuf); blk_aio_complete(acb); } @@ -1626,7 +1626,7 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, /* To be called between exactly one pair of blk_inc/dec_in_flight() */ static int coroutine_fn -blk_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) +blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; @@ -1645,7 +1645,7 @@ static void blk_aio_pdiscard_entry(void *opaque) BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; - rwco->ret = blk_do_pdiscard(rwco->blk, rwco->offset, acb->bytes); + rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, acb->bytes); blk_aio_complete(acb); } @@ -1663,7 +1663,7 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int ret; blk_inc_in_flight(blk); - ret = blk_do_pdiscard(blk, offset, bytes); + ret = blk_co_do_pdiscard(blk, offset, bytes); blk_dec_in_flight(blk); return ret; @@ -1674,7 +1674,7 @@ static void blk_pdiscard_entry(void *opaque) BlkRwCo *rwco = opaque; QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_do_pdiscard(rwco->blk, rwco->offset, qiov->size); + rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, qiov->size); aio_wait_kick(); } @@ -1684,7 +1684,7 @@ int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -static int coroutine_fn blk_do_flush(BlockBackend *blk) +static int coroutine_fn blk_co_do_flush(BlockBackend *blk) { blk_wait_while_drained(blk); @@ -1700,7 +1700,7 @@ static void blk_aio_flush_entry(void *opaque) BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; - rwco->ret = blk_do_flush(rwco->blk); + rwco->ret = blk_co_do_flush(rwco->blk); blk_aio_complete(acb); } @@ -1715,7 +1715,7 @@ int coroutine_fn blk_co_flush(BlockBackend *blk) int ret; blk_inc_in_flight(blk); - ret = blk_do_flush(blk); + ret = blk_co_do_flush(blk); blk_dec_in_flight(blk); return ret; @@ -1724,7 +1724,7 @@ int coroutine_fn blk_co_flush(BlockBackend *blk) static void blk_flush_entry(void *opaque) { BlkRwCo *rwco = opaque; - rwco->ret = blk_do_flush(rwco->blk); + rwco->ret = blk_co_do_flush(rwco->blk); aio_wait_kick(); } From 7d55a3bbadc0b00ff6c2a4f37116db887caec7ab Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:12 +0200 Subject: [PATCH 0532/1334] block-coroutine-wrapper.py: support BlockBackend first argument Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-7-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/coroutines.h | 3 +++ scripts/block-coroutine-wrapper.py | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/block/coroutines.h b/block/coroutines.h index 514d169d23..35a6c49857 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -27,6 +27,9 @@ #include "block/block_int.h" +/* For blk_bs() in generated block/block-gen.c */ +#include "sysemu/block-backend.h" + int coroutine_fn bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp); diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py index 85dbeb9ecf..08be813407 100644 --- a/scripts/block-coroutine-wrapper.py +++ b/scripts/block-coroutine-wrapper.py @@ -100,12 +100,20 @@ def snake_to_camel(func_name: str) -> str: def gen_wrapper(func: FuncDecl) -> str: assert not '_co_' in func.name assert func.return_type == 'int' - assert func.args[0].type in ['BlockDriverState *', 'BdrvChild *'] + assert func.args[0].type in ['BlockDriverState *', 'BdrvChild *', + 'BlockBackend *'] subsystem, subname = func.name.split('_', 1) name = f'{subsystem}_co_{subname}' - bs = 'bs' if func.args[0].type == 'BlockDriverState *' else 'child->bs' + + t = func.args[0].type + if t == 'BlockDriverState *': + bs = 'bs' + elif t == 'BdrvChild *': + bs = 'child->bs' + else: + bs = 'blk_bs(blk)' struct_name = snake_to_camel(name) return f"""\ From 16d36e299669145f3021f2912aefbc32d7f0593c Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:13 +0200 Subject: [PATCH 0533/1334] block-backend: drop blk_prw, use block-coroutine-wrapper Let's drop hand-made coroutine wrappers and use coroutine wrapper generation like in block/io.c. Now, blk_foo() functions are written in same way as blk_co_foo() ones, but wrap blk_do_foo() instead of blk_co_do_foo(). Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-8-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: spelling fix] Signed-off-by: Eric Blake --- block/block-backend.c | 155 ++++++++++++++++-------------------------- block/coroutines.h | 30 ++++++++ 2 files changed, 90 insertions(+), 95 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index cecac67485..2e6ccce7ef 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -14,6 +14,7 @@ #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" +#include "block/coroutines.h" #include "block/throttle-groups.h" #include "hw/qdev-core.h" #include "sysemu/blockdev.h" @@ -1204,7 +1205,7 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -static int coroutine_fn +int coroutine_fn blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -1249,7 +1250,7 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -static int coroutine_fn +int coroutine_fn blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) @@ -1306,6 +1307,20 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags); } +static int coroutine_fn blk_pwritev_part(BlockBackend *blk, int64_t offset, + int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) +{ + int ret; + + blk_inc_in_flight(blk); + ret = blk_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); + blk_dec_in_flight(blk); + + return ret; +} + typedef struct BlkRwCo { BlockBackend *blk; int64_t offset; @@ -1314,58 +1329,11 @@ typedef struct BlkRwCo { BdrvRequestFlags flags; } BlkRwCo; -static void blk_read_entry(void *opaque) -{ - BlkRwCo *rwco = opaque; - QEMUIOVector *qiov = rwco->iobuf; - - rwco->ret = blk_co_do_preadv(rwco->blk, rwco->offset, qiov->size, - qiov, rwco->flags); - aio_wait_kick(); -} - -static void blk_write_entry(void *opaque) -{ - BlkRwCo *rwco = opaque; - QEMUIOVector *qiov = rwco->iobuf; - - rwco->ret = blk_co_do_pwritev_part(rwco->blk, rwco->offset, qiov->size, - qiov, 0, rwco->flags); - aio_wait_kick(); -} - -static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, - int64_t bytes, CoroutineEntry co_entry, - BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - BlkRwCo rwco = { - .blk = blk, - .offset = offset, - .iobuf = &qiov, - .flags = flags, - .ret = NOT_DONE, - }; - - blk_inc_in_flight(blk); - if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - co_entry(&rwco); - } else { - Coroutine *co = qemu_coroutine_create(co_entry, &rwco); - bdrv_coroutine_enter(blk_bs(blk), co); - BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE); - } - blk_dec_in_flight(blk); - - return rwco.ret; -} - int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, int bytes, BdrvRequestFlags flags) { - return blk_prw(blk, offset, NULL, bytes, blk_write_entry, - flags | BDRV_REQ_ZERO_WRITE); + return blk_pwritev_part(blk, offset, bytes, NULL, 0, + flags | BDRV_REQ_ZERO_WRITE); } int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) @@ -1510,22 +1478,25 @@ BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) { - int ret = blk_prw(blk, offset, buf, count, blk_read_entry, 0); - if (ret < 0) { - return ret; - } - return count; + int ret; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, count); + + blk_inc_in_flight(blk); + ret = blk_do_preadv(blk, offset, count, &qiov, 0); + blk_dec_in_flight(blk); + + return ret < 0 ? ret : count; } int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count, BdrvRequestFlags flags) { - int ret = blk_prw(blk, offset, (void *) buf, count, blk_write_entry, - flags); - if (ret < 0) { - return ret; - } - return count; + int ret; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, count); + + ret = blk_pwritev_part(blk, offset, count, &qiov, 0, flags); + + return ret < 0 ? ret : count; } int64_t blk_getlength(BlockBackend *blk) @@ -1582,7 +1553,7 @@ void blk_aio_cancel_async(BlockAIOCB *acb) } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -static int coroutine_fn +int coroutine_fn blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { blk_wait_while_drained(blk); @@ -1594,18 +1565,15 @@ blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) return bdrv_co_ioctl(blk_bs(blk), req, buf); } -static void blk_ioctl_entry(void *opaque) -{ - BlkRwCo *rwco = opaque; - QEMUIOVector *qiov = rwco->iobuf; - - rwco->ret = blk_co_do_ioctl(rwco->blk, rwco->offset, qiov->iov[0].iov_base); - aio_wait_kick(); -} - int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { - return blk_prw(blk, req, buf, 0, blk_ioctl_entry, 0); + int ret; + + blk_inc_in_flight(blk); + ret = blk_do_ioctl(blk, req, buf); + blk_dec_in_flight(blk); + + return ret; } static void blk_aio_ioctl_entry(void *opaque) @@ -1625,7 +1593,7 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -static int coroutine_fn +int coroutine_fn blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; @@ -1669,22 +1637,19 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, return ret; } -static void blk_pdiscard_entry(void *opaque) -{ - BlkRwCo *rwco = opaque; - QEMUIOVector *qiov = rwco->iobuf; - - rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, qiov->size); - aio_wait_kick(); -} - int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) { - return blk_prw(blk, offset, NULL, bytes, blk_pdiscard_entry, 0); + int ret; + + blk_inc_in_flight(blk); + ret = blk_do_pdiscard(blk, offset, bytes); + blk_dec_in_flight(blk); + + return ret; } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -static int coroutine_fn blk_co_do_flush(BlockBackend *blk) +int coroutine_fn blk_co_do_flush(BlockBackend *blk) { blk_wait_while_drained(blk); @@ -1721,16 +1686,15 @@ int coroutine_fn blk_co_flush(BlockBackend *blk) return ret; } -static void blk_flush_entry(void *opaque) -{ - BlkRwCo *rwco = opaque; - rwco->ret = blk_co_do_flush(rwco->blk); - aio_wait_kick(); -} - int blk_flush(BlockBackend *blk) { - return blk_prw(blk, 0, NULL, 0, blk_flush_entry, 0); + int ret; + + blk_inc_in_flight(blk); + ret = blk_do_flush(blk); + blk_dec_in_flight(blk); + + return ret; } void blk_drain(BlockBackend *blk) @@ -2224,8 +2188,9 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, int count) { - return blk_prw(blk, offset, (void *) buf, count, blk_write_entry, - BDRV_REQ_WRITE_COMPRESSED); + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, count); + return blk_pwritev_part(blk, offset, count, &qiov, 0, + BDRV_REQ_WRITE_COMPRESSED); } int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, diff --git a/block/coroutines.h b/block/coroutines.h index 35a6c49857..c8c14a29c8 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -75,4 +75,34 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, Error **errp); +int generated_co_wrapper +blk_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags); +int coroutine_fn +blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags); + + +int generated_co_wrapper +blk_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); +int coroutine_fn +blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); + +int generated_co_wrapper +blk_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); +int coroutine_fn +blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); + +int generated_co_wrapper +blk_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); +int coroutine_fn +blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); + +int generated_co_wrapper blk_do_flush(BlockBackend *blk); +int coroutine_fn blk_co_do_flush(BlockBackend *blk); + #endif /* BLOCK_COROUTINES_INT_H */ From 06f0325c5b62f80bab1c9eb50edc814158d6005e Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:14 +0200 Subject: [PATCH 0534/1334] block-backend: convert blk_foo wrappers to use int64_t bytes parameter Convert blk_pdiscard, blk_pwrite_compressed, blk_pwrite_zeroes. These are just wrappers for functions with int64_t argument, so allow passing int64_t as well. Parameter type becomes wider so all callers should be OK with it. Note that requests exceeding INT_MAX are still restricted by blk_check_byte_request(). Note also that we don't (and are not going to) convert blk_pwrite and blk_pread: these functions return number of bytes on success, so to update them, we should change return type to int64_t as well, which will lead to investigating and updating all callers which is too much. So, blk_pread and blk_pwrite remain unchanged. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-9-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweaks] Signed-off-by: Eric Blake --- block/block-backend.c | 10 +++++----- include/sysemu/block-backend.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 2e6ccce7ef..ee20ae5f0f 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1330,7 +1330,7 @@ typedef struct BlkRwCo { } BlkRwCo; int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { return blk_pwritev_part(blk, offset, bytes, NULL, 0, flags | BDRV_REQ_ZERO_WRITE); @@ -1637,7 +1637,7 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, return ret; } -int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) +int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; @@ -2186,10 +2186,10 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, } int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, - int count) + int64_t bytes) { - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, count); - return blk_pwritev_part(blk, offset, count, &qiov, 0, + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + return blk_pwritev_part(blk, offset, bytes, &qiov, 0, BDRV_REQ_WRITE_COMPRESSED); } diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 3efa025639..3294d1b07a 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -155,7 +155,7 @@ static inline int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, } int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int bytes, BdrvRequestFlags flags); + int64_t bytes, BdrvRequestFlags flags); BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, int bytes, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); @@ -246,10 +246,10 @@ void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequestFlags flags); int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, - int bytes); + int64_t bytes); int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); -int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes); +int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int64_t pos, int size); int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); From e192179bb2c080f0130a7ff427c2572909fc31ed Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:15 +0200 Subject: [PATCH 0535/1334] block-backend: convert blk_co_copy_range to int64_t bytes Function is updated so that parameter type becomes wider, so all callers should be OK with it. Look at blk_co_copy_range() itself: bytes is passed only to blk_check_byte_request() and bdrv_co_copy_range(), which already have int64_t bytes parameter, so we are OK. Note that requests exceeding INT_MAX are still restricted by blk_check_byte_request(). Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-10-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweaks] Signed-off-by: Eric Blake --- block/block-backend.c | 2 +- include/sysemu/block-backend.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index ee20ae5f0f..0746be8984 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2418,7 +2418,7 @@ void blk_unregister_buf(BlockBackend *blk, void *host) int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, BlockBackend *blk_out, int64_t off_out, - int bytes, BdrvRequestFlags read_flags, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { int r; diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 3294d1b07a..9ccf9f1a80 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -270,7 +270,7 @@ void blk_unregister_buf(BlockBackend *blk, void *host); int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, BlockBackend *blk_out, int64_t off_out, - int bytes, BdrvRequestFlags read_flags, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); const BdrvChild *blk_root(BlockBackend *blk); From a93d81c84afa717b0a1a6947524d8d1fbfd6bbf5 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:16 +0200 Subject: [PATCH 0536/1334] block-backend: convert blk_aio_ functions to int64_t bytes paramter 1. Convert bytes in BlkAioEmAIOCB: aio->bytes is only passed to already int64_t interfaces, and set in blk_aio_prwv, which is updated here. 2. For all updated functions the parameter type becomes wider so callers are safe. 3. In blk_aio_prwv we only store bytes to BlkAioEmAIOCB, which is updated here. 4. Other updated functions are wrappers on blk_aio_prwv. Note that blk_aio_preadv and blk_aio_pwritev become safer: before this commit, it's theoretically possible to pass qiov with size exceeding INT_MAX, which than converted to int argument of blk_aio_prwv. Now it's converted to int64_t which is a lot better. Still add assertions. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-11-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: tweak assertion and grammar] Signed-off-by: Eric Blake --- block/block-backend.c | 13 ++++++++----- include/sysemu/block-backend.h | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 0746be8984..c889d0f97c 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1380,7 +1380,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, typedef struct BlkAioEmAIOCB { BlockAIOCB common; BlkRwCo rwco; - int bytes; + int64_t bytes; bool has_returned; } BlkAioEmAIOCB; @@ -1412,7 +1412,8 @@ static void blk_aio_complete_bh(void *opaque) blk_aio_complete(acb); } -static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes, +static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, + int64_t bytes, void *iobuf, CoroutineEntry co_entry, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) @@ -1469,10 +1470,10 @@ static void blk_aio_write_entry(void *opaque) } BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int count, BdrvRequestFlags flags, + int64_t bytes, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { - return blk_aio_prwv(blk, offset, count, NULL, blk_aio_write_entry, + return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_write_entry, flags | BDRV_REQ_ZERO_WRITE, cb, opaque); } @@ -1530,6 +1531,7 @@ BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { + assert((uint64_t)qiov->size <= INT64_MAX); return blk_aio_prwv(blk, offset, qiov->size, qiov, blk_aio_read_entry, flags, cb, opaque); } @@ -1538,6 +1540,7 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { + assert(qiov->size <= INT64_MAX); return blk_aio_prwv(blk, offset, qiov->size, qiov, blk_aio_write_entry, flags, cb, opaque); } @@ -1618,7 +1621,7 @@ static void blk_aio_pdiscard_entry(void *opaque) } BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, - int64_t offset, int bytes, + int64_t offset, int64_t bytes, BlockCompletionFunc *cb, void *opaque) { return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0, diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 9ccf9f1a80..b5409a6b45 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -157,7 +157,7 @@ static inline int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequestFlags flags); BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int bytes, BdrvRequestFlags flags, + int64_t bytes, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes); @@ -174,7 +174,7 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *blk_aio_flush(BlockBackend *blk, BlockCompletionFunc *cb, void *opaque); -BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int bytes, +BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, BlockCompletionFunc *cb, void *opaque); void blk_aio_cancel(BlockAIOCB *acb); void blk_aio_cancel_async(BlockAIOCB *acb); From 14149710f98fcf7af13eeb6cf6040b33c01b9792 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:17 +0200 Subject: [PATCH 0537/1334] block-backend: blk_pread, blk_pwrite: rename count parameter to bytes To be consistent with declarations in include/sysemu/block-backend.h. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-12-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/block-backend.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index c889d0f97c..59746eda45 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1477,27 +1477,27 @@ BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, flags | BDRV_REQ_ZERO_WRITE, cb, opaque); } -int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) +int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes) { int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, count); + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); blk_inc_in_flight(blk); - ret = blk_do_preadv(blk, offset, count, &qiov, 0); + ret = blk_do_preadv(blk, offset, bytes, &qiov, 0); blk_dec_in_flight(blk); - return ret < 0 ? ret : count; + return ret < 0 ? ret : bytes; } -int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count, +int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, BdrvRequestFlags flags) { int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, count); + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - ret = blk_pwritev_part(blk, offset, count, &qiov, 0, flags); + ret = blk_pwritev_part(blk, offset, bytes, &qiov, 0, flags); - return ret < 0 ? ret : count; + return ret < 0 ? ret : bytes; } int64_t blk_getlength(BlockBackend *blk) From aa78b825162163d2c51903490d67c10a689eac6b Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 6 Oct 2021 15:17:18 +0200 Subject: [PATCH 0538/1334] block-backend: drop INT_MAX restriction from blk_check_byte_request() blk_check_bytes_request is called from blk_co_do_preadv, blk_co_do_pwritev_part, blk_co_do_pdiscard and blk_co_copy_range before (maybe) calling throttle_group_co_io_limits_intercept() (which has int64_t argument) and then calling corresponding bdrv_co_ function. bdrv_co_ functions are OK with int64_t bytes as well. So dropping the check for INT_MAX we just get same restrictions as in bdrv_ layer: discard and write-zeroes goes through bdrv_check_qiov_request() and are allowed to be 64bit. Other requests go through bdrv_check_request32() and still restricted by INT_MAX boundary. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211006131718.214235-13-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/block-backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/block-backend.c b/block/block-backend.c index 59746eda45..39cd99df2b 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1166,7 +1166,7 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, { int64_t len; - if (bytes < 0 || bytes > INT_MAX) { + if (bytes < 0) { return -EIO; } From 57768ec166493260494b76a2fa9fea980ca30e18 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 7 Oct 2021 19:52:42 +0200 Subject: [PATCH 0539/1334] block-backend: fix blk_co_flush prototype to mention coroutine_fn We already have this marker for the blk_co_flush function declaration in block/block-backend.c. Add it in the header too. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211007175243.642516-1-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: wording tweak] Signed-off-by: Eric Blake --- include/sysemu/block-backend.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index b5409a6b45..3fbc74e17c 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -183,7 +183,7 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); -int blk_co_flush(BlockBackend *blk); +int coroutine_fn blk_co_flush(BlockBackend *blk); int blk_flush(BlockBackend *blk); int blk_commit_all(void); void blk_inc_in_flight(BlockBackend *blk); From bec4042baefc1bfeae05b161aa17d2f57d526b60 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 7 Oct 2021 19:52:43 +0200 Subject: [PATCH 0540/1334] block-backend: update blk_co_pwrite() and blk_co_pread() wrappers Make bytes argument int64_t to be consistent with modern block-layer. Callers should be OK with it as type becomes wider. What is inside functions? - Conversion from int64_t to size_t. Still, we can't have a buffer larger than SIZE_MAX, therefore bytes should not be larger than SIZE_MAX as well. Add an assertion. - Passing to blk_co_pwritev() / blk_co_preadv() which already has int64_t bytes argument. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211007175243.642516-2-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: spelling fix] Signed-off-by: Eric Blake --- include/sysemu/block-backend.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 3fbc74e17c..e5e1524f06 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -137,20 +137,24 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, BdrvRequestFlags flags); static inline int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, - unsigned int bytes, void *buf, + int64_t bytes, void *buf, BdrvRequestFlags flags) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + assert(bytes <= SIZE_MAX); + return blk_co_preadv(blk, offset, bytes, &qiov, flags); } static inline int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, - unsigned int bytes, void *buf, + int64_t bytes, void *buf, BdrvRequestFlags flags) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + assert(bytes <= SIZE_MAX); + return blk_co_pwritev(blk, offset, bytes, &qiov, flags); } From c9460d75c59fc6b6f110156e4c1035e9ae9bd611 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 18 Jul 2021 15:12:12 -1000 Subject: [PATCH 0541/1334] accel/tcg: Handle gdb singlestep in cpu_tb_exec Currently the change in cpu_tb_exec is masked by the debug exception being raised by the translators. But this allows us to remove that code. Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 5fd1ed3422..c9764c1325 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -383,6 +383,17 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit) cc->set_pc(cpu, last_tb->pc); } } + + /* + * If gdb single-step, and we haven't raised another exception, + * raise a debug exception. Single-step with another exception + * is handled in cpu_handle_exception. + */ + if (unlikely(cpu->singlestep_enabled) && cpu->exception_index == -1) { + cpu->exception_index = EXCP_DEBUG; + cpu_loop_exit(cpu); + } + return last_tb; } From 1760e4abf11223535b92e25a7fbd928b29b57233 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 18 Jul 2021 15:25:13 -1000 Subject: [PATCH 0542/1334] target/alpha: Drop checks for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/alpha/translate.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 0eee3a1bcc..a4c3f43e72 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -3005,17 +3005,10 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); /* FALLTHRU */ case DISAS_PC_UPDATED: - if (!ctx->base.singlestep_enabled) { - tcg_gen_lookup_and_goto_ptr(); - break; - } - /* FALLTHRU */ + tcg_gen_lookup_and_goto_ptr(); + break; case DISAS_PC_UPDATED_NOCHAIN: - if (ctx->base.singlestep_enabled) { - gen_excp_1(EXCP_DEBUG, 0); - } else { - tcg_gen_exit_tb(NULL, 0); - } + tcg_gen_exit_tb(NULL, 0); break; default: g_assert_not_reached(); From a893daa936161b8f45b2567875922343e388de9c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 18 Jul 2021 20:56:46 -1000 Subject: [PATCH 0543/1334] target/avr: Drop checks for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Tested-by: Michael Rolnik Reviewed-by: Michael Rolnik Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/avr/translate.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/target/avr/translate.c b/target/avr/translate.c index 438e7b13c1..af8a3e0f9c 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -1087,11 +1087,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) tcg_gen_exit_tb(tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); - if (ctx->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); } ctx->base.is_jmp = DISAS_NORETURN; } @@ -3009,17 +3005,10 @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) tcg_gen_movi_tl(cpu_pc, ctx->npc); /* fall through */ case DISAS_LOOKUP: - if (!ctx->base.singlestep_enabled) { - tcg_gen_lookup_and_goto_ptr(); - break; - } - /* fall through */ + tcg_gen_lookup_and_goto_ptr(); + break; case DISAS_EXIT: - if (ctx->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else { - tcg_gen_exit_tb(NULL, 0); - } + tcg_gen_exit_tb(NULL, 0); break; default: g_assert_not_reached(); From e3774881b5b651da7b44e5e98c5c8cea610e35bc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 18 Jul 2021 20:58:53 -1000 Subject: [PATCH 0544/1334] target/cris: Drop checks for singlestep_enabled GDB single-stepping is now handled generically. Signed-off-by: Richard Henderson --- target/cris/translate.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/target/cris/translate.c b/target/cris/translate.c index a84b753349..59325b388a 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -3249,22 +3249,6 @@ static void cris_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } - if (unlikely(dc->base.singlestep_enabled)) { - switch (is_jmp) { - case DISAS_TOO_MANY: - case DISAS_UPDATE_NEXT: - tcg_gen_movi_tl(env_pc, npc); - /* fall through */ - case DISAS_JUMP: - case DISAS_UPDATE: - t_gen_raise_exception(EXCP_DEBUG); - return; - default: - break; - } - g_assert_not_reached(); - } - switch (is_jmp) { case DISAS_TOO_MANY: gen_goto_tb(dc, 0, npc); From db07bd026ee6f5ea4fe32c503645813252bae2c6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 18 Jul 2021 21:02:03 -1000 Subject: [PATCH 0545/1334] target/hexagon: Drop checks for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hexagon/translate.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 4f05ce3388..159931e8ee 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -69,11 +69,7 @@ static void gen_end_tb(DisasContext *ctx) { gen_exec_counters(ctx); tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC); - if (ctx->base.singlestep_enabled) { - gen_exception_raw(EXCP_DEBUG); - } else { - tcg_gen_exit_tb(NULL, 0); - } + tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; } @@ -614,11 +610,7 @@ static void hexagon_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_TOO_MANY: gen_exec_counters(ctx); tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->base.pc_next); - if (ctx->base.singlestep_enabled) { - gen_exception_raw(EXCP_DEBUG); - } else { - tcg_gen_exit_tb(NULL, 0); - } + tcg_gen_exit_tb(NULL, 0); break; case DISAS_NORETURN: break; From 364caea70f965a4fc81395863a3bae6bc16ce240 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 18 Jul 2021 21:12:59 -1000 Subject: [PATCH 0546/1334] target/arm: Drop checks for singlestep_enabled GDB single-stepping is now handled generically. Signed-off-by: Richard Henderson --- target/arm/translate-a64.c | 10 ++-------- target/arm/translate.c | 36 ++++++------------------------------ 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 717afd481c..cec672f229 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -404,8 +404,6 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) gen_a64_set_pc_im(dest); if (s->ss_active) { gen_step_complete_exception(s); - } else if (s->base.singlestep_enabled) { - gen_exception_internal(EXCP_DEBUG); } else { tcg_gen_lookup_and_goto_ptr(); s->base.is_jmp = DISAS_NORETURN; @@ -14879,7 +14877,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - if (unlikely(dc->base.singlestep_enabled || dc->ss_active)) { + if (unlikely(dc->ss_active)) { /* Note that this means single stepping WFI doesn't halt the CPU. * For conditional branch insns this is harmless unreachable code as * gen_goto_tb() has already handled emitting the debug exception @@ -14891,11 +14889,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* fall through */ case DISAS_EXIT: case DISAS_JUMP: - if (dc->base.singlestep_enabled) { - gen_exception_internal(EXCP_DEBUG); - } else { - gen_step_complete_exception(dc); - } + gen_step_complete_exception(dc); break; case DISAS_NORETURN: break; diff --git a/target/arm/translate.c b/target/arm/translate.c index f7086c66a5..d6af5b1b03 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -341,7 +341,7 @@ static void gen_exception_internal(int excp) tcg_temp_free_i32(tcg_excp); } -static void gen_step_complete_exception(DisasContext *s) +static void gen_singlestep_exception(DisasContext *s) { /* We just completed step of an insn. Move from Active-not-pending * to Active-pending, and then also take the swstep exception. @@ -357,30 +357,6 @@ static void gen_step_complete_exception(DisasContext *s) s->base.is_jmp = DISAS_NORETURN; } -static void gen_singlestep_exception(DisasContext *s) -{ - /* Generate the right kind of exception for singlestep, which is - * either the architectural singlestep or EXCP_DEBUG for QEMU's - * gdb singlestepping. - */ - if (s->ss_active) { - gen_step_complete_exception(s); - } else { - gen_exception_internal(EXCP_DEBUG); - } -} - -static inline bool is_singlestepping(DisasContext *s) -{ - /* Return true if we are singlestepping either because of - * architectural singlestep or QEMU gdbstub singlestep. This does - * not include the command line '-singlestep' mode which is rather - * misnamed as it only means "one instruction per TB" and doesn't - * affect the code we generate. - */ - return s->base.singlestep_enabled || s->ss_active; -} - void clear_eci_state(DisasContext *s) { /* @@ -837,7 +813,7 @@ static inline void gen_bx_excret_final_code(DisasContext *s) /* Is the new PC value in the magic range indicating exception return? */ tcg_gen_brcondi_i32(TCG_COND_GEU, cpu_R[15], min_magic, excret_label); /* No: end the TB as we would for a DISAS_JMP */ - if (is_singlestepping(s)) { + if (s->ss_active) { gen_singlestep_exception(s); } else { tcg_gen_exit_tb(NULL, 0); @@ -2606,7 +2582,7 @@ static void gen_goto_tb(DisasContext *s, int n, target_ulong dest) /* Jump, specifying which TB number to use if we gen_goto_tb() */ static inline void gen_jmp_tb(DisasContext *s, uint32_t dest, int tbno) { - if (unlikely(is_singlestepping(s))) { + if (unlikely(s->ss_active)) { /* An indirect jump so that we still trigger the debug exception. */ gen_set_pc_im(s, dest); s->base.is_jmp = DISAS_JUMP; @@ -9459,7 +9435,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->page_start = dc->base.pc_first & TARGET_PAGE_MASK; /* If architectural single step active, limit to 1. */ - if (is_singlestepping(dc)) { + if (dc->ss_active) { dc->base.max_insns = 1; } @@ -9794,7 +9770,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) * insn codepath itself. */ gen_bx_excret_final_code(dc); - } else if (unlikely(is_singlestepping(dc))) { + } else if (unlikely(dc->ss_active)) { /* Unconditional and "condition passed" instruction codepath. */ switch (dc->base.is_jmp) { case DISAS_SWI: @@ -9889,7 +9865,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* "Condition failed" instruction codepath for the branch/trap insn */ gen_set_label(dc->condlabel); gen_set_condexec(dc); - if (unlikely(is_singlestepping(dc))) { + if (unlikely(dc->ss_active)) { gen_set_pc_im(dc, dc->base.pc_next); gen_singlestep_exception(dc); } else { From 8532a14e413b3395c3ee2f13285462259858f433 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 18 Jul 2021 21:19:26 -1000 Subject: [PATCH 0547/1334] target/hppa: Drop checks for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c3698cf067..3b9744deb4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -814,11 +814,7 @@ static void gen_goto_tb(DisasContext *ctx, int which, } else { copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b); copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var); - if (ctx->base.singlestep_enabled) { - gen_excp_1(EXCP_DEBUG); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); } } @@ -2346,11 +2342,7 @@ static bool do_rfi(DisasContext *ctx, bool rfi_r) gen_helper_rfi(cpu_env); } /* Exit the TB to recognize new interrupts. */ - if (ctx->base.singlestep_enabled) { - gen_excp_1(EXCP_DEBUG); - } else { - tcg_gen_exit_tb(NULL, 0); - } + tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; return nullify_end(ctx); @@ -4274,10 +4266,9 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) nullify_save(ctx); /* FALLTHRU */ case DISAS_IAQ_N_UPDATED: - if (ctx->base.singlestep_enabled) { - gen_excp_1(EXCP_DEBUG); - } else if (is_jmp != DISAS_IAQ_N_STALE_EXIT) { + if (is_jmp != DISAS_IAQ_N_STALE_EXIT) { tcg_gen_lookup_and_goto_ptr(); + break; } /* FALLTHRU */ case DISAS_EXIT: From 9ef6c6ec08b8ecd89a797fa6819b0c1d121e24d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 15:59:08 -1000 Subject: [PATCH 0548/1334] target/i386: Check CF_NO_GOTO_TB for dc->jmp_opt We were using singlestep_enabled as a proxy for whether translator_use_goto_tb would always return false. Signed-off-by: Richard Henderson --- target/i386/tcg/translate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index a46be75b00..c8d919bc3f 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -8556,6 +8556,7 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); CPUX86State *env = cpu->env_ptr; uint32_t flags = dc->base.tb->flags; + uint32_t cflags = tb_cflags(dc->base.tb); int cpl = (flags >> HF_CPL_SHIFT) & 3; int iopl = (flags >> IOPL_SHIFT) & 3; @@ -8593,14 +8594,14 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX]; dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX]; dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; - dc->jmp_opt = !(dc->base.singlestep_enabled || + dc->jmp_opt = !((cflags & CF_NO_GOTO_TB) || (flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); /* * If jmp_opt, we want to handle each string instruction individually. * For icount also disable repz optimization so that each iteration * is accounted separately. */ - dc->repz_opt = !dc->jmp_opt && !(tb_cflags(dc->base.tb) & CF_USE_ICOUNT); + dc->repz_opt = !dc->jmp_opt && !(cflags & CF_USE_ICOUNT); dc->T0 = tcg_temp_new(); dc->T1 = tcg_temp_new(); From 4bc4c3135be4b258ae8143b1f8982fe76cba460a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 16:04:29 -1000 Subject: [PATCH 0549/1334] target/i386: Drop check for singlestep_enabled GDB single-stepping is now handled generically. Signed-off-by: Richard Henderson --- target/i386/helper.h | 1 - target/i386/tcg/misc_helper.c | 8 -------- target/i386/tcg/translate.c | 4 +--- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/target/i386/helper.h b/target/i386/helper.h index 574ff75615..ac3b4d1ee3 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -56,7 +56,6 @@ DEF_HELPER_2(syscall, void, env, int) DEF_HELPER_2(sysret, void, env, int) #endif DEF_HELPER_FLAGS_2(pause, TCG_CALL_NO_WG, noreturn, env, int) -DEF_HELPER_FLAGS_1(debug, TCG_CALL_NO_WG, noreturn, env) DEF_HELPER_1(reset_rf, void, env) DEF_HELPER_FLAGS_3(raise_interrupt, TCG_CALL_NO_WG, noreturn, env, int, int) DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, int) diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c index baffa5d7ba..5769db5ace 100644 --- a/target/i386/tcg/misc_helper.c +++ b/target/i386/tcg/misc_helper.c @@ -110,14 +110,6 @@ void QEMU_NORETURN helper_pause(CPUX86State *env, int next_eip_addend) do_pause(env); } -void QEMU_NORETURN helper_debug(CPUX86State *env) -{ - CPUState *cs = env_cpu(env); - - cs->exception_index = EXCP_DEBUG; - cpu_loop_exit(cs); -} - uint64_t helper_rdpkru(CPUX86State *env, uint32_t ecx) { if ((env->cr[4] & CR4_PKE_MASK) == 0) { diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index c8d919bc3f..e9e1451540 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2660,9 +2660,7 @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) if (s->base.tb->flags & HF_RF_MASK) { gen_helper_reset_rf(cpu_env); } - if (s->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else if (recheck_tf) { + if (recheck_tf) { gen_helper_rechecking_single_step(cpu_env); tcg_gen_exit_tb(NULL, 0); } else if (s->flags & HF_TF_MASK) { From 661da0f63f973b84755423ae9e919582ff330a0c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 18 Jul 2021 21:27:53 -1000 Subject: [PATCH 0550/1334] target/m68k: Drop checks for singlestep_enabled GDB single-stepping is now handled generically. Acked-by: Laurent Vivier Signed-off-by: Richard Henderson --- target/m68k/translate.c | 44 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 50a55f949c..af43c8eab8 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -194,18 +194,6 @@ static void do_writebacks(DisasContext *s) } } -static bool is_singlestepping(DisasContext *s) -{ - /* - * Return true if we are singlestepping either because of - * architectural singlestep or QEMU gdbstub singlestep. This does - * not include the command line '-singlestep' mode which is rather - * misnamed as it only means "one instruction per TB" and doesn't - * affect the code we generate. - */ - return s->base.singlestep_enabled || s->ss_active; -} - /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_EXIT DISAS_TARGET_1 /* cpu state was modified dynamically */ @@ -320,20 +308,6 @@ static void gen_exception(DisasContext *s, uint32_t dest, int nr) s->base.is_jmp = DISAS_NORETURN; } -static void gen_singlestep_exception(DisasContext *s) -{ - /* - * Generate the right kind of exception for singlestep, which is - * either the architectural singlestep or EXCP_DEBUG for QEMU's - * gdb singlestepping. - */ - if (s->ss_active) { - gen_raise_exception(EXCP_TRACE); - } else { - gen_raise_exception(EXCP_DEBUG); - } -} - static inline void gen_addr_fault(DisasContext *s) { gen_exception(s, s->base.pc_next, EXCP_ADDRESS); @@ -1522,10 +1496,10 @@ static void gen_exit_tb(DisasContext *s) /* Generate a jump to an immediate address. */ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest) { - if (unlikely(is_singlestepping(s))) { + if (unlikely(s->ss_active)) { update_cc_op(s); tcg_gen_movi_i32(QREG_PC, dest); - gen_singlestep_exception(s); + gen_raise_exception(EXCP_TRACE); } else if (translator_use_goto_tb(&s->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(QREG_PC, dest); @@ -6193,7 +6167,7 @@ static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->ss_active = (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS); /* If architectural single step active, limit to 1 */ - if (is_singlestepping(dc)) { + if (dc->ss_active) { dc->base.max_insns = 1; } } @@ -6252,17 +6226,17 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) break; case DISAS_TOO_MANY: update_cc_op(dc); - if (is_singlestepping(dc)) { + if (dc->ss_active) { tcg_gen_movi_i32(QREG_PC, dc->pc); - gen_singlestep_exception(dc); + gen_raise_exception(EXCP_TRACE); } else { gen_jmp_tb(dc, 0, dc->pc); } break; case DISAS_JUMP: /* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */ - if (is_singlestepping(dc)) { - gen_singlestep_exception(dc); + if (dc->ss_active) { + gen_raise_exception(EXCP_TRACE); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -6272,8 +6246,8 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) * We updated CC_OP and PC in gen_exit_tb, but also modified * other state that may require returning to the main loop. */ - if (is_singlestepping(dc)) { - gen_singlestep_exception(dc); + if (dc->ss_active) { + gen_raise_exception(EXCP_TRACE); } else { tcg_gen_exit_tb(NULL, 0); } From fbafb3a4d2fc90efdba05ec0d1f2ad7c4f68e9a5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 06:16:42 -1000 Subject: [PATCH 0551/1334] target/microblaze: Check CF_NO_GOTO_TB for DISAS_JUMP We were using singlestep_enabled as a proxy for whether translator_use_goto_tb would always return false. Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index a14ffed784..7e465b629a 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1779,7 +1779,7 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) break; case DISAS_JUMP: - if (dc->jmp_dest != -1 && !cs->singlestep_enabled) { + if (dc->jmp_dest != -1 && !(tb_cflags(dc->base.tb) & CF_NO_GOTO_TB)) { /* Direct jump. */ tcg_gen_discard_i32(cpu_btarget); @@ -1804,7 +1804,7 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) return; } - /* Indirect jump (or direct jump w/ singlestep) */ + /* Indirect jump (or direct jump w/ goto_tb disabled) */ tcg_gen_mov_i32(cpu_pc, cpu_btarget); tcg_gen_discard_i32(cpu_btarget); From 66345580255f4a9f382cb9e8321a2590025eb1b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 06:17:20 -1000 Subject: [PATCH 0552/1334] target/microblaze: Drop checks for singlestep_enabled GDB single-stepping is now handled generically. Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 7e465b629a..437bbed6d6 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -126,12 +126,7 @@ static void gen_raise_hw_excp(DisasContext *dc, uint32_t esr_ec) static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { - if (dc->base.singlestep_enabled) { - TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); - tcg_gen_movi_i32(cpu_pc, dest); - gen_helper_raise_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); - } else if (translator_use_goto_tb(&dc->base, dest)) { + if (translator_use_goto_tb(&dc->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_exit_tb(dc->base.tb, n); @@ -1807,12 +1802,7 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) /* Indirect jump (or direct jump w/ goto_tb disabled) */ tcg_gen_mov_i32(cpu_pc, cpu_btarget); tcg_gen_discard_i32(cpu_btarget); - - if (unlikely(cs->singlestep_enabled)) { - gen_raise_exception(dc, EXCP_DEBUG); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); return; default: From ef00cd4a22701923583862c48448b43bbcdaca0f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:01:49 -1000 Subject: [PATCH 0553/1334] target/mips: Fix single stepping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per an ancient comment in mips_tr_translate_insn about the expectations of gdb, when restarting the insn in a delay slot we also re-execute the branch. Which means that we are expected to execute two insns in this case. This has been broken since 8b86d6d2580, where we forced max_insns to 1 while single-stepping. This resulted in an exit from the translator loop after the branch but before the delay slot is translated. Increase the max_insns to 2 for this case. In addition, bypass the end-of-page check, for when the branch itself ends the page. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/tcg/translate.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 148afec9dc..f239f9ffc0 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -16016,6 +16016,16 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->default_tcg_memop_mask = (ctx->insn_flags & (ISA_MIPS_R6 | INSN_LOONGSON3A)) ? MO_UNALN : MO_ALIGN; + /* + * Execute a branch and its delay slot as a single instruction. + * This is what GDB expects and is consistent with what the + * hardware does (e.g. if a delay slot instruction faults, the + * reported PC is the PC of the branch). + */ + if (ctx->base.singlestep_enabled && (ctx->hflags & MIPS_HFLAG_BMASK)) { + ctx->base.max_insns = 2; + } + LOG_DISAS("\ntb %p idx %d hflags %04x\n", ctx->base.tb, ctx->mem_idx, ctx->hflags); } @@ -16085,17 +16095,14 @@ static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) if (ctx->base.is_jmp != DISAS_NEXT) { return; } + /* - * Execute a branch and its delay slot as a single instruction. - * This is what GDB expects and is consistent with what the - * hardware does (e.g. if a delay slot instruction faults, the - * reported PC is the PC of the branch). + * End the TB on (most) page crossings. + * See mips_tr_init_disas_context about single-stepping a branch + * together with its delay slot. */ - if (ctx->base.singlestep_enabled && - (ctx->hflags & MIPS_HFLAG_BMASK) == 0) { - ctx->base.is_jmp = DISAS_TOO_MANY; - } - if (ctx->base.pc_next - ctx->page_start >= TARGET_PAGE_SIZE) { + if (ctx->base.pc_next - ctx->page_start >= TARGET_PAGE_SIZE + && !ctx->base.singlestep_enabled) { ctx->base.is_jmp = DISAS_TOO_MANY; } } From 1a79c413999e241bbc24487b4db08faec2493c9d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:04:32 -1000 Subject: [PATCH 0554/1334] target/mips: Drop exit checks for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/tcg/translate.c | 50 +++++++++++++------------------------ 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index f239f9ffc0..0e59b97190 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -4823,12 +4823,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) tcg_gen_exit_tb(ctx->base.tb, n); } else { gen_save_pc(dest); - if (ctx->base.singlestep_enabled) { - save_cpu_state(ctx, 0); - gen_helper_raise_exception_debug(cpu_env); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); } } @@ -11788,10 +11783,6 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) } else { tcg_gen_mov_tl(cpu_PC, btarget); } - if (ctx->base.singlestep_enabled) { - save_cpu_state(ctx, 0); - gen_helper_raise_exception_debug(cpu_env); - } tcg_gen_lookup_and_goto_ptr(); break; default: @@ -16111,28 +16102,23 @@ static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - if (ctx->base.singlestep_enabled && ctx->base.is_jmp != DISAS_NORETURN) { - save_cpu_state(ctx, ctx->base.is_jmp != DISAS_EXIT); - gen_helper_raise_exception_debug(cpu_env); - } else { - switch (ctx->base.is_jmp) { - case DISAS_STOP: - gen_save_pc(ctx->base.pc_next); - tcg_gen_lookup_and_goto_ptr(); - break; - case DISAS_NEXT: - case DISAS_TOO_MANY: - save_cpu_state(ctx, 0); - gen_goto_tb(ctx, 0, ctx->base.pc_next); - break; - case DISAS_EXIT: - tcg_gen_exit_tb(NULL, 0); - break; - case DISAS_NORETURN: - break; - default: - g_assert_not_reached(); - } + switch (ctx->base.is_jmp) { + case DISAS_STOP: + gen_save_pc(ctx->base.pc_next); + tcg_gen_lookup_and_goto_ptr(); + break; + case DISAS_NEXT: + case DISAS_TOO_MANY: + save_cpu_state(ctx, 0); + gen_goto_tb(ctx, 0, ctx->base.pc_next); + break; + case DISAS_EXIT: + tcg_gen_exit_tb(NULL, 0); + break; + case DISAS_NORETURN: + break; + default: + g_assert_not_reached(); } } From b21fce536c5e673b1f3faab6db9bd7a7aec88cc1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:07:10 -1000 Subject: [PATCH 0555/1334] target/openrisc: Drop checks for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/openrisc/translate.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 5f3d430245..ca79e609da 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1659,11 +1659,7 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* The jump destination is indirect/computed; use jmp_pc. */ tcg_gen_mov_tl(cpu_pc, jmp_pc); tcg_gen_discard_tl(jmp_pc); - if (unlikely(dc->base.singlestep_enabled)) { - gen_exception(dc, EXCP_DEBUG); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); break; } /* The jump destination is direct; use jmp_pc_imm. @@ -1680,19 +1676,11 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) break; } tcg_gen_movi_tl(cpu_pc, jmp_dest); - if (unlikely(dc->base.singlestep_enabled)) { - gen_exception(dc, EXCP_DEBUG); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); break; case DISAS_EXIT: - if (unlikely(dc->base.singlestep_enabled)) { - gen_exception(dc, EXCP_DEBUG); - } else { - tcg_gen_exit_tb(NULL, 0); - } + tcg_gen_exit_tb(NULL, 0); break; default: g_assert_not_reached(); From 9498d1032c107166ffb72939f99929a4dd3f600a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:26:48 -1000 Subject: [PATCH 0556/1334] target/ppc: Drop exit checks for singlestep_enabled GDB single-stepping is now handled generically. Reuse gen_debug_exception to handle architectural debug exceptions. Signed-off-by: Richard Henderson --- target/ppc/translate.c | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 9ca78ee156..c3c6cb9589 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -42,7 +42,6 @@ #define CPU_SINGLE_STEP 0x1 #define CPU_BRANCH_STEP 0x2 -#define GDBSTUB_SINGLE_STEP 0x4 /* Include definitions for instructions classes and implementations flags */ /* #define PPC_DEBUG_DISAS */ @@ -333,7 +332,7 @@ static uint32_t gen_prep_dbgex(DisasContext *ctx) static void gen_debug_exception(DisasContext *ctx) { - gen_helper_raise_exception(cpu_env, tcg_constant_i32(EXCP_DEBUG)); + gen_helper_raise_exception(cpu_env, tcg_constant_i32(gen_prep_dbgex(ctx))); ctx->base.is_jmp = DISAS_NORETURN; } @@ -4309,15 +4308,8 @@ static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) static void gen_lookup_and_goto_ptr(DisasContext *ctx) { - int sse = ctx->singlestep_enabled; - if (unlikely(sse)) { - if (sse & GDBSTUB_SINGLE_STEP) { - gen_debug_exception(ctx); - } else if (sse & (CPU_SINGLE_STEP | CPU_BRANCH_STEP)) { - gen_helper_raise_exception(cpu_env, tcg_constant_i32(gen_prep_dbgex(ctx))); - } else { - tcg_gen_exit_tb(NULL, 0); - } + if (unlikely(ctx->singlestep_enabled)) { + gen_debug_exception(ctx); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -8563,17 +8555,11 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->singlestep_enabled = 0; if ((hflags >> HFLAGS_SE) & 1) { ctx->singlestep_enabled |= CPU_SINGLE_STEP; + ctx->base.max_insns = 1; } if ((hflags >> HFLAGS_BE) & 1) { ctx->singlestep_enabled |= CPU_BRANCH_STEP; } - if (unlikely(ctx->base.singlestep_enabled)) { - ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP; - } - - if (ctx->singlestep_enabled & (CPU_SINGLE_STEP | GDBSTUB_SINGLE_STEP)) { - ctx->base.max_insns = 1; - } } static void ppc_tr_tb_start(DisasContextBase *db, CPUState *cs) @@ -8642,7 +8628,6 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) DisasContext *ctx = container_of(dcbase, DisasContext, base); DisasJumpType is_jmp = ctx->base.is_jmp; target_ulong nip = ctx->base.pc_next; - int sse; if (is_jmp == DISAS_NORETURN) { /* We have already exited the TB. */ @@ -8650,8 +8635,8 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } /* Honor single stepping. */ - sse = ctx->singlestep_enabled & (CPU_SINGLE_STEP | GDBSTUB_SINGLE_STEP); - if (unlikely(sse)) { + if (unlikely(ctx->singlestep_enabled & CPU_SINGLE_STEP) + && (nip <= 0x100 || nip > 0xf00)) { switch (is_jmp) { case DISAS_TOO_MANY: case DISAS_EXIT_UPDATE: @@ -8665,15 +8650,8 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) g_assert_not_reached(); } - if (sse & GDBSTUB_SINGLE_STEP) { - gen_debug_exception(ctx); - return; - } - /* else CPU_SINGLE_STEP... */ - if (nip <= 0x100 || nip > 0xf00) { - gen_helper_raise_exception(cpu_env, tcg_constant_i32(gen_prep_dbgex(ctx))); - return; - } + gen_debug_exception(ctx); + return; } switch (is_jmp) { From ea06a006525181826b1197a987d474bdaeb0acf7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:34:13 -1000 Subject: [PATCH 0557/1334] target/riscv: Remove dead code after exception We have already set DISAS_NORETURN in generate_exception, which makes the exit_tb unreachable. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- target/riscv/insn_trans/trans_privileged.c.inc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 32312be202..a7afcb15ce 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -22,8 +22,6 @@ static bool trans_ecall(DisasContext *ctx, arg_ecall *a) { /* always generates U-level ECALL, fixed in do_interrupt handler */ generate_exception(ctx, RISCV_EXCP_U_ECALL); - exit_tb(ctx); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -60,13 +58,11 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) post = opcode_at(&ctx->base, post_addr); } - if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) { + if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) { generate_exception(ctx, RISCV_EXCP_SEMIHOST); } else { generate_exception(ctx, RISCV_EXCP_BREAKPOINT); } - exit_tb(ctx); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; return true; } From 273b68b10428117ffb1917d4081cf8ab4cc940ca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:35:18 -1000 Subject: [PATCH 0558/1334] target/riscv: Remove exit_tb and lookup_and_goto_ptr GDB single-stepping is now handled generically, which means we don't need to do anything in the wrappers. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- .../riscv/insn_trans/trans_privileged.c.inc | 4 +-- target/riscv/insn_trans/trans_rvi.c.inc | 8 +++--- target/riscv/insn_trans/trans_rvv.c.inc | 2 +- target/riscv/translate.c | 27 +------------------ 4 files changed, 7 insertions(+), 34 deletions(-) diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index a7afcb15ce..75c6ef80a6 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -78,7 +78,7 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) if (has_ext(ctx, RVS)) { gen_helper_sret(cpu_pc, cpu_env, cpu_pc); - exit_tb(ctx); /* no chaining */ + tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; } else { return false; @@ -94,7 +94,7 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) #ifndef CONFIG_USER_ONLY tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); gen_helper_mret(cpu_pc, cpu_env, cpu_pc); - exit_tb(ctx); /* no chaining */ + tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; return true; #else diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 920ae0edb3..a6a57c94bb 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -71,9 +71,7 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) if (a->rd != 0) { tcg_gen_movi_tl(cpu_gpr[a->rd], ctx->pc_succ_insn); } - - /* No chaining with JALR. */ - lookup_and_goto_ptr(ctx); + tcg_gen_lookup_and_goto_ptr(); if (misaligned) { gen_set_label(misaligned); @@ -421,7 +419,7 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) * however we need to end the translation block */ tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - exit_tb(ctx); + tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -430,7 +428,7 @@ static bool do_csr_post(DisasContext *ctx) { /* We may have changed important cpu state -- exit to main loop. */ tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - exit_tb(ctx); + tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; return true; } diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index fa451938f1..081a5ca34d 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -41,7 +41,7 @@ static bool trans_vsetvl(DisasContext *ctx, arg_vsetvl *a) gen_set_gpr(ctx, a->rd, dst); tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - lookup_and_goto_ptr(ctx); + tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; return true; } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index d2442f0cf5..6d7fbca1fa 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -149,31 +149,6 @@ static void generate_exception_mtval(DisasContext *ctx, int excp) ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_exception_debug(void) -{ - gen_helper_raise_exception(cpu_env, tcg_constant_i32(EXCP_DEBUG)); -} - -/* Wrapper around tcg_gen_exit_tb that handles single stepping */ -static void exit_tb(DisasContext *ctx) -{ - if (ctx->base.singlestep_enabled) { - gen_exception_debug(); - } else { - tcg_gen_exit_tb(NULL, 0); - } -} - -/* Wrapper around tcg_gen_lookup_and_goto_ptr that handles single stepping */ -static void lookup_and_goto_ptr(DisasContext *ctx) -{ - if (ctx->base.singlestep_enabled) { - gen_exception_debug(); - } else { - tcg_gen_lookup_and_goto_ptr(); - } -} - static void gen_exception_illegal(DisasContext *ctx) { generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); @@ -192,7 +167,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_tl(cpu_pc, dest); - lookup_and_goto_ptr(ctx); + tcg_gen_lookup_and_goto_ptr(); } } From b6509e35b9e3a3fa90cdafcb8d53bf75306f427b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:41:55 -1000 Subject: [PATCH 0559/1334] target/rx: Drop checks for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/rx/helper.h | 1 - target/rx/op_helper.c | 8 -------- target/rx/translate.c | 12 ++---------- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/target/rx/helper.h b/target/rx/helper.h index f0b7ebbbf7..ebb4739474 100644 --- a/target/rx/helper.h +++ b/target/rx/helper.h @@ -2,7 +2,6 @@ DEF_HELPER_1(raise_illegal_instruction, noreturn, env) DEF_HELPER_1(raise_access_fault, noreturn, env) DEF_HELPER_1(raise_privilege_violation, noreturn, env) DEF_HELPER_1(wait, noreturn, env) -DEF_HELPER_1(debug, noreturn, env) DEF_HELPER_2(rxint, noreturn, env, i32) DEF_HELPER_1(rxbrk, noreturn, env) DEF_HELPER_FLAGS_3(fadd, TCG_CALL_NO_WG, f32, env, f32, f32) diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index 4d315b4449..11f952d340 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -451,14 +451,6 @@ void QEMU_NORETURN helper_wait(CPURXState *env) raise_exception(env, EXCP_HLT, 0); } -void QEMU_NORETURN helper_debug(CPURXState *env) -{ - CPUState *cs = env_cpu(env); - - cs->exception_index = EXCP_DEBUG; - cpu_loop_exit(cs); -} - void QEMU_NORETURN helper_rxint(CPURXState *env, uint32_t vec) { raise_exception(env, 0x100 + vec, 0); diff --git a/target/rx/translate.c b/target/rx/translate.c index a3cf720455..5db8f79a82 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -150,11 +150,7 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) tcg_gen_exit_tb(dc->base.tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); - if (dc->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); } dc->base.is_jmp = DISAS_NORETURN; } @@ -2331,11 +2327,7 @@ static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) gen_goto_tb(ctx, 0, dcbase->pc_next); break; case DISAS_JUMP: - if (ctx->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); break; case DISAS_UPDATE: tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); From 2df7a3618aacaa0f057d7b05444a21974a157263 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:52:20 -1000 Subject: [PATCH 0560/1334] target/s390x: Drop check for singlestep_enabled GDB single-stepping is now handled generically. Signed-off-by: Richard Henderson --- target/s390x/tcg/translate.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index a2d6fa5cca..dcc249a197 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -149,7 +149,6 @@ struct DisasContext { uint64_t pc_tmp; uint32_t ilen; enum cc_op cc_op; - bool do_debug; }; /* Information carried about a condition to be evaluated. */ @@ -6544,7 +6543,6 @@ static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->cc_op = CC_OP_DYNAMIC; dc->ex_value = dc->base.tb->cs_base; - dc->do_debug = dc->base.singlestep_enabled; } static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) @@ -6596,10 +6594,8 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* FALLTHRU */ case DISAS_PC_CC_UPDATED: /* Exit the TB, either by raising a debug exception or by return. */ - if (dc->do_debug) { - gen_exception(EXCP_DEBUG); - } else if ((dc->base.tb->flags & FLAG_MASK_PER) || - dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) { + if ((dc->base.tb->flags & FLAG_MASK_PER) || + dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) { tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); From 52df5adce9c028cec91cc0c10f7012b17212ea35 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:54:55 -1000 Subject: [PATCH 0561/1334] target/sh4: Drop check for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sh4/helper.h | 1 - target/sh4/op_helper.c | 5 ----- target/sh4/translate.c | 14 +++----------- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/target/sh4/helper.h b/target/sh4/helper.h index 1e768fcbc7..8d792f6b55 100644 --- a/target/sh4/helper.h +++ b/target/sh4/helper.h @@ -3,7 +3,6 @@ DEF_HELPER_1(raise_illegal_instruction, noreturn, env) DEF_HELPER_1(raise_slot_illegal_instruction, noreturn, env) DEF_HELPER_1(raise_fpu_disable, noreturn, env) DEF_HELPER_1(raise_slot_fpu_disable, noreturn, env) -DEF_HELPER_1(debug, noreturn, env) DEF_HELPER_1(sleep, noreturn, env) DEF_HELPER_2(trapa, noreturn, env, i32) DEF_HELPER_1(exclusive, noreturn, env) diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index c0cbb95382..c996dce7df 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -81,11 +81,6 @@ void helper_raise_slot_fpu_disable(CPUSH4State *env) raise_exception(env, 0x820, 0); } -void helper_debug(CPUSH4State *env) -{ - raise_exception(env, EXCP_DEBUG, 0); -} - void helper_sleep(CPUSH4State *env) { CPUState *cs = env_cpu(env); diff --git a/target/sh4/translate.c b/target/sh4/translate.c index d363050272..ce5d674a52 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -240,9 +240,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); - if (ctx->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else if (use_exit_tb(ctx)) { + if (use_exit_tb(ctx)) { tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); @@ -258,9 +256,7 @@ static void gen_jump(DisasContext * ctx) delayed jump as immediate jump are conditinal jumps */ tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc); tcg_gen_discard_i32(cpu_delayed_pc); - if (ctx->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else if (use_exit_tb(ctx)) { + if (use_exit_tb(ctx)) { tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); @@ -2324,11 +2320,7 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) switch (ctx->base.is_jmp) { case DISAS_STOP: gen_save_cpu_state(ctx, true); - if (ctx->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else { - tcg_gen_exit_tb(NULL, 0); - } + tcg_gen_exit_tb(NULL, 0); break; case DISAS_NEXT: case DISAS_TOO_MANY: From 1b55c52d607043210ed4579009191f99a5cf8f71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 14:59:06 -1000 Subject: [PATCH 0562/1334] target/tricore: Drop check for singlestep_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB single-stepping is now handled generically. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/tricore/helper.h | 1 - target/tricore/op_helper.c | 7 ------- target/tricore/translate.c | 14 +------------- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/target/tricore/helper.h b/target/tricore/helper.h index 78176aa17a..b64780c37d 100644 --- a/target/tricore/helper.h +++ b/target/tricore/helper.h @@ -153,4 +153,3 @@ DEF_HELPER_2(psw_write, void, env, i32) DEF_HELPER_1(psw_read, i32, env) /* Exceptions */ DEF_HELPER_3(raise_exception_sync, noreturn, env, i32, i32) -DEF_HELPER_2(qemu_excp, noreturn, env, i32) diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index 32c2bc1699..9476d10d00 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -107,13 +107,6 @@ static void raise_exception_sync_helper(CPUTriCoreState *env, uint32_t class, raise_exception_sync_internal(env, class, tin, pc, 0); } -void helper_qemu_excp(CPUTriCoreState *env, uint32_t excp) -{ - CPUState *cs = env_cpu(env); - cs->exception_index = excp; - cpu_loop_exit(cs); -} - /* Addressing mode helper */ static uint16_t reverse16(uint16_t val) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index a0cc0f1cb3..07084407cb 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -3225,14 +3225,6 @@ static inline void gen_save_pc(target_ulong pc) tcg_gen_movi_tl(cpu_PC, pc); } -static void generate_qemu_excp(DisasContext *ctx, int excp) -{ - TCGv_i32 tmp = tcg_const_i32(excp); - gen_helper_qemu_excp(cpu_env, tmp); - ctx->base.is_jmp = DISAS_NORETURN; - tcg_temp_free(tmp); -} - static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { if (translator_use_goto_tb(&ctx->base, dest)) { @@ -3241,11 +3233,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) tcg_gen_exit_tb(ctx->base.tb, n); } else { gen_save_pc(dest); - if (ctx->base.singlestep_enabled) { - generate_qemu_excp(ctx, EXCP_DEBUG); - } else { - tcg_gen_lookup_and_goto_ptr(); - } + tcg_gen_lookup_and_goto_ptr(); } } From 02bf7fa022feae36b0f818b5ee30daccef8615fd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jul 2021 15:02:11 -1000 Subject: [PATCH 0563/1334] target/xtensa: Drop check for singlestep_enabled GDB single-stepping is now handled generically. Signed-off-by: Richard Henderson --- target/xtensa/translate.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index dcf6b500ef..09430c1bf9 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -382,18 +382,14 @@ static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) if (dc->icount) { tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount); } - if (dc->base.singlestep_enabled) { - gen_exception(dc, EXCP_DEBUG); + if (dc->op_flags & XTENSA_OP_POSTPROCESS) { + slot = gen_postprocess(dc, slot); + } + if (slot >= 0) { + tcg_gen_goto_tb(slot); + tcg_gen_exit_tb(dc->base.tb, slot); } else { - if (dc->op_flags & XTENSA_OP_POSTPROCESS) { - slot = gen_postprocess(dc, slot); - } - if (slot >= 0) { - tcg_gen_goto_tb(slot); - tcg_gen_exit_tb(dc->base.tb, slot); - } else { - tcg_gen_exit_tb(NULL, 0); - } + tcg_gen_exit_tb(NULL, 0); } dc->base.is_jmp = DISAS_NORETURN; } @@ -1293,12 +1289,7 @@ static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_NORETURN: break; case DISAS_TOO_MANY: - if (dc->base.singlestep_enabled) { - tcg_gen_movi_i32(cpu_pc, dc->pc); - gen_exception(dc, EXCP_DEBUG); - } else { - gen_jumpi(dc, dc->pc, 0); - } + gen_jumpi(dc, dc->pc, 0); break; default: g_assert_not_reached(); From 995b87dedc78b0467f5f18bbc3546072ba97516a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Aug 2021 00:25:28 -0700 Subject: [PATCH 0564/1334] Revert "cpu: Move cpu_common_props to hw/core/cpu.c" This reverts commit 1b36e4f5a5de585210ea95f2257839c2312be28f. Despite a comment saying why cpu_common_props cannot be placed in a file that is compiled once, it was moved anyway. Revert that. Since then, Property is not defined in hw/core/cpu.h, so it is now easier to declare a function to install the properties rather than the Property array itself. Cc: Eduardo Habkost Suggested-by: Peter Maydell Signed-off-by: Richard Henderson --- cpu.c | 21 +++++++++++++++++++++ hw/core/cpu-common.c | 17 +---------------- include/hw/core/cpu.h | 1 + 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cpu.c b/cpu.c index e1799a15bc..9bce67ef55 100644 --- a/cpu.c +++ b/cpu.c @@ -179,6 +179,27 @@ void cpu_exec_unrealizefn(CPUState *cpu) cpu_list_remove(cpu); } +static Property cpu_common_props[] = { +#ifndef CONFIG_USER_ONLY + /* + * Create a memory property for softmmu CPU object, + * so users can wire up its memory. (This can't go in hw/core/cpu.c + * because that file is compiled only once for both user-mode + * and system builds.) The default if no link is set up is to use + * the system address space. + */ + DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), +#endif + DEFINE_PROP_BOOL("start-powered-off", CPUState, start_powered_off, false), + DEFINE_PROP_END_OF_LIST(), +}; + +void cpu_class_init_props(DeviceClass *dc) +{ + device_class_set_props(dc, cpu_common_props); +} + void cpu_exec_initfn(CPUState *cpu) { cpu->as = NULL; diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index e2f5a64604..9e3241b430 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -257,21 +257,6 @@ static int64_t cpu_common_get_arch_id(CPUState *cpu) return cpu->cpu_index; } -static Property cpu_common_props[] = { -#ifndef CONFIG_USER_ONLY - /* Create a memory property for softmmu CPU object, - * so users can wire up its memory. (This can't go in hw/core/cpu.c - * because that file is compiled only once for both user-mode - * and system builds.) The default if no link is set up is to use - * the system address space. - */ - DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, - MemoryRegion *), -#endif - DEFINE_PROP_BOOL("start-powered-off", CPUState, start_powered_off, false), - DEFINE_PROP_END_OF_LIST(), -}; - static void cpu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -286,7 +271,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; dc->reset = cpu_common_reset; - device_class_set_props(dc, cpu_common_props); + cpu_class_init_props(dc); /* * Reason: CPUs still need special care by board code: wiring up * IRQs, adding reset handlers, halting non-first CPUs, ... diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index b7d5bc1200..1a10497af3 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1008,6 +1008,7 @@ void QEMU_NORETURN cpu_abort(CPUState *cpu, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /* $(top_srcdir)/cpu.c */ +void cpu_class_init_props(DeviceClass *dc); void cpu_exec_initfn(CPUState *cpu); void cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); From 719f874b83792951faa3fe67378eca4034bec3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 27 Jul 2021 10:30:08 +0200 Subject: [PATCH 0565/1334] target/mips: Check nanoMIPS DSP MULT[U] accumulator with Release 6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the "MIPS Architecture Extension: nanoMIPS32 DSP TRM" rev 0.04, MULT and MULTU opcodes: The value of ac selects an accumulator numbered from 0 to 3. When ac=0, this refers to the original HI/LO register pair of the MIPS32 architecture. In Release 6 of the MIPS Architecture, accumulators are eliminated from MIPS32. Ensure pre-Release 6 is restricted to HI/LO registers pair. Fixes: 8b3698b2947 ("target/mips: Add emulation of DSP ASE for nanoMIPS - part 4") Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/nanomips_translate.c.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index ccbcecad09..2c022a49f2 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -1868,6 +1868,9 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, TCGv_i32 t2 = tcg_temp_new_i32(); TCGv_i32 t3 = tcg_temp_new_i32(); + if (acc || ctx->insn_flags & ISA_MIPS_R6) { + check_dsp_r2(ctx); + } gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); tcg_gen_trunc_tl_i32(t2, t0); @@ -1925,6 +1928,9 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, TCGv_i32 t2 = tcg_temp_new_i32(); TCGv_i32 t3 = tcg_temp_new_i32(); + if (acc || ctx->insn_flags & ISA_MIPS_R6) { + check_dsp_r2(ctx); + } gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); tcg_gen_trunc_tl_i32(t2, t0); From e07f3e265b90d078d69c1f305563f04f1371b7aa Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Sat, 2 Oct 2021 19:45:37 +0100 Subject: [PATCH 0566/1334] hw/mips/boston: Massage memory map information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use memmap array to uinfy address of memory map. That would allow us reuse address information for FDT generation. Signed-off-by: Jiaxun Yang Reviewed-by: Philippe Mathieu-Daudé [PMD: Use local 'regaddr' in gen_firmware(), fix coding style] Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211002184539.169-2-jiaxun.yang@flygoat.com> --- hw/mips/boston.c | 108 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 28 deletions(-) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 20b06865b2..37b8278623 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -64,6 +64,44 @@ struct BostonState { hwaddr fdt_base; }; +enum { + BOSTON_LOWDDR, + BOSTON_PCIE0, + BOSTON_PCIE1, + BOSTON_PCIE2, + BOSTON_PCIE2_MMIO, + BOSTON_CM, + BOSTON_GIC, + BOSTON_CDMM, + BOSTON_CPC, + BOSTON_PLATREG, + BOSTON_UART, + BOSTON_LCD, + BOSTON_FLASH, + BOSTON_PCIE1_MMIO, + BOSTON_PCIE0_MMIO, + BOSTON_HIGHDDR, +}; + +static const MemMapEntry boston_memmap[] = { + [BOSTON_LOWDDR] = { 0x0, 0x10000000 }, + [BOSTON_PCIE0] = { 0x10000000, 0x2000000 }, + [BOSTON_PCIE1] = { 0x12000000, 0x2000000 }, + [BOSTON_PCIE2] = { 0x14000000, 0x2000000 }, + [BOSTON_PCIE2_MMIO] = { 0x16000000, 0x100000 }, + [BOSTON_CM] = { 0x16100000, 0x20000 }, + [BOSTON_GIC] = { 0x16120000, 0x20000 }, + [BOSTON_CDMM] = { 0x16140000, 0x8000 }, + [BOSTON_CPC] = { 0x16200000, 0x8000 }, + [BOSTON_PLATREG] = { 0x17ffd000, 0x1000 }, + [BOSTON_UART] = { 0x17ffe000, 0x20 }, + [BOSTON_LCD] = { 0x17fff000, 0x8 }, + [BOSTON_FLASH] = { 0x18000000, 0x8000000 }, + [BOSTON_PCIE1_MMIO] = { 0x20000000, 0x20000000 }, + [BOSTON_PCIE0_MMIO] = { 0x40000000, 0x40000000 }, + [BOSTON_HIGHDDR] = { 0x80000000, 0x0 }, +}; + enum boston_plat_reg { PLAT_FPGA_BUILD = 0x00, PLAT_CORE_CL = 0x04, @@ -275,24 +313,24 @@ type_init(boston_register_types) static void gen_firmware(uint32_t *p, hwaddr kernel_entry, hwaddr fdt_addr) { - const uint32_t cm_base = 0x16100000; - const uint32_t gic_base = 0x16120000; - const uint32_t cpc_base = 0x16200000; + uint64_t regaddr; /* Move CM GCRs */ - bl_gen_write_ulong(&p, - cpu_mips_phys_to_kseg1(NULL, GCR_BASE_ADDR + GCR_BASE_OFS), - cm_base); + regaddr = cpu_mips_phys_to_kseg1(NULL, GCR_BASE_ADDR + GCR_BASE_OFS), + bl_gen_write_ulong(&p, regaddr, + boston_memmap[BOSTON_CM].base); /* Move & enable GIC GCRs */ - bl_gen_write_ulong(&p, - cpu_mips_phys_to_kseg1(NULL, cm_base + GCR_GIC_BASE_OFS), - gic_base | GCR_GIC_BASE_GICEN_MSK); + regaddr = cpu_mips_phys_to_kseg1(NULL, boston_memmap[BOSTON_CM].base + + GCR_GIC_BASE_OFS), + bl_gen_write_ulong(&p, regaddr, + boston_memmap[BOSTON_GIC].base | GCR_GIC_BASE_GICEN_MSK); /* Move & enable CPC GCRs */ - bl_gen_write_ulong(&p, - cpu_mips_phys_to_kseg1(NULL, cm_base + GCR_CPC_BASE_OFS), - cpc_base | GCR_CPC_BASE_CPCEN_MSK); + regaddr = cpu_mips_phys_to_kseg1(NULL, boston_memmap[BOSTON_CM].base + + GCR_CPC_BASE_OFS), + bl_gen_write_ulong(&p, regaddr, + boston_memmap[BOSTON_CPC].base | GCR_CPC_BASE_CPCEN_MSK); /* * Setup argument registers to follow the UHI boot protocol: @@ -333,8 +371,9 @@ static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, ram_low_sz = MIN(256 * MiB, machine->ram_size); ram_high_sz = machine->ram_size - ram_low_sz; qemu_fdt_setprop_sized_cells(fdt, "/memory@0", "reg", - 1, 0x00000000, 1, ram_low_sz, - 1, 0x90000000, 1, ram_high_sz); + 1, boston_memmap[BOSTON_LOWDDR].base, 1, ram_low_sz, + 1, boston_memmap[BOSTON_HIGHDDR].base + ram_low_sz, + 1, ram_high_sz); fdt = g_realloc(fdt, fdt_totalsize(fdt)); qemu_fdt_dumpdtb(fdt, fdt_sz); @@ -438,11 +477,15 @@ static void boston_mach_init(MachineState *machine) sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->cps), 0, 0, 1); flash = g_new(MemoryRegion, 1); - memory_region_init_rom(flash, NULL, "boston.flash", 128 * MiB, - &error_fatal); - memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0); + memory_region_init_rom(flash, NULL, "boston.flash", + boston_memmap[BOSTON_FLASH].size, &error_fatal); + memory_region_add_subregion_overlap(sys_mem, + boston_memmap[BOSTON_FLASH].base, + flash, 0); - memory_region_add_subregion_overlap(sys_mem, 0x80000000, machine->ram, 0); + memory_region_add_subregion_overlap(sys_mem, + boston_memmap[BOSTON_HIGHDDR].base, + machine->ram, 0); ddr_low_alias = g_new(MemoryRegion, 1); memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr", @@ -451,32 +494,41 @@ static void boston_mach_init(MachineState *machine) memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0); xilinx_pcie_init(sys_mem, 0, - 0x10000000, 32 * MiB, - 0x40000000, 1 * GiB, + boston_memmap[BOSTON_PCIE0].base, + boston_memmap[BOSTON_PCIE0].size, + boston_memmap[BOSTON_PCIE0_MMIO].base, + boston_memmap[BOSTON_PCIE0_MMIO].size, get_cps_irq(&s->cps, 2), false); xilinx_pcie_init(sys_mem, 1, - 0x12000000, 32 * MiB, - 0x20000000, 512 * MiB, + boston_memmap[BOSTON_PCIE1].base, + boston_memmap[BOSTON_PCIE1].size, + boston_memmap[BOSTON_PCIE1_MMIO].base, + boston_memmap[BOSTON_PCIE1_MMIO].size, get_cps_irq(&s->cps, 1), false); pcie2 = xilinx_pcie_init(sys_mem, 2, - 0x14000000, 32 * MiB, - 0x16000000, 1 * MiB, + boston_memmap[BOSTON_PCIE2].base, + boston_memmap[BOSTON_PCIE2].size, + boston_memmap[BOSTON_PCIE2_MMIO].base, + boston_memmap[BOSTON_PCIE2_MMIO].size, get_cps_irq(&s->cps, 0), true); platreg = g_new(MemoryRegion, 1); memory_region_init_io(platreg, NULL, &boston_platreg_ops, s, - "boston-platregs", 0x1000); - memory_region_add_subregion_overlap(sys_mem, 0x17ffd000, platreg, 0); + "boston-platregs", + boston_memmap[BOSTON_PLATREG].size); + memory_region_add_subregion_overlap(sys_mem, + boston_memmap[BOSTON_PLATREG].base, platreg, 0); - s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2, + s->uart = serial_mm_init(sys_mem, boston_memmap[BOSTON_UART].base, 2, get_cps_irq(&s->cps, 3), 10000000, serial_hd(0), DEVICE_NATIVE_ENDIAN); lcd = g_new(MemoryRegion, 1); memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); - memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0); + memory_region_add_subregion_overlap(sys_mem, + boston_memmap[BOSTON_LCD].base, lcd, 0); chr = qemu_chr_new("lcd", "vc:320x240", NULL); qemu_chr_fe_init(&s->lcd_display, chr, NULL); From 10e3f30ff730624094a2fe6a81aaa72064853036 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Sat, 2 Oct 2021 19:45:38 +0100 Subject: [PATCH 0567/1334] hw/mips/boston: Allow loading elf kernel and dtb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ELF kernel allows us debugging much easier with DWARF symbols. Signed-off-by: Jiaxun Yang Reviewed-by: Philippe Mathieu-Daudé [PMD: Fix coding style] Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211002184539.169-3-jiaxun.yang@flygoat.com> --- hw/mips/boston.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 37b8278623..7374bb5da4 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "elf.h" #include "hw/boards.h" #include "hw/char/serial.h" #include "hw/ide/pci.h" @@ -551,10 +552,37 @@ static void boston_mach_init(MachineState *machine) exit(1); } } else if (machine->kernel_filename) { - fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); - if (fit_err) { - error_report("unable to load FIT image"); - exit(1); + uint64_t kernel_entry, kernel_high, kernel_size; + + kernel_size = load_elf(machine->kernel_filename, NULL, + cpu_mips_kseg0_to_phys, NULL, + &kernel_entry, NULL, &kernel_high, + NULL, 0, EM_MIPS, 1, 0); + + if (kernel_size) { + hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB); + hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr); + + s->kernel_entry = kernel_entry; + if (machine->dtb) { + int dt_size; + g_autofree const void *dtb_file_data, *dtb_load_data; + + dtb_file_data = load_device_tree(machine->dtb, &dt_size); + dtb_load_data = boston_fdt_filter(s, dtb_file_data, + NULL, &dtb_vaddr); + + /* Calculate real fdt size after filter */ + dt_size = fdt_totalsize(dtb_load_data); + rom_add_blob_fixed("dtb", dtb_load_data, dt_size, dtb_paddr); + } + } else { + /* Try to load file as FIT */ + fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); + if (fit_err) { + error_report("unable to load kernel image"); + exit(1); + } } gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000, From 723038999ef42fec4f845841d2d35a52f9ab1dbe Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Sat, 2 Oct 2021 19:45:39 +0100 Subject: [PATCH 0568/1334] hw/mips/boston: Add FDT generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generate FDT on our own if no dtb argument supplied. Avoid introducing unused device in FDT with user supplied dtb. Signed-off-by: Jiaxun Yang [PMD: Fix coding style] Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211002184539.169-4-jiaxun.yang@flygoat.com> --- hw/mips/boston.c | 245 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 236 insertions(+), 9 deletions(-) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 7374bb5da4..0e3cca5511 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -49,6 +49,15 @@ typedef struct BostonState BostonState; DECLARE_INSTANCE_CHECKER(BostonState, BOSTON, TYPE_BOSTON) +#define FDT_IRQ_TYPE_NONE 0 +#define FDT_IRQ_TYPE_LEVEL_HIGH 4 +#define FDT_GIC_SHARED 0 +#define FDT_GIC_LOCAL 1 +#define FDT_BOSTON_CLK_SYS 1 +#define FDT_BOSTON_CLK_CPU 2 +#define FDT_PCI_IRQ_MAP_PINS 4 +#define FDT_PCI_IRQ_MAP_DESCS 6 + struct BostonState { SysBusDevice parent_obj; @@ -437,6 +446,222 @@ xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr, return XILINX_PCIE_HOST(dev); } + +static void fdt_create_pcie(void *fdt, int gic_ph, int irq, hwaddr reg_base, + hwaddr reg_size, hwaddr mmio_base, hwaddr mmio_size) +{ + int i; + char *name, *intc_name; + uint32_t intc_ph; + uint32_t interrupt_map[FDT_PCI_IRQ_MAP_PINS][FDT_PCI_IRQ_MAP_DESCS]; + + intc_ph = qemu_fdt_alloc_phandle(fdt); + name = g_strdup_printf("/soc/pci@%" HWADDR_PRIx, reg_base); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", + "xlnx,axi-pcie-host-1.00.a"); + qemu_fdt_setprop_string(fdt, name, "device_type", "pci"); + qemu_fdt_setprop_cells(fdt, name, "reg", reg_base, reg_size); + + qemu_fdt_setprop_cell(fdt, name, "#address-cells", 3); + qemu_fdt_setprop_cell(fdt, name, "#size-cells", 2); + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 1); + + qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", gic_ph); + qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_SHARED, irq, + FDT_IRQ_TYPE_LEVEL_HIGH); + + qemu_fdt_setprop_cells(fdt, name, "ranges", 0x02000000, 0, mmio_base, + mmio_base, 0, mmio_size); + qemu_fdt_setprop_cells(fdt, name, "bus-range", 0x00, 0xff); + + + + intc_name = g_strdup_printf("%s/interrupt-controller", name); + qemu_fdt_add_subnode(fdt, intc_name); + qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, intc_name, "#address-cells", 0); + qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); + qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_ph); + + qemu_fdt_setprop_cells(fdt, name, "interrupt-map-mask", 0, 0, 0, 7); + for (i = 0; i < FDT_PCI_IRQ_MAP_PINS; i++) { + uint32_t *irqmap = interrupt_map[i]; + + irqmap[0] = cpu_to_be32(0); + irqmap[1] = cpu_to_be32(0); + irqmap[2] = cpu_to_be32(0); + irqmap[3] = cpu_to_be32(i + 1); + irqmap[4] = cpu_to_be32(intc_ph); + irqmap[5] = cpu_to_be32(i + 1); + } + qemu_fdt_setprop(fdt, name, "interrupt-map", + &interrupt_map, sizeof(interrupt_map)); + + g_free(intc_name); + g_free(name); +} + +static const void *create_fdt(BostonState *s, + const MemMapEntry *memmap, int *dt_size) +{ + void *fdt; + int cpu; + MachineState *mc = s->mach; + uint32_t platreg_ph, gic_ph, clk_ph; + char *name, *gic_name, *platreg_name, *stdout_name; + static const char * const syscon_compat[2] = { + "img,boston-platform-regs", "syscon" + }; + + fdt = create_device_tree(dt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + platreg_ph = qemu_fdt_alloc_phandle(fdt); + gic_ph = qemu_fdt_alloc_phandle(fdt); + clk_ph = qemu_fdt_alloc_phandle(fdt); + + qemu_fdt_setprop_string(fdt, "/", "model", "img,boston"); + qemu_fdt_setprop_string(fdt, "/", "compatible", "img,boston"); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x1); + + + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + + for (cpu = 0; cpu < mc->smp.cpus; cpu++) { + name = g_strdup_printf("/cpus/cpu@%d", cpu); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "img,mips"); + qemu_fdt_setprop_string(fdt, name, "status", "okay"); + qemu_fdt_setprop_cell(fdt, name, "reg", cpu); + qemu_fdt_setprop_string(fdt, name, "device_type", "cpu"); + qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_CPU); + g_free(name); + } + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x1); + + fdt_create_pcie(fdt, gic_ph, 2, + memmap[BOSTON_PCIE0].base, memmap[BOSTON_PCIE0].size, + memmap[BOSTON_PCIE0_MMIO].base, memmap[BOSTON_PCIE0_MMIO].size); + + fdt_create_pcie(fdt, gic_ph, 1, + memmap[BOSTON_PCIE1].base, memmap[BOSTON_PCIE1].size, + memmap[BOSTON_PCIE1_MMIO].base, memmap[BOSTON_PCIE1_MMIO].size); + + fdt_create_pcie(fdt, gic_ph, 0, + memmap[BOSTON_PCIE2].base, memmap[BOSTON_PCIE2].size, + memmap[BOSTON_PCIE2_MMIO].base, memmap[BOSTON_PCIE2_MMIO].size); + + /* GIC with it's timer node */ + gic_name = g_strdup_printf("/soc/interrupt-controller@%" HWADDR_PRIx, + memmap[BOSTON_GIC].base); + qemu_fdt_add_subnode(fdt, gic_name); + qemu_fdt_setprop_string(fdt, gic_name, "compatible", "mti,gic"); + qemu_fdt_setprop_cells(fdt, gic_name, "reg", memmap[BOSTON_GIC].base, + memmap[BOSTON_GIC].size); + qemu_fdt_setprop(fdt, gic_name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, gic_name, "#interrupt-cells", 3); + qemu_fdt_setprop_cell(fdt, gic_name, "phandle", gic_ph); + + name = g_strdup_printf("%s/timer", gic_name); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "mti,gic-timer"); + qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_LOCAL, 1, + FDT_IRQ_TYPE_NONE); + qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_CPU); + g_free(name); + g_free(gic_name); + + /* CDMM node */ + name = g_strdup_printf("/soc/cdmm@%" HWADDR_PRIx, memmap[BOSTON_CDMM].base); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "mti,mips-cdmm"); + qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_CDMM].base, + memmap[BOSTON_CDMM].size); + g_free(name); + + /* CPC node */ + name = g_strdup_printf("/soc/cpc@%" HWADDR_PRIx, memmap[BOSTON_CPC].base); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "mti,mips-cpc"); + qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_CPC].base, + memmap[BOSTON_CPC].size); + g_free(name); + + /* platreg and it's clk node */ + platreg_name = g_strdup_printf("/soc/system-controller@%" HWADDR_PRIx, + memmap[BOSTON_PLATREG].base); + qemu_fdt_add_subnode(fdt, platreg_name); + qemu_fdt_setprop_string_array(fdt, platreg_name, "compatible", + (char **)&syscon_compat, + ARRAY_SIZE(syscon_compat)); + qemu_fdt_setprop_cells(fdt, platreg_name, "reg", + memmap[BOSTON_PLATREG].base, + memmap[BOSTON_PLATREG].size); + qemu_fdt_setprop_cell(fdt, platreg_name, "phandle", platreg_ph); + + name = g_strdup_printf("%s/clock", platreg_name); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "img,boston-clock"); + qemu_fdt_setprop_cell(fdt, name, "#clock-cells", 1); + qemu_fdt_setprop_cell(fdt, name, "phandle", clk_ph); + g_free(name); + g_free(platreg_name); + + /* reboot node */ + name = g_strdup_printf("/soc/reboot"); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-reboot"); + qemu_fdt_setprop_cell(fdt, name, "regmap", platreg_ph); + qemu_fdt_setprop_cell(fdt, name, "offset", 0x10); + qemu_fdt_setprop_cell(fdt, name, "mask", 0x10); + g_free(name); + + /* uart node */ + name = g_strdup_printf("/soc/uart@%" HWADDR_PRIx, memmap[BOSTON_UART].base); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_UART].base, + memmap[BOSTON_UART].size); + qemu_fdt_setprop_cell(fdt, name, "reg-shift", 0x2); + qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", gic_ph); + qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_SHARED, 3, + FDT_IRQ_TYPE_LEVEL_HIGH); + qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_SYS); + + qemu_fdt_add_subnode(fdt, "/chosen"); + stdout_name = g_strdup_printf("%s:115200", name); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", stdout_name); + g_free(stdout_name); + g_free(name); + + /* lcd node */ + name = g_strdup_printf("/soc/lcd@%" HWADDR_PRIx, memmap[BOSTON_LCD].base); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "img,boston-lcd"); + qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_LCD].base, + memmap[BOSTON_LCD].size); + g_free(name); + + name = g_strdup_printf("/memory@0"); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "device_type", "memory"); + g_free(name); + + return fdt; +} + static void boston_mach_init(MachineState *machine) { DeviceState *dev; @@ -560,22 +785,24 @@ static void boston_mach_init(MachineState *machine) NULL, 0, EM_MIPS, 1, 0); if (kernel_size) { + int dt_size; + g_autofree const void *dtb_file_data, *dtb_load_data; hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB); hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr); s->kernel_entry = kernel_entry; if (machine->dtb) { - int dt_size; - g_autofree const void *dtb_file_data, *dtb_load_data; - dtb_file_data = load_device_tree(machine->dtb, &dt_size); - dtb_load_data = boston_fdt_filter(s, dtb_file_data, - NULL, &dtb_vaddr); - - /* Calculate real fdt size after filter */ - dt_size = fdt_totalsize(dtb_load_data); - rom_add_blob_fixed("dtb", dtb_load_data, dt_size, dtb_paddr); + } else { + dtb_file_data = create_fdt(s, boston_memmap, &dt_size); } + + dtb_load_data = boston_fdt_filter(s, dtb_file_data, + NULL, &dtb_vaddr); + + /* Calculate real fdt size after filter */ + dt_size = fdt_totalsize(dtb_load_data); + rom_add_blob_fixed("dtb", dtb_load_data, dt_size, dtb_paddr); } else { /* Try to load file as FIT */ fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); From 7da51cb391bc1100b941d04a0e9fec2cdc5b9632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 02:19:31 +0200 Subject: [PATCH 0569/1334] target/mips: Remove unused register from MSA 2R/2RF instruction format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commits cbe50b9a8e7 ("target-mips: add MSA VEC/2R format instructions") and 3bdeb68866e ("target-mips: add MSA 2RF format instructions") added the MSA 2R/2RF instructions. However these instructions don't use any target vector register, so remove the unused TCG temporaries. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211003175743.3738710-2-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 8170a8df26..ee6424126f 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -1942,13 +1942,11 @@ static void gen_msa_2r(DisasContext *ctx) { #define MASK_MSA_2R(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21)) | \ (op & (0x7 << 18))) - uint8_t wt = (ctx->opcode >> 16) & 0x1f; uint8_t ws = (ctx->opcode >> 11) & 0x1f; uint8_t wd = (ctx->opcode >> 6) & 0x1f; uint8_t df = (ctx->opcode >> 16) & 0x3; TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tws = tcg_const_i32(ws); - TCGv_i32 twt = tcg_const_i32(wt); TCGv_i32 tdf = tcg_const_i32(df); switch (MASK_MSA_2R(ctx->opcode)) { @@ -2018,7 +2016,6 @@ static void gen_msa_2r(DisasContext *ctx) tcg_temp_free_i32(twd); tcg_temp_free_i32(tws); - tcg_temp_free_i32(twt); tcg_temp_free_i32(tdf); } @@ -2026,13 +2023,11 @@ static void gen_msa_2rf(DisasContext *ctx) { #define MASK_MSA_2RF(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21)) | \ (op & (0xf << 17))) - uint8_t wt = (ctx->opcode >> 16) & 0x1f; uint8_t ws = (ctx->opcode >> 11) & 0x1f; uint8_t wd = (ctx->opcode >> 6) & 0x1f; uint8_t df = (ctx->opcode >> 16) & 0x1; TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tws = tcg_const_i32(ws); - TCGv_i32 twt = tcg_const_i32(wt); /* adjust df value for floating-point instruction */ TCGv_i32 tdf = tcg_const_i32(df + 2); @@ -2089,7 +2084,6 @@ static void gen_msa_2rf(DisasContext *ctx) tcg_temp_free_i32(twd); tcg_temp_free_i32(tws); - tcg_temp_free_i32(twt); tcg_temp_free_i32(tdf); } From 2b537a3d856e2500acc321c9333f03b36a829f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 14:43:44 +0200 Subject: [PATCH 0570/1334] target/mips: Use tcg_constant_i32() in gen_msa_elm_df() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Data Format is a 2-bit constant value. Avoid using a TCG temporary by moving it to the constant pool. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211003175743.3738710-3-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index ee6424126f..20036ae496 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -1650,7 +1650,7 @@ static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) TCGv_i32 tws = tcg_const_i32(ws); TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tn = tcg_const_i32(n); - TCGv_i32 tdf = tcg_const_i32(df); + TCGv_i32 tdf = tcg_constant_i32(df); switch (MASK_MSA_ELM(ctx->opcode)) { case OPC_SLDI_df: @@ -1748,7 +1748,6 @@ static void gen_msa_elm_df(DisasContext *ctx, uint32_t df, uint32_t n) tcg_temp_free_i32(twd); tcg_temp_free_i32(tws); tcg_temp_free_i32(tn); - tcg_temp_free_i32(tdf); } static void gen_msa_elm(DisasContext *ctx) From e81a48b9e7fb3db72b7820b0342779227bf0510f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 14:44:21 +0200 Subject: [PATCH 0571/1334] target/mips: Use tcg_constant_i32() in gen_msa_2rf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using a TCG temporary by moving Data Format to the constant pool. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211003175743.3738710-4-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 20036ae496..5e8f80f2f2 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -2028,7 +2028,7 @@ static void gen_msa_2rf(DisasContext *ctx) TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tws = tcg_const_i32(ws); /* adjust df value for floating-point instruction */ - TCGv_i32 tdf = tcg_const_i32(df + 2); + TCGv_i32 tdf = tcg_constant_i32(df + 2); switch (MASK_MSA_2RF(ctx->opcode)) { case OPC_FCLASS_df: @@ -2083,7 +2083,6 @@ static void gen_msa_2rf(DisasContext *ctx) tcg_temp_free_i32(twd); tcg_temp_free_i32(tws); - tcg_temp_free_i32(tdf); } static void gen_msa_vec_v(DisasContext *ctx) From 74341af7d6c7093bba0893a7efd53d30aa50a200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 14:41:57 +0200 Subject: [PATCH 0572/1334] target/mips: Use tcg_constant_i32() in gen_msa_2r() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using a TCG temporary by moving Data Format to the constant pool. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211003175743.3738710-5-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 5e8f80f2f2..bbe9146513 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -1946,7 +1946,6 @@ static void gen_msa_2r(DisasContext *ctx) uint8_t df = (ctx->opcode >> 16) & 0x3; TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tws = tcg_const_i32(ws); - TCGv_i32 tdf = tcg_const_i32(df); switch (MASK_MSA_2R(ctx->opcode)) { case OPC_FILL_df: @@ -1957,7 +1956,8 @@ static void gen_msa_2r(DisasContext *ctx) break; } #endif - gen_helper_msa_fill_df(cpu_env, tdf, twd, tws); /* trs */ + gen_helper_msa_fill_df(cpu_env, tcg_constant_i32(df), + twd, tws); /* trs */ break; case OPC_NLOC_df: switch (df) { @@ -2015,7 +2015,6 @@ static void gen_msa_2r(DisasContext *ctx) tcg_temp_free_i32(twd); tcg_temp_free_i32(tws); - tcg_temp_free_i32(tdf); } static void gen_msa_2rf(DisasContext *ctx) From 1b5c0a11471cd0c3c2f206fd49e31972a2dc3bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 14:39:05 +0200 Subject: [PATCH 0573/1334] target/mips: Use tcg_constant_i32() in gen_msa_3rf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using a TCG temporary by moving Data Format to the constant pool. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211003175743.3738710-6-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index bbe9146513..e107cad57e 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -1790,10 +1790,22 @@ static void gen_msa_3rf(DisasContext *ctx) TCGv_i32 twd = tcg_const_i32(wd); TCGv_i32 tws = tcg_const_i32(ws); TCGv_i32 twt = tcg_const_i32(wt); - TCGv_i32 tdf = tcg_temp_new_i32(); + TCGv_i32 tdf; /* adjust df value for floating-point instruction */ - tcg_gen_movi_i32(tdf, df + 2); + switch (MASK_MSA_3RF(ctx->opcode)) { + case OPC_MUL_Q_df: + case OPC_MADD_Q_df: + case OPC_MSUB_Q_df: + case OPC_MULR_Q_df: + case OPC_MADDR_Q_df: + case OPC_MSUBR_Q_df: + tdf = tcg_constant_i32(df + 1); + break; + default: + tdf = tcg_constant_i32(df + 2); + break; + } switch (MASK_MSA_3RF(ctx->opcode)) { case OPC_FCAF_df: @@ -1836,7 +1848,6 @@ static void gen_msa_3rf(DisasContext *ctx) gen_helper_msa_fmadd_df(cpu_env, tdf, twd, tws, twt); break; case OPC_MUL_Q_df: - tcg_gen_movi_i32(tdf, df + 1); gen_helper_msa_mul_q_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FCULT_df: @@ -1846,14 +1857,12 @@ static void gen_msa_3rf(DisasContext *ctx) gen_helper_msa_fmsub_df(cpu_env, tdf, twd, tws, twt); break; case OPC_MADD_Q_df: - tcg_gen_movi_i32(tdf, df + 1); gen_helper_msa_madd_q_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FCLE_df: gen_helper_msa_fcle_df(cpu_env, tdf, twd, tws, twt); break; case OPC_MSUB_Q_df: - tcg_gen_movi_i32(tdf, df + 1); gen_helper_msa_msub_q_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FCULE_df: @@ -1896,7 +1905,6 @@ static void gen_msa_3rf(DisasContext *ctx) gen_helper_msa_fmin_df(cpu_env, tdf, twd, tws, twt); break; case OPC_MULR_Q_df: - tcg_gen_movi_i32(tdf, df + 1); gen_helper_msa_mulr_q_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FSULT_df: @@ -1906,7 +1914,6 @@ static void gen_msa_3rf(DisasContext *ctx) gen_helper_msa_fmin_a_df(cpu_env, tdf, twd, tws, twt); break; case OPC_MADDR_Q_df: - tcg_gen_movi_i32(tdf, df + 1); gen_helper_msa_maddr_q_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FSLE_df: @@ -1916,7 +1923,6 @@ static void gen_msa_3rf(DisasContext *ctx) gen_helper_msa_fmax_df(cpu_env, tdf, twd, tws, twt); break; case OPC_MSUBR_Q_df: - tcg_gen_movi_i32(tdf, df + 1); gen_helper_msa_msubr_q_df(cpu_env, tdf, twd, tws, twt); break; case OPC_FSULE_df: @@ -1934,7 +1940,6 @@ static void gen_msa_3rf(DisasContext *ctx) tcg_temp_free_i32(twd); tcg_temp_free_i32(tws); tcg_temp_free_i32(twt); - tcg_temp_free_i32(tdf); } static void gen_msa_2r(DisasContext *ctx) From 469a316dc42cd669ab502a385fdd3539fd45e36e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 18:10:35 +0200 Subject: [PATCH 0574/1334] target/mips: Use explicit extract32() calls in gen_msa_i5() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already use sextract32(), use extract32() for completeness instead of open-coding it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211003175743.3738710-7-f4bug@amsat.org> --- target/mips/tcg/msa_translate.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index e107cad57e..3ef912da6b 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -473,15 +473,12 @@ static void gen_msa_i8(DisasContext *ctx) static void gen_msa_i5(DisasContext *ctx) { #define MASK_MSA_I5(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) - uint8_t df = (ctx->opcode >> 21) & 0x3; int8_t s5 = (int8_t) sextract32(ctx->opcode, 16, 5); - uint8_t u5 = (ctx->opcode >> 16) & 0x1f; - uint8_t ws = (ctx->opcode >> 11) & 0x1f; - uint8_t wd = (ctx->opcode >> 6) & 0x1f; + uint8_t u5 = extract32(ctx->opcode, 16, 5); - TCGv_i32 tdf = tcg_const_i32(df); - TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 tdf = tcg_const_i32(extract32(ctx->opcode, 21, 2)); + TCGv_i32 twd = tcg_const_i32(extract32(ctx->opcode, 11, 5)); + TCGv_i32 tws = tcg_const_i32(extract32(ctx->opcode, 6, 5)); TCGv_i32 timm = tcg_temp_new_i32(); tcg_gen_movi_i32(timm, u5); From d2db0f729da6946873b1352bfb7c12c2c9f91fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 14:25:14 +0200 Subject: [PATCH 0575/1334] target/mips: Use tcg_constant_tl() in gen_compute_compact_branch() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The offset is constant and read-only: move it to the constant pool. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211003175743.3738710-9-f4bug@amsat.org> --- target/mips/tcg/translate.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 0e59b97190..5fdeb67e82 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -11857,13 +11857,11 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, } else { /* OPC_JIC, OPC_JIALC */ TCGv tbase = tcg_temp_new(); - TCGv toffset = tcg_temp_new(); + TCGv toffset = tcg_constant_tl(offset); gen_load_gpr(tbase, rt); - tcg_gen_movi_tl(toffset, offset); gen_op_addr_add(ctx, btarget, tbase, toffset); tcg_temp_free(tbase); - tcg_temp_free(toffset); } break; default: From 0e235827de65b8a0a5fa403ad9ed15d04f8b1a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 13 Oct 2021 23:42:37 +0200 Subject: [PATCH 0576/1334] target/mips: Fix DEXTRV_S.H DSP opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While for the DEXTR_S.H opcode: "The shift argument is provided in the instruction." For the DEXTRV_S.H opcode we have: "The five least-significant bits of register rs provide the shift argument, interpreted as a five-bit unsigned integer; the remaining bits in rs are ignored." While 't1' contains the 'rs' register content (the shift value for DEXTR_S.H), we need to load the value of 'rs' for DEXTRV_S.H. We can directly use the v1_t TCG register which already contains this shift value. Fixes: b53371ed5d4 ("target-mips: Add ASE DSP accumulator instructions") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211013215652.1764551-1-f4bug@amsat.org> --- target/mips/tcg/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 5fdeb67e82..519b00121f 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -13796,8 +13796,7 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, break; case OPC_DEXTRV_S_H: tcg_gen_movi_tl(t0, v2); - tcg_gen_movi_tl(t1, v1); - gen_helper_dextr_s_h(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextr_s_h(cpu_gpr[ret], t0, v1_t, cpu_env); break; case OPC_DEXTRV_L: tcg_gen_movi_tl(t0, v2); From cfddceba7f9f56a5564015962154dccd242f5c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Oct 2021 17:45:29 +0200 Subject: [PATCH 0577/1334] target/mips: Remove unused TCG temporary in gen_mipsdsp_accinsn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since gen_mipsdsp_accinsn() got added in commit b53371ed5d4 ("target-mips: Add ASE DSP accumulator instructions"), the 'v2_t' TCG temporary has never been used. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20211014224551.2204949-1-f4bug@amsat.org> --- target/mips/tcg/translate.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 519b00121f..47db35d7dd 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -13616,7 +13616,6 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, TCGv t0; TCGv t1; TCGv v1_t; - TCGv v2_t; int16_t imm; if ((ret == 0) && (check_ret == 1)) { @@ -13627,10 +13626,8 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, t0 = tcg_temp_new(); t1 = tcg_temp_new(); v1_t = tcg_temp_new(); - v2_t = tcg_temp_new(); gen_load_gpr(v1_t, v1); - gen_load_gpr(v2_t, v2); switch (op1) { case OPC_EXTR_W_DSP: @@ -13830,7 +13827,6 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, tcg_temp_free(t0); tcg_temp_free(t1); tcg_temp_free(v1_t); - tcg_temp_free(v2_t); } /* End MIPSDSP functions. */ From 7c8eae45c0c12c92055486184f779d57a7677948 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 15 Oct 2021 11:16:54 +0200 Subject: [PATCH 0578/1334] via-ide: Set user_creatable to false MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This model only works as a function of the via superio chip not as a standalone PCI device. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211015092159.3E863748F57@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/via.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/ide/via.c b/hw/ide/via.c index 94cc2142c7..e91dad632a 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -217,6 +217,9 @@ static void via_ide_class_init(ObjectClass *klass, void *data) dc->reset = via_ide_reset; dc->vmsd = &vmstate_ide_pci; + /* Reason: only works as function of VIA southbridge */ + dc->user_creatable = false; + k->realize = via_ide_realize; k->exit = via_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_VIA; From 3a2f166fe05ce4b00ca781d7abd08e6accd6e472 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 15 Oct 2021 03:06:20 +0200 Subject: [PATCH 0579/1334] vt82c686: Move common code to via_isa_realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vt82c686b_realize and vt8231_realize methods are almost identical, factor out the common parts to a via_isa_realize function to avoid code duplication. Signed-off-by: BALATON Zoltan Reviewed-by: Jiaxun Yang Reviewed-by: Philippe Mathieu-Daudé Message-Id: <7cb7a16ff4daf8f48d576246255bea1fd355207c.1634259980.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/isa/vt82c686.c | 67 ++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index f57f3e7067..5b41539f2c 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -542,6 +542,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA) struct ViaISAState { PCIDevice dev; qemu_irq cpu_intr; + ISABus *isa_bus; ViaSuperIOState *via_sio; }; @@ -572,6 +573,29 @@ static void via_isa_request_i8259_irq(void *opaque, int irq, int level) qemu_set_irq(s->cpu_intr, level); } +static void via_isa_realize(PCIDevice *d, Error **errp) +{ + ViaISAState *s = VIA_ISA(d); + DeviceState *dev = DEVICE(d); + qemu_irq *isa_irq; + int i; + + qdev_init_gpio_out(dev, &s->cpu_intr, 1); + isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); + s->isa_bus = isa_bus_new(dev, get_system_memory(), pci_address_space_io(d), + &error_fatal); + isa_bus_irqs(s->isa_bus, i8259_init(s->isa_bus, *isa_irq)); + i8254_pit_init(s->isa_bus, 0x40, 0, NULL); + i8257_dma_init(s->isa_bus, 0); + mc146818_rtc_init(s->isa_bus, 2000, NULL); + + for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i++) { + if (i < PCI_COMMAND || i >= PCI_REVISION_ID) { + d->wmask[i] = 0; + } + } +} + /* TYPE_VT82C686B_ISA */ static void vt82c686b_write_config(PCIDevice *d, uint32_t addr, @@ -610,27 +634,10 @@ static void vt82c686b_isa_reset(DeviceState *dev) static void vt82c686b_realize(PCIDevice *d, Error **errp) { ViaISAState *s = VIA_ISA(d); - DeviceState *dev = DEVICE(d); - ISABus *isa_bus; - qemu_irq *isa_irq; - int i; - qdev_init_gpio_out(dev, &s->cpu_intr, 1); - isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); - isa_bus = isa_bus_new(dev, get_system_memory(), pci_address_space_io(d), - &error_fatal); - isa_bus_irqs(isa_bus, i8259_init(isa_bus, *isa_irq)); - i8254_pit_init(isa_bus, 0x40, 0, NULL); - i8257_dma_init(isa_bus, 0); - s->via_sio = VIA_SUPERIO(isa_create_simple(isa_bus, + via_isa_realize(d, errp); + s->via_sio = VIA_SUPERIO(isa_create_simple(s->isa_bus, TYPE_VT82C686B_SUPERIO)); - mc146818_rtc_init(isa_bus, 2000, NULL); - - for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i++) { - if (i < PCI_COMMAND || i >= PCI_REVISION_ID) { - d->wmask[i] = 0; - } - } } static void vt82c686b_class_init(ObjectClass *klass, void *data) @@ -691,26 +698,10 @@ static void vt8231_isa_reset(DeviceState *dev) static void vt8231_realize(PCIDevice *d, Error **errp) { ViaISAState *s = VIA_ISA(d); - DeviceState *dev = DEVICE(d); - ISABus *isa_bus; - qemu_irq *isa_irq; - int i; - qdev_init_gpio_out(dev, &s->cpu_intr, 1); - isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); - isa_bus = isa_bus_new(dev, get_system_memory(), pci_address_space_io(d), - &error_fatal); - isa_bus_irqs(isa_bus, i8259_init(isa_bus, *isa_irq)); - i8254_pit_init(isa_bus, 0x40, 0, NULL); - i8257_dma_init(isa_bus, 0); - s->via_sio = VIA_SUPERIO(isa_create_simple(isa_bus, TYPE_VT8231_SUPERIO)); - mc146818_rtc_init(isa_bus, 2000, NULL); - - for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i++) { - if (i < PCI_COMMAND || i >= PCI_REVISION_ID) { - d->wmask[i] = 0; - } - } + via_isa_realize(d, errp); + s->via_sio = VIA_SUPERIO(isa_create_simple(s->isa_bus, + TYPE_VT8231_SUPERIO)); } static void vt8231_class_init(ObjectClass *klass, void *data) From a4d65b701ffce52b69b5b6c3f253519129af182e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 15 Oct 2021 03:06:20 +0200 Subject: [PATCH 0580/1334] vt82c686: Add a method to VIA_ISA to raise ISA interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Other functions in the VT82xx chips need to raise ISA interrupts. Keep a reference to them in the device state and add via_isa_set_irq() to allow setting their state. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Jiaxun Yang Message-Id: <778c04dc2c8affac060b8edf9e8d7dab3c3e04eb.1634259980.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/isa/vt82c686.c | 10 +++++++++- include/hw/isa/vt82c686.h | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 5b41539f2c..8f656251b8 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -542,6 +542,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA) struct ViaISAState { PCIDevice dev; qemu_irq cpu_intr; + qemu_irq *isa_irqs; ISABus *isa_bus; ViaSuperIOState *via_sio; }; @@ -567,6 +568,12 @@ static const TypeInfo via_isa_info = { }, }; +void via_isa_set_irq(PCIDevice *d, int n, int level) +{ + ViaISAState *s = VIA_ISA(d); + qemu_set_irq(s->isa_irqs[n], level); +} + static void via_isa_request_i8259_irq(void *opaque, int irq, int level) { ViaISAState *s = opaque; @@ -584,7 +591,8 @@ static void via_isa_realize(PCIDevice *d, Error **errp) isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); s->isa_bus = isa_bus_new(dev, get_system_memory(), pci_address_space_io(d), &error_fatal); - isa_bus_irqs(s->isa_bus, i8259_init(s->isa_bus, *isa_irq)); + s->isa_irqs = i8259_init(s->isa_bus, *isa_irq); + isa_bus_irqs(s->isa_bus, s->isa_irqs); i8254_pit_init(s->isa_bus, 0x40, 0, NULL); i8257_dma_init(s->isa_bus, 0); mc146818_rtc_init(s->isa_bus, 2000, NULL); diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h index 0f01aaa471..56ac141be3 100644 --- a/include/hw/isa/vt82c686.h +++ b/include/hw/isa/vt82c686.h @@ -1,6 +1,8 @@ #ifndef HW_VT82C686_H #define HW_VT82C686_H +#include "hw/pci/pci.h" + #define TYPE_VT82C686B_ISA "vt82c686b-isa" #define TYPE_VT82C686B_PM "vt82c686b-pm" #define TYPE_VT8231_ISA "vt8231-isa" @@ -8,4 +10,6 @@ #define TYPE_VIA_AC97 "via-ac97" #define TYPE_VIA_MC97 "via-mc97" +void via_isa_set_irq(PCIDevice *d, int n, int level); + #endif From 2792cf20ca7eed0e354a0ed731422411faca4908 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 15 Oct 2021 03:06:20 +0200 Subject: [PATCH 0581/1334] via-ide: Avoid using isa_get_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use via_isa_set_irq() which better encapsulates irq handling in the vt82xx model and avoids using isa_get_irq() that has a comment saying it should not be used. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <26cb1848c9fc0360df7a57c2c9ba5e03c4a692b5.1634259980.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/via.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ide/via.c b/hw/ide/via.c index e91dad632a..82def819c4 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -29,7 +29,7 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "sysemu/dma.h" - +#include "hw/isa/vt82c686.h" #include "hw/ide/pci.h" #include "trace.h" @@ -112,7 +112,7 @@ static void via_ide_set_irq(void *opaque, int n, int level) d->config[0x70 + n * 8] &= ~0x80; } - qemu_set_irq(isa_get_irq(NULL, 14 + n), level); + via_isa_set_irq(pci_get_function_0(d), 14 + n, level); } static void via_ide_reset(DeviceState *dev) From 948516a3fac0bdd47eb127fe1a86148ed86d5c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Urankar?= Date: Sat, 8 Jul 2017 13:13:31 +0200 Subject: [PATCH 0582/1334] bsd-user/mmap.c: Always zero MAP_ANONYMOUS memory in mmap_frag() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to the equivalent linux-user commit e6deac9cf99 When mapping MAP_ANONYMOUS memory fragments, still need notice about to set it zero, or it will cause issues. Signed-off-by: Mikaël Urankar Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/mmap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index b40ab9045f..fc3c1480f5 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -180,10 +180,12 @@ static int mmap_frag(abi_ulong real_start, if (prot_new != (prot1 | PROT_WRITE)) mprotect(host_start, qemu_host_page_size, prot_new); } else { - /* just update the protection */ if (prot_new != prot1) { mprotect(host_start, qemu_host_page_size, prot_new); } + if (prot_new & PROT_WRITE) { + memset(g2h_untagged(start), 0, end - start); + } } return 0; } From 26778ac3da794f29c2c7c7d473f0a8d77b874392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Urankar?= Date: Thu, 16 Sep 2021 17:45:05 -0600 Subject: [PATCH 0583/1334] bsd-user/mmap.c: check pread's return value to fix warnings with _FORTIFY_SOURCE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simmilar to the equivalent linux-user: commit fb7e378cf9c, which added checking to pread's return value. Update to current qemu standards with {} around the if statement. Signed-off-by: Mikaël Urankar Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/mmap.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index fc3c1480f5..4f4fa3ab46 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -174,7 +174,9 @@ static int mmap_frag(abi_ulong real_start, mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); /* read the corresponding file data */ - pread(fd, g2h_untagged(start), end - start, offset); + if (pread(fd, g2h_untagged(start), end - start, offset) == -1) { + return -1; + } /* put final protection */ if (prot_new != (prot1 | PROT_WRITE)) @@ -593,7 +595,9 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, -1, 0); if (retaddr == -1) goto fail; - pread(fd, g2h_untagged(start), len, offset); + if (pread(fd, g2h_untagged(start), len, offset) == -1) { + goto fail; + } if (!(prot & PROT_WRITE)) { ret = target_mprotect(start, len, prot); if (ret != 0) { From 36d5d891559f6b9f0bae4907669de9bfdf5d4d94 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Thu, 16 Sep 2021 18:37:21 -0600 Subject: [PATCH 0584/1334] bsd-user/mmap.c: MAP_ symbols are defined, so no need for ifdefs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All these MAP_ symbols are always defined on supported FreeBSD versions (12.2 and newer), so remove the #ifdefs since they aren't needed. Signed-off-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/mmap.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 4f4fa3ab46..6f33aec58b 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -286,13 +286,9 @@ static abi_ulong mmap_find_vma_aligned(abi_ulong start, abi_ulong size, wrapped = repeat = 0; prev = 0; flags = MAP_ANONYMOUS | MAP_PRIVATE; -#ifdef MAP_ALIGNED if (alignment != 0) { flags |= MAP_ALIGNED(alignment); } -#else - /* XXX TODO */ -#endif for (;; prev = ptr) { /* @@ -407,22 +403,18 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, printf("MAP_ALIGNED(%u) ", (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT); } -#if MAP_GUARD if (flags & MAP_GUARD) { printf("MAP_GUARD "); } -#endif if (flags & MAP_FIXED) { printf("MAP_FIXED "); } if (flags & MAP_ANONYMOUS) { printf("MAP_ANON "); } -#ifdef MAP_EXCL if (flags & MAP_EXCL) { printf("MAP_EXCL "); } -#endif if (flags & MAP_PRIVATE) { printf("MAP_PRIVATE "); } @@ -432,11 +424,9 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, if (flags & MAP_NOCORE) { printf("MAP_NOCORE "); } -#ifdef MAP_STACK if (flags & MAP_STACK) { printf("MAP_STACK "); } -#endif printf("fd=%d offset=0x%llx\n", fd, offset); } #endif @@ -445,7 +435,6 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, errno = EINVAL; goto fail; } -#ifdef MAP_STACK if (flags & MAP_STACK) { if ((fd != -1) || ((prot & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE))) { @@ -453,8 +442,6 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, goto fail; } } -#endif /* MAP_STACK */ -#ifdef MAP_GUARD if ((flags & MAP_GUARD) && (prot != PROT_NONE || fd != -1 || offset != 0 || (flags & (MAP_SHARED | MAP_PRIVATE | /* MAP_PREFAULT | */ /* MAP_PREFAULT not in mman.h */ @@ -462,7 +449,6 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, errno = EINVAL; goto fail; } -#endif if (offset & ~TARGET_PAGE_MASK) { errno = EINVAL; From 14837a3f7540f38ba78261238da3914a6529d882 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Thu, 16 Sep 2021 18:43:01 -0600 Subject: [PATCH 0585/1334] bsd-user/mmap.c: mmap return ENOMEM on overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mmap should return ENOMEM on len overflow rather than EINVAL. Return EINVAL when len == 0 and ENOMEM when the rounded to a page length is 0. Found by make check-tcg. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/mmap.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 6f33aec58b..f0be3b12cf 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -455,11 +455,18 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, goto fail; } - len = TARGET_PAGE_ALIGN(len); if (len == 0) { errno = EINVAL; goto fail; } + + /* Check for overflows */ + len = TARGET_PAGE_ALIGN(len); + if (len == 0) { + errno = ENOMEM; + goto fail; + } + real_start = start & qemu_host_page_mask; host_offset = offset & qemu_host_page_mask; From 953b69cc06fe3ae5fa1c157f17f054fa95620f38 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Thu, 16 Sep 2021 18:45:28 -0600 Subject: [PATCH 0586/1334] bsd-user/mmap.c: mmap prefer MAP_ANON for BSD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MAP_ANON and MAP_ANONYMOUS are identical. Prefer MAP_ANON for BSD since the file is now a confusing mix of the two. Signed-off-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/mmap.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index f0be3b12cf..301108ed25 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -285,7 +285,7 @@ static abi_ulong mmap_find_vma_aligned(abi_ulong start, abi_ulong size, addr = start; wrapped = repeat = 0; prev = 0; - flags = MAP_ANONYMOUS | MAP_PRIVATE; + flags = MAP_ANON | MAP_PRIVATE; if (alignment != 0) { flags |= MAP_ALIGNED(alignment); } @@ -409,7 +409,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, if (flags & MAP_FIXED) { printf("MAP_FIXED "); } - if (flags & MAP_ANONYMOUS) { + if (flags & MAP_ANON) { printf("MAP_ANON "); } if (flags & MAP_EXCL) { @@ -431,7 +431,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, } #endif - if ((flags & MAP_ANONYMOUS) && fd != -1) { + if ((flags & MAP_ANON) && fd != -1) { errno = EINVAL; goto fail; } @@ -533,7 +533,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, * qemu_real_host_page_size */ p = mmap(g2h_untagged(start), host_len, prot, - flags | MAP_FIXED | ((fd != -1) ? MAP_ANONYMOUS : 0), -1, 0); + flags | MAP_FIXED | ((fd != -1) ? MAP_ANON : 0), -1, 0); if (p == MAP_FAILED) goto fail; /* update start so that it points to the file position at 'offset' */ @@ -696,8 +696,7 @@ static void mmap_reserve(abi_ulong start, abi_ulong size) } if (real_start != real_end) { mmap(g2h_untagged(real_start), real_end - real_start, PROT_NONE, - MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, - -1, 0); + MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); } } From 45b8765e8f3001436c09cebcd9b8b281e6c55804 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Thu, 16 Sep 2021 18:47:19 -0600 Subject: [PATCH 0587/1334] bsd-user/mmap.c: Convert to qemu_log logging for mmap debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert DEBUG_MMAP to qemu_log CPU_LOG_PAGE. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/mmap.c | 53 +++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 301108ed25..face98573f 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -21,8 +21,6 @@ #include "qemu.h" #include "qemu-common.h" -//#define DEBUG_MMAP - static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; static __thread int mmap_lock_count; @@ -67,14 +65,11 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) abi_ulong end, host_start, host_end, addr; int prot1, ret; -#ifdef DEBUG_MMAP - printf("mprotect: start=0x" TARGET_ABI_FMT_lx - "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len, - prot & PROT_READ ? 'r' : '-', - prot & PROT_WRITE ? 'w' : '-', - prot & PROT_EXEC ? 'x' : '-'); -#endif - + qemu_log_mask(CPU_LOG_PAGE, "mprotect: start=0x" TARGET_ABI_FMT_lx + " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len, + prot & PROT_READ ? 'r' : '-', + prot & PROT_WRITE ? 'w' : '-', + prot & PROT_EXEC ? 'x' : '-'); if ((start & ~TARGET_PAGE_MASK) != 0) return -EINVAL; len = TARGET_PAGE_ALIGN(len); @@ -391,45 +386,43 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; mmap_lock(); -#ifdef DEBUG_MMAP - { - printf("mmap: start=0x" TARGET_ABI_FMT_lx - " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=", - start, len, - prot & PROT_READ ? 'r' : '-', - prot & PROT_WRITE ? 'w' : '-', - prot & PROT_EXEC ? 'x' : '-'); + if (qemu_loglevel_mask(CPU_LOG_PAGE)) { + qemu_log("mmap: start=0x" TARGET_ABI_FMT_lx + " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=", + start, len, + prot & PROT_READ ? 'r' : '-', + prot & PROT_WRITE ? 'w' : '-', + prot & PROT_EXEC ? 'x' : '-'); if (flags & MAP_ALIGNMENT_MASK) { - printf("MAP_ALIGNED(%u) ", (flags & MAP_ALIGNMENT_MASK) - >> MAP_ALIGNMENT_SHIFT); + qemu_log("MAP_ALIGNED(%u) ", + (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT); } if (flags & MAP_GUARD) { - printf("MAP_GUARD "); + qemu_log("MAP_GUARD "); } if (flags & MAP_FIXED) { - printf("MAP_FIXED "); + qemu_log("MAP_FIXED "); } if (flags & MAP_ANON) { - printf("MAP_ANON "); + qemu_log("MAP_ANON "); } if (flags & MAP_EXCL) { - printf("MAP_EXCL "); + qemu_log("MAP_EXCL "); } if (flags & MAP_PRIVATE) { - printf("MAP_PRIVATE "); + qemu_log("MAP_PRIVATE "); } if (flags & MAP_SHARED) { - printf("MAP_SHARED "); + qemu_log("MAP_SHARED "); } if (flags & MAP_NOCORE) { - printf("MAP_NOCORE "); + qemu_log("MAP_NOCORE "); } if (flags & MAP_STACK) { - printf("MAP_STACK "); + qemu_log("MAP_STACK "); } - printf("fd=%d offset=0x%llx\n", fd, offset); + qemu_log("fd=%d offset=0x%lx\n", fd, offset); } -#endif if ((flags & MAP_ANON) && fd != -1) { errno = EINVAL; From a6b2d060667422d54e077c0a8e4c55bd083ef489 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 18 Oct 2021 12:51:17 -0600 Subject: [PATCH 0588/1334] bsd-user/mmap.c: Don't mmap fd == -1 independently from MAP_ANON flag Switch checks for !(flags & MAP_ANONYMOUS) with checks for fd != -1. MAP_STACK and MAP_GUARD both require fd == -1 and don't require mapping the fd either. Add analysis from Guy Yur detailing the different cases for MAP_GUARD and MAP_STACK. Signed-off-by: Guy Yur [ partially merged before, finishing the job and documenting origin] Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/mmap.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index face98573f..4ecd949a10 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -127,7 +127,27 @@ error: return ret; } -/* map an incomplete host page */ +/* + * map an incomplete host page + * + * mmap_frag can be called with a valid fd, if flags doesn't contain one of + * MAP_ANON, MAP_STACK, MAP_GUARD. If we need to map a page in those cases, we + * pass fd == -1. However, if flags contains MAP_GUARD then MAP_ANON cannot be + * added. + * + * * If fd is valid (not -1) we want to map the pages with MAP_ANON. + * * If flags contains MAP_GUARD we don't want to add MAP_ANON because it + * will be rejected. See kern_mmap's enforcing of constraints for MAP_GUARD + * in sys/vm/vm_mmap.c. + * * If flags contains MAP_ANON it doesn't matter if we add it or not. + * * If flags contains MAP_STACK, mmap adds MAP_ANON when called so doesn't + * matter if we add it or not either. See enforcing of constraints for + * MAP_STACK in kern_mmap. + * + * Don't add MAP_ANON for the flags that use fd == -1 without specifying the + * flags directly, with the assumption that future flags that require fd == -1 + * will also not require MAP_ANON. + */ static int mmap_frag(abi_ulong real_start, abi_ulong start, abi_ulong end, int prot, int flags, int fd, abi_ulong offset) @@ -147,9 +167,9 @@ static int mmap_frag(abi_ulong real_start, } if (prot1 == 0) { - /* no page was there, so we allocate one */ + /* no page was there, so we allocate one. See also above. */ void *p = mmap(host_start, qemu_host_page_size, prot, - flags | MAP_ANON, -1, 0); + flags | ((fd != -1) ? MAP_ANON : 0), -1, 0); if (p == MAP_FAILED) return -1; prot1 = prot; @@ -157,7 +177,7 @@ static int mmap_frag(abi_ulong real_start, prot1 &= PAGE_BITS; prot_new = prot | prot1; - if (!(flags & MAP_ANON)) { + if (fd != -1) { /* msync() won't work here, so we return an error if write is possible while it is a shared mapping */ if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED && @@ -565,7 +585,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, * worst case: we cannot map the file because the offset is not * aligned, so we read it */ - if (!(flags & MAP_ANON) && + if (fd != -1 && (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { /* * msync() won't work here, so we return an error if write is From 0fc76b685989d30a32316b17a9c43ba017e114a1 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 8 Nov 2018 14:39:47 -0600 Subject: [PATCH 0589/1334] bsd-user/mmap.c: Implement MAP_EXCL, required by jemalloc in head jemalloc requires a working MAP_EXCL. Ensure that no page is double mapped when specified. In addition, use guest_range_valid_untagged to test for valid ranges of pages rather than an incomplete inlined version of the test that might be wrong. Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/mmap.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 4ecd949a10..5b6ed5eed1 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -574,12 +574,10 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, * It can fail only on 64-bit host with 32-bit target. * On any other target/host host mmap() handles this error correctly. */ -#if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64 - if ((unsigned long)start + len - 1 > (abi_ulong) -1) { + if (!guest_range_valid_untagged(start, len)) { errno = EINVAL; goto fail; } -#endif /* * worst case: we cannot map the file because the offset is not @@ -614,6 +612,12 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, goto the_end; } + /* Reject the mapping if any page within the range is mapped */ + if ((flags & MAP_EXCL) && page_check_range(start, len, 0) < 0) { + errno = EINVAL; + goto fail; + } + /* handle the start of the mapping */ if (start > real_start) { if (real_end == real_start + qemu_host_page_size) { From 91a5adda1583fa8a3166bc16d79c67f3c87e958b Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Fri, 17 Sep 2021 09:16:54 -0600 Subject: [PATCH 0590/1334] bsd-user/mmap.c: assert that target_mprotect cannot fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to the equivalent linux-user change 86abac06c14. All error conditions that target_mprotect checks are also checked by target_mmap. EACCESS cannot happen because we are just removing PROT_WRITE. ENOMEM should not happen because we are modifying a whole VMA (and we have bigger problems anyway if it happens). Fixes a Coverity false positive, where Coverity complains about target_mprotect's return value being passed to tb_invalidate_phys_range. Signed-off-by: Mikaël Urankar Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/mmap.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 5b6ed5eed1..13cb32dba1 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -604,10 +604,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, } if (!(prot & PROT_WRITE)) { ret = target_mprotect(start, len, prot); - if (ret != 0) { - start = ret; - goto the_end; - } + assert(ret == 0); } goto the_end; } From dda2da6c94484b85d28fe7c29f7fee562deaf177 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Fri, 8 Oct 2021 16:47:37 -0600 Subject: [PATCH 0591/1334] meson: *-user: only descend into *-user when configured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To increase flexibility, only descend into *-user when that is configured. This allows *-user to selectively include directories based on the host OS which may not exist on all hosts. Adopt Paolo's suggestion of checking the configuration in the directories that know about the configuration. Message-Id: <20210926220103.1721355-2-f4bug@amsat.org> Message-Id: <20210926220103.1721355-3-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Warner Losh Acked-by: Paolo Bonzini Reviewed-by: Kyle Evans --- bsd-user/meson.build | 4 ++++ linux-user/meson.build | 4 ++++ meson.build | 12 ++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/bsd-user/meson.build b/bsd-user/meson.build index 0369549340..5378f56f71 100644 --- a/bsd-user/meson.build +++ b/bsd-user/meson.build @@ -1,3 +1,7 @@ +if not have_bsd_user + subdir_done() +endif + bsd_user_ss.add(files( 'bsdload.c', 'elfload.c', diff --git a/linux-user/meson.build b/linux-user/meson.build index 9549f81682..bf62c13e37 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -1,3 +1,7 @@ +if not have_linux_user + subdir_done() +endif + linux_user_ss.add(files( 'elfload.c', 'exit.c', diff --git a/meson.build b/meson.build index 6b7487b725..5e7946776d 100644 --- a/meson.build +++ b/meson.build @@ -40,12 +40,15 @@ config_host_data = configuration_data() genh = [] target_dirs = config_host['TARGET_DIRS'].split() -have_user = false +have_linux_user = false +have_bsd_user = false have_system = false foreach target : target_dirs - have_user = have_user or target.endswith('-user') + have_linux_user = have_linux_user or target.endswith('linux-user') + have_bsd_user = have_bsd_user or target.endswith('bsd-user') have_system = have_system or target.endswith('-softmmu') endforeach +have_user = have_linux_user or have_bsd_user have_tools = 'CONFIG_TOOLS' in config_host have_block = have_system or have_tools @@ -2595,10 +2598,11 @@ subdir('bsd-user') subdir('linux-user') subdir('ebpf') -bsd_user_ss.add(files('gdbstub.c')) +common_ss.add(libbpf) + specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss) -linux_user_ss.add(files('gdbstub.c', 'thunk.c')) +linux_user_ss.add(files('thunk.c')) specific_ss.add_all(when: 'CONFIG_LINUX_USER', if_true: linux_user_ss) # needed for fuzzing binaries From 1fecb605f83f4a5db315cd183b6f4e30fc72518d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 19 Sep 2021 00:17:40 -0600 Subject: [PATCH 0592/1334] bsd-user/target_os-user.h: Remove support for FreeBSD older than 12.0 Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/freebsd/target_os_user.h | 100 +----------------------------- 1 file changed, 1 insertion(+), 99 deletions(-) diff --git a/bsd-user/freebsd/target_os_user.h b/bsd-user/freebsd/target_os_user.h index 95b1fa9f99..19892c5071 100644 --- a/bsd-user/freebsd/target_os_user.h +++ b/bsd-user/freebsd/target_os_user.h @@ -61,15 +61,7 @@ struct target_sockaddr_storage { /* * from sys/user.h */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 #define TARGET_KI_NSPARE_INT 2 -#elif defined(__FreeBSD_version) && __FreeBSD_version >= 1100000 -#define TARGET_KI_NSPARE_INT 4 -#elif defined(__FreeBSD_version) && __FreeBSD_version >= 1000000 -#define TARGET_KI_NSPARE_INT 7 -#else -#define TARGET_KI_NSPARE_INT 9 -#endif /* ! __FreeBSD_version >= 1000000 */ #define TARGET_KI_NSPARE_LONG 12 #define TARGET_KI_NSPARE_PTR 6 @@ -116,11 +108,7 @@ struct target_kinfo_proc { int32_t ki_tsid; /* Terminal session ID */ int16_t ki_jobc; /* job control counter */ int16_t ki_spare_short1; /* unused (just here for alignment) */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 int32_t ki_tdev__freebsd11; /* controlling tty dev */ -#else - int32_t ki_tdev; /* controlling tty dev */ -#endif target_sigset_t ki_siglist; /* Signals arrived but not delivered */ target_sigset_t ki_sigmask; /* Current signal mask */ target_sigset_t ki_sigignore; /* Signals being ignored */ @@ -164,45 +152,24 @@ struct target_kinfo_proc { int8_t ki_nice; /* Process "nice" value */ char ki_lock; /* Process lock (prevent swap) count */ char ki_rqindex; /* Run queue index */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000 u_char ki_oncpu_old; /* Which cpu we are on (legacy) */ u_char ki_lastcpu_old; /* Last cpu we were on (legacy) */ -#else - u_char ki_oncpu; /* Which cpu we are on */ - u_char ki_lastcpu; /* Last cpu we were on */ -#endif /* ! __FreeBSD_version >= 1100000 */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 900000 char ki_tdname[TARGET_TDNAMLEN + 1]; /* thread name */ -#else - char ki_ocomm[TARGET_TDNAMLEN + 1]; /* thread name */ -#endif /* ! __FreeBSD_version >= 900000 */ char ki_wmesg[TARGET_WMESGLEN + 1]; /* wchan message */ char ki_login[TARGET_LOGNAMELEN + 1]; /* setlogin name */ char ki_lockname[TARGET_LOCKNAMELEN + 1]; /* lock name */ char ki_comm[TARGET_COMMLEN + 1]; /* command name */ char ki_emul[TARGET_KI_EMULNAMELEN + 1]; /* emulation name */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 900000 char ki_loginclass[TARGET_LOGINCLASSLEN + 1]; /* login class */ -#endif /* ! __FreeBSD_version >= 900000 */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 900000 char ki_sparestrings[50]; /* spare string space */ -#else - char ki_sparestrings[68]; /* spare string space */ -#endif /* ! __FreeBSD_version >= 900000 */ int32_t ki_spareints[TARGET_KI_NSPARE_INT]; /* spare room for growth */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 - uint64_t ki_tdev; /* controlling tty dev */ -#endif -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000 + uint64_t ki_tdev; /* controlling tty dev */ int32_t ki_oncpu; /* Which cpu we are on */ int32_t ki_lastcpu; /* Last cpu we were on */ int32_t ki_tracer; /* Pid of tracing process */ -#endif /* __FreeBSD_version >= 1100000 */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 900000 int32_t ki_flag2; /* P2_* flags */ int32_t ki_fibnum; /* Default FIB number */ -#endif /* ! __FreeBSD_version >= 900000 */ uint32_t ki_cr_flags; /* Credential flags */ int32_t ki_jid; /* Process jail ID */ int32_t ki_numthreads; /* XXXKSE number of threads in total */ @@ -234,18 +201,8 @@ struct target_kinfo_file { int32_t kf_flags; /* Flags. */ int32_t kf_pad0; /* Round to 64 bit alignment. */ int64_t kf_offset; /* Seek location. */ -#if defined(__FreeBSD_version) && __FreeBSD_version < 1200031 - int32_t kf_vnode_type; /* Vnode type. */ - int32_t kf_sock_domain; /* Socket domain. */ - int32_t kf_sock_type; /* Socket type. */ - int32_t kf_sock_protocol; /* Socket protocol. */ - struct target_sockaddr_storage kf_sa_local; /* Socket address. */ - struct target_sockaddr_storage kf_sa_peer; /* Peer address. */ -#endif -#if defined(__FreeBSD_version) && __FreeBSD_version >= 900000 union { struct { -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 uint32_t kf_spareint; /* Socket domain. */ int kf_sock_domain0; @@ -257,7 +214,6 @@ struct target_kinfo_file { struct sockaddr_storage kf_sa_local; /* Peer address. */ struct sockaddr_storage kf_sa_peer; -#endif /* Address of so_pcb. */ uint64_t kf_sock_pcb; /* Address of inp_ppcb. */ @@ -272,7 +228,6 @@ struct target_kinfo_file { uint32_t kf_sock_pad0; } kf_sock; struct { -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 /* Vnode type. */ int kf_file_type; /* Space for future use */ @@ -290,16 +245,6 @@ struct target_kinfo_file { uint32_t kf_file_fsid_freebsd11; /* File device, FreeBSD 11 compat. */ uint32_t kf_file_rdev_freebsd11; -#else - /* Global file id. */ - uint64_t kf_file_fileid; - /* File size. */ - uint64_t kf_file_size; - /* Vnode filesystem id. */ - uint32_t kf_file_fsid; - /* File device. */ - uint32_t kf_file_rdev; -#endif /* File mode. */ uint16_t kf_file_mode; /* Round to 64 bit alignment. */ @@ -307,18 +252,14 @@ struct target_kinfo_file { uint32_t kf_file_pad1; } kf_file; struct { -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 uint32_t kf_spareint[4]; uint64_t kf_spareint64[32]; -#endif uint32_t kf_sem_value; uint16_t kf_sem_mode; } kf_sem; struct { -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 uint32_t kf_spareint[4]; uint64_t kf_spareint64[32]; -#endif uint64_t kf_pipe_addr; uint64_t kf_pipe_peer; uint32_t kf_pipe_buffer_cnt; @@ -326,7 +267,6 @@ struct target_kinfo_file { uint32_t kf_pipe_pad0[3]; } kf_pipe; struct { -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 uint32_t kf_spareint[4]; uint64_t kf_spareint64[32]; uint32_t kf_pts_dev_freebsd11; @@ -334,34 +274,18 @@ struct target_kinfo_file { uint64_t kf_pts_dev; /* Round to 64 bit alignment. */ uint32_t kf_pts_pad1[4]; -#else - uint32_t kf_pts_dev; - /* Round to 64 bit alignment. */ - uint32_t kf_pts_pad0[7]; -#endif } kf_pts; struct { -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 uint32_t kf_spareint[4]; uint64_t kf_spareint64[32]; -#endif int32_t kf_pid; } kf_proc; } kf_un; uint16_t kf_status; /* Status flags. */ uint16_t kf_pad1; /* Round to 32 bit alignment. */ int32_t _kf_ispare0; /* Space for more stuff. */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1000000 target_cap_rights_t kf_cap_rights; /* Capability rights. */ uint64_t _kf_cap_spare; /* Space for future cap_rights_t. */ -#else /* ! __FreeBSD_version >= 1000000 */ - uint64_t kf_cap_rights; - int _kf_ispare[4]; -#endif /* ! __FreeBSD_version >= 1000000 */ - -#else /* ! __FreeBSD_version >= 900000 */ - int _kf_ispare[16]; -#endif /* ! __FreeBSD_version >= 900000 */ /* Truncated before copyout in sysctl */ char kf_path[PATH_MAX]; /* Path to file, if any. */ }; @@ -372,34 +296,19 @@ struct target_kinfo_vmentry { uint64_t kve_start; /* Starting address. */ uint64_t kve_end; /* Finishing address. */ uint64_t kve_offset; /* Mapping offset in object */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 900000 uint64_t kve_vn_fileid; /* inode number if vnode */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 uint32_t kve_vn_fsid_freebsd11; /* dev_t of vnode location */ -#else - uint32_t kve_vn_fsid; /* dev_t of vnode location */ -#endif -#else /* ! __FreeBSD_version >= 900000 */ - uint64_t kve_fileid; /* inode number if vnode */ - uint32_t kve_fsid; /* dev_t of vnode location */ -#endif /* ! __FreeBSD_version >= 900000 */ int32_t kve_flags; /* Flags on map entry. */ int32_t kve_resident; /* Number of resident pages. */ int32_t kve_private_resident; /* Number of private pages. */ int32_t kve_protection; /* Protection bitmask. */ int32_t kve_ref_count; /* VM obj ref count. */ int32_t kve_shadow_count; /* VM obj shadow count. */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 900000 int32_t kve_vn_type; /* Vnode type. */ uint64_t kve_vn_size; /* File size. */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 uint32_t kve_vn_rdev_freebsd11; /* Device id if device. */ -#else - uint32_t kve_vn_rdev; /* Device id if device. */ -#endif uint16_t kve_vn_mode; /* File mode. */ uint16_t kve_status; /* Status flags. */ -#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 #if (__FreeBSD_version >= 1300501 && __FreeBSD_version < 1400000) || \ __FreeBSD_version >= 1400009 union { @@ -413,13 +322,6 @@ struct target_kinfo_vmentry { #endif uint64_t kve_vn_rdev; /* Device id if device. */ int _kve_ispare[8]; /* Space for more stuff. */ -#else - int32_t _kve_ispare[12]; /* Space for more stuff. */ -#endif -#else /* ! __FreeBSD_version >= 900000 */ - int _kve_pad0; - int32_t _kve_ispare[16]; /* Space for more stuff. */ -#endif /* ! __FreeBSD_version >= 900000 */ /* Truncated before copyout in sysctl */ char kve_path[PATH_MAX]; /* Path to VM obj, if any. */ }; From b03c0bb27aa513869fbdbff941b92e5aa3604bd0 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 19 Sep 2021 00:15:37 -0600 Subject: [PATCH 0593/1334] bsd-user/strace.list: Remove support for FreeBSD versions older than 12.0 Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/freebsd/strace.list | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/bsd-user/freebsd/strace.list b/bsd-user/freebsd/strace.list index b01b5f36e8..275d2dbe27 100644 --- a/bsd-user/freebsd/strace.list +++ b/bsd-user/freebsd/strace.list @@ -33,10 +33,6 @@ { TARGET_FREEBSD_NR___syscall, "__syscall", NULL, NULL, NULL }, { TARGET_FREEBSD_NR___sysctl, "__sysctl", NULL, print_sysctl, NULL }, { TARGET_FREEBSD_NR__umtx_op, "_umtx_op", "%s(%#x, %d, %d, %#x, %#x)", NULL, NULL }, -#if defined(__FreeBSD_version) && __FreeBSD_version < 1000000 -{ TARGET_FREEBSD_NR__umtx_lock, "__umtx_lock", NULL, NULL, NULL }, -{ TARGET_FREEBSD_NR__umtx_unlock, "__umtx_unlock", NULL, NULL, NULL }, -#endif { TARGET_FREEBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_accept4, "accept4", "%s(%d,%d,%#x,%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL }, @@ -49,10 +45,6 @@ { TARGET_FREEBSD_NR_cap_fcntls_get, "cap_fcntls_get", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_cap_fcntls_limit, "cap_fcntls_limit", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_cap_getmode, "cap_getmode", NULL, NULL, NULL }, -#if defined(__FreeBSD_version) && __FreeBSD_version < 1000000 -{ TARGET_FREEBSD_NR_cap_getrights, "cap_getrights", NULL, NULL, NULL }, -{ TARGET_FREEBSD_NR_cap_new, "cap_new", NULL, NULL, NULL }, -#endif { TARGET_FREEBSD_NR_cap_ioctls_get, "cap_ioctls_get", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_cap_ioctls_limit, "cap_ioctls_limit", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_cap_rights_limit, "cap_rights_limit", NULL, NULL, NULL }, @@ -146,9 +138,6 @@ { TARGET_FREEBSD_NR_freebsd11_kevent, "freebsd11_kevent", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_kevent, "kevent", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_kill, "kill", NULL, NULL, NULL }, -#if defined(__FreeBSD_version) && __FreeBSD_version < 1000000 -{ TARGET_FREEBSD_NR_killpg, "killpg", NULL, NULL, NULL }, -#endif { TARGET_FREEBSD_NR_kqueue, "kqueue", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_ktrace, "ktrace", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_lchown, "lchown", NULL, NULL, NULL }, From fbbacc99821781f4d0faa0e714c4885d9570faf4 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Tue, 21 Sep 2021 18:59:05 -0600 Subject: [PATCH 0594/1334] bsd-user: TARGET_RESET define is unused, remove it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/i386/target_arch_cpu.h | 2 -- bsd-user/x86_64/target_arch_cpu.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index 978e8066af..b28602adbb 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -23,8 +23,6 @@ #define TARGET_DEFAULT_CPU_MODEL "qemu32" -#define TARGET_CPU_RESET(cpu) - static inline void target_cpu_init(CPUX86State *env, struct target_pt_regs *regs) { diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index 5f5ee602f9..5172b230f0 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -23,8 +23,6 @@ #define TARGET_DEFAULT_CPU_MODEL "qemu64" -#define TARGET_CPU_RESET(cpu) - static inline void target_cpu_init(CPUX86State *env, struct target_pt_regs *regs) { From e5f674f01cdfbb7b98374a947aed84412f4c763f Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 18 Sep 2021 00:26:49 -0600 Subject: [PATCH 0595/1334] bsd-user: export get_errno and is_error from syscall.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make get_errno and is_error global so files other than syscall.c can use them. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/qemu.h | 4 ++++ bsd-user/syscall.c | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 522d6c4031..3b8475394c 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -235,6 +235,10 @@ extern unsigned long target_dflssiz; extern unsigned long target_maxssiz; extern unsigned long target_sgrowsiz; +/* syscall.c */ +abi_long get_errno(abi_long ret); +bool is_error(abi_long ret); + /* user access */ #define VERIFY_READ PAGE_READ diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c index 372836d44d..2fd2ba8330 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -33,18 +33,18 @@ static abi_ulong target_brk; static abi_ulong target_original_brk; -static inline abi_long get_errno(abi_long ret) +abi_long get_errno(abi_long ret) { - if (ret == -1) + if (ret == -1) { /* XXX need to translate host -> target errnos here */ return -(errno); - else - return ret; + } + return ret; } #define target_to_host_bitmask(x, tbl) (x) -static inline int is_error(abi_long ret) +bool is_error(abi_long ret) { return (abi_ulong)ret >= (abi_ulong)(-4096); } From 7cb4d7c917c2718f9fd5a075c2e4b4fca3be482a Mon Sep 17 00:00:00 2001 From: Stacey Son Date: Sat, 18 Sep 2021 09:27:47 -0600 Subject: [PATCH 0596/1334] bsd-user/errno_defs.h: Add internal error numbers To emulate signals and interrupted system calls, we need to have the same mechanisms we have in the kernel, including these errno values. Signed-off-by: Stacey Son Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/errno_defs.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/bsd-user/errno_defs.h b/bsd-user/errno_defs.h index 1efa502a12..832671354f 100644 --- a/bsd-user/errno_defs.h +++ b/bsd-user/errno_defs.h @@ -1,6 +1,3 @@ -/* $OpenBSD: errno.h,v 1.20 2007/09/03 14:37:52 millert Exp $ */ -/* $NetBSD: errno.h,v 1.10 1996/01/20 01:33:53 jtc Exp $ */ - /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -37,6 +34,9 @@ * @(#)errno.h 8.5 (Berkeley) 1/21/94 */ +#ifndef _ERRNO_DEFS_H_ +#define _ERRNO_DEFS_H_ + #define TARGET_EPERM 1 /* Operation not permitted */ #define TARGET_ENOENT 2 /* No such file or directory */ #define TARGET_ESRCH 3 /* No such process */ @@ -147,3 +147,10 @@ #define TARGET_EIDRM 89 /* Identifier removed */ #define TARGET_ENOMSG 90 /* No message of desired type */ #define TARGET_ELAST 90 /* Must be equal largest errno */ + +/* Internal errors: */ +#define TARGET_EJUSTRETURN 254 /* Just return without modifing regs */ +#define TARGET_ERESTART 255 /* Restart syscall */ +#define TARGET_ERESTARTSYS TARGET_ERESTART /* Linux compat */ + +#endif /* ! _ERRNO_DEFS_H_ */ From f4a29b6ed29360cf556851c82875a2b782adb5ed Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Tue, 21 Sep 2021 17:40:23 -0600 Subject: [PATCH 0597/1334] bsd-user: move TARGET_MC_GET_CLEAR_RET to target_os_signal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move TARGET_MC_GET_CLEAR_RET to freebsd/target_os_signal.h since it's architecture agnostic on FreeBSD. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/freebsd/target_os_signal.h | 3 +++ bsd-user/i386/target_arch_signal.h | 2 -- bsd-user/x86_64/target_arch_signal.h | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bsd-user/freebsd/target_os_signal.h b/bsd-user/freebsd/target_os_signal.h index 3ed454e086..1a4c5faf19 100644 --- a/bsd-user/freebsd/target_os_signal.h +++ b/bsd-user/freebsd/target_os_signal.h @@ -1,6 +1,9 @@ #ifndef _TARGET_OS_SIGNAL_H_ #define _TARGET_OS_SIGNAL_H_ +/* FreeBSD's sys/ucontext.h defines this */ +#define TARGET_MC_GET_CLEAR_RET 0x0001 + #include "target_os_siginfo.h" #include "target_arch_signal.h" diff --git a/bsd-user/i386/target_arch_signal.h b/bsd-user/i386/target_arch_signal.h index 9812c6b034..a90750d602 100644 --- a/bsd-user/i386/target_arch_signal.h +++ b/bsd-user/i386/target_arch_signal.h @@ -27,8 +27,6 @@ #define TARGET_MINSIGSTKSZ (512 * 4) /* min sig stack size */ #define TARGET_SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended size */ -#define TARGET_MC_GET_CLEAR_RET 0x0001 - struct target_sigcontext { /* to be added */ }; diff --git a/bsd-user/x86_64/target_arch_signal.h b/bsd-user/x86_64/target_arch_signal.h index 4c1ff0e5ba..4bb753b08b 100644 --- a/bsd-user/x86_64/target_arch_signal.h +++ b/bsd-user/x86_64/target_arch_signal.h @@ -27,8 +27,6 @@ #define TARGET_MINSIGSTKSZ (512 * 4) /* min sig stack size */ #define TARGET_SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended size */ -#define TARGET_MC_GET_CLEAR_RET 0x0001 - struct target_sigcontext { /* to be added */ }; From 11170cbdcc64e4207850eb886baeb6db436aaf46 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 18 Sep 2021 09:35:43 -0600 Subject: [PATCH 0598/1334] bsd-user/target_os_elf.h: Remove fallback ELF_HWCAP and reorder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All architectures have a ELF_HWCAP, so remove the fallback ifdef. Place ELF_HWCAP in the same order as on native FreeBSD. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/freebsd/target_os_elf.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/bsd-user/freebsd/target_os_elf.h b/bsd-user/freebsd/target_os_elf.h index 2d03a883aa..adcffd1ddb 100644 --- a/bsd-user/freebsd/target_os_elf.h +++ b/bsd-user/freebsd/target_os_elf.h @@ -38,10 +38,6 @@ #define ELF_PLATFORM (NULL) #endif -#ifndef ELF_HWCAP -#define ELF_HWCAP 0 -#endif - /* XXX Look at the other conflicting AT_* values. */ #define FREEBSD_AT_NCPUS 19 #define FREEBSD_AT_HWCAP 25 @@ -114,12 +110,12 @@ static abi_ulong target_create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0); NEW_AUX_ENT(FREEBSD_AT_NCPUS, (abi_ulong)bsd_get_ncpu()); NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry); + features = ELF_HWCAP; + NEW_AUX_ENT(FREEBSD_AT_HWCAP, features); NEW_AUX_ENT(AT_UID, (abi_ulong)getuid()); NEW_AUX_ENT(AT_EUID, (abi_ulong)geteuid()); NEW_AUX_ENT(AT_GID, (abi_ulong)getgid()); NEW_AUX_ENT(AT_EGID, (abi_ulong)getegid()); - features = ELF_HWCAP; - NEW_AUX_ENT(FREEBSD_AT_HWCAP, features); target_auxents = sp; /* Note where the aux entries are in the target */ #ifdef ARCH_DLINFO /* From f6f0706cc2081ebaf786603aaaf204ae5a39dda7 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 18 Sep 2021 09:38:11 -0600 Subject: [PATCH 0599/1334] bsd-user/target_os_elf: If ELF_HWCAP2 is defined, publish it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some architectures publish AT_HWCAP2 as well as AT_HWCAP. Those architectures will define ELF_HWCAP2 in their target_arch_elf.h files for the value for this process. If it is defined, then publish it. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/freebsd/target_os_elf.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bsd-user/freebsd/target_os_elf.h b/bsd-user/freebsd/target_os_elf.h index adcffd1ddb..e5ac8e8e50 100644 --- a/bsd-user/freebsd/target_os_elf.h +++ b/bsd-user/freebsd/target_os_elf.h @@ -112,6 +112,10 @@ static abi_ulong target_create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry); features = ELF_HWCAP; NEW_AUX_ENT(FREEBSD_AT_HWCAP, features); +#ifdef ELF_HWCAP2 + features = ELF_HWCAP2; + NEW_AUX_ENT(FREEBSD_AT_HWCAP2, features); +#endif NEW_AUX_ENT(AT_UID, (abi_ulong)getuid()); NEW_AUX_ENT(AT_EUID, (abi_ulong)geteuid()); NEW_AUX_ENT(AT_GID, (abi_ulong)getgid()); From 7aac7392346d1f5bb9fc31cb7e4f3f1f3fc2be05 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 18 Sep 2021 22:03:43 -0600 Subject: [PATCH 0600/1334] bsd-user: Remove used from TaskState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'used' field in TaskState is write only. Remove it from TaskState. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/main.c | 1 - bsd-user/qemu.h | 1 - 2 files changed, 2 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 48643eeabc..ee84554854 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -210,7 +210,6 @@ void init_task_state(TaskState *ts) { int i; - ts->used = 1; ts->first_free = ts->sigqueue_table; for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) { ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1]; diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 3b8475394c..c1170f14d9 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -92,7 +92,6 @@ typedef struct TaskState { struct TaskState *next; struct bsd_binprm *bprm; - int used; /* non zero if used */ struct image_info *info; struct emulated_sigtable sigtab[TARGET_NSIG]; From 653ccec26dd3f9942ac258c43be0edb93e16dfba Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 19 Sep 2021 01:11:43 -0600 Subject: [PATCH 0601/1334] bsd-user: Add stop_all_tasks Similar to the same function in linux-user: this stops all the current tasks. Signed-off-by: Stacey Son Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/main.c | 9 +++++++++ bsd-user/qemu.h | 1 + 2 files changed, 10 insertions(+) diff --git a/bsd-user/main.c b/bsd-user/main.c index ee84554854..cb5ea40236 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -195,6 +195,15 @@ static void usage(void) __thread CPUState *thread_cpu; +void stop_all_tasks(void) +{ + /* + * We trust when using NPTL (pthreads) start_exclusive() handles thread + * stopping correctly. + */ + start_exclusive(); +} + bool qemu_cpu_is_self(CPUState *cpu) { return thread_cpu == cpu; diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index c1170f14d9..cdb85140f4 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -103,6 +103,7 @@ typedef struct TaskState { } __attribute__((aligned(16))) TaskState; void init_task_state(TaskState *ts); +void stop_all_tasks(void); extern const char *qemu_uname_release; /* From da07e6944fb0f1fe162246cbf31271f31ec9a5c0 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 20 Sep 2021 13:41:38 -0600 Subject: [PATCH 0602/1334] bsd-user/sysarch: Move to using do_freebsd_arch_sysarch interface do_freebsd_arch_sysarch() exists in $ARCH/target_arch_sysarch.h for x86. Call it from do_freebsd_sysarch() and remove the mostly duplicate version in syscall.c. Future changes will move it to os-sys.c and support other architectures. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/freebsd/meson.build | 3 +++ bsd-user/freebsd/os-sys.c | 27 +++++++++++++++++++ bsd-user/meson.build | 3 +++ bsd-user/qemu.h | 3 +++ bsd-user/syscall.c | 50 ------------------------------------ 5 files changed, 36 insertions(+), 50 deletions(-) create mode 100644 bsd-user/freebsd/meson.build create mode 100644 bsd-user/freebsd/os-sys.c diff --git a/bsd-user/freebsd/meson.build b/bsd-user/freebsd/meson.build new file mode 100644 index 0000000000..4b69cca7b9 --- /dev/null +++ b/bsd-user/freebsd/meson.build @@ -0,0 +1,3 @@ +bsd_user_ss.add(files( + 'os-sys.c', +)) diff --git a/bsd-user/freebsd/os-sys.c b/bsd-user/freebsd/os-sys.c new file mode 100644 index 0000000000..309e27b9d6 --- /dev/null +++ b/bsd-user/freebsd/os-sys.c @@ -0,0 +1,27 @@ +/* + * FreeBSD sysctl() and sysarch() system call emulation + * + * Copyright (c) 2013-15 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu.h" +#include "target_arch_sysarch.h" + +/* sysarch() is architecture dependent. */ +abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2) +{ + return do_freebsd_arch_sysarch(cpu_env, arg1, arg2); +} diff --git a/bsd-user/meson.build b/bsd-user/meson.build index 5378f56f71..87885d91ed 100644 --- a/bsd-user/meson.build +++ b/bsd-user/meson.build @@ -12,3 +12,6 @@ bsd_user_ss.add(files( 'syscall.c', 'uaccess.c', )) + +# Pull in the OS-specific build glue, if any +subdir(targetos) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index cdb85140f4..e65e41d53d 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -239,6 +239,9 @@ extern unsigned long target_sgrowsiz; abi_long get_errno(abi_long ret); bool is_error(abi_long ret); +/* os-sys.c */ +abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2); + /* user access */ #define VERIFY_READ PAGE_READ diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c index 2fd2ba8330..d3322760f4 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -88,56 +88,6 @@ static abi_long do_obreak(abi_ulong new_brk) return 0; } -#if defined(TARGET_I386) -static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms) -{ - abi_long ret = 0; - abi_ulong val; - int idx; - - switch (op) { -#ifdef TARGET_ABI32 - case TARGET_FREEBSD_I386_SET_GSBASE: - case TARGET_FREEBSD_I386_SET_FSBASE: - if (op == TARGET_FREEBSD_I386_SET_GSBASE) -#else - case TARGET_FREEBSD_AMD64_SET_GSBASE: - case TARGET_FREEBSD_AMD64_SET_FSBASE: - if (op == TARGET_FREEBSD_AMD64_SET_GSBASE) -#endif - idx = R_GS; - else - idx = R_FS; - if (get_user(val, parms, abi_ulong)) - return -TARGET_EFAULT; - cpu_x86_load_seg(env, idx, 0); - env->segs[idx].base = val; - break; -#ifdef TARGET_ABI32 - case TARGET_FREEBSD_I386_GET_GSBASE: - case TARGET_FREEBSD_I386_GET_FSBASE: - if (op == TARGET_FREEBSD_I386_GET_GSBASE) -#else - case TARGET_FREEBSD_AMD64_GET_GSBASE: - case TARGET_FREEBSD_AMD64_GET_FSBASE: - if (op == TARGET_FREEBSD_AMD64_GET_GSBASE) -#endif - idx = R_GS; - else - idx = R_FS; - val = env->segs[idx].base; - if (put_user(val, parms, abi_ulong)) - return -TARGET_EFAULT; - break; - /* XXX handle the others... */ - default: - ret = -TARGET_EINVAL; - break; - } - return ret; -} -#endif - #ifdef __FreeBSD__ /* * XXX this uses the undocumented oidfmt interface to find the kind of From 11c7b43faa1d19c94524984c878479d14a7194f6 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 20 Sep 2021 13:56:06 -0600 Subject: [PATCH 0603/1334] bsd-user: Rename sigqueue to qemu_sigqueue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid a name clash with FreeBSD's sigqueue data structure in signalvar.h, rename sigqueue to qemu_sigqueue. This structure is currently defined, but unused. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kyle Evans --- bsd-user/qemu.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index e65e41d53d..ba15b1b56f 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -73,15 +73,15 @@ struct image_info { #define MAX_SIGQUEUE_SIZE 1024 -struct sigqueue { - struct sigqueue *next; +struct qemu_sigqueue { + struct qemu_sigqueue *next; + target_siginfo_t info; }; struct emulated_sigtable { int pending; /* true if signal is pending */ - struct sigqueue *first; - /* in order to always have memory for the first signal, we put it here */ - struct sigqueue info; + struct qemu_sigqueue *first; + struct qemu_sigqueue info; /* Put first signal info here */ }; /* @@ -95,8 +95,8 @@ typedef struct TaskState { struct image_info *info; struct emulated_sigtable sigtab[TARGET_NSIG]; - struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ - struct sigqueue *first_free; /* first free siginfo queue entry */ + struct qemu_sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ + struct qemu_sigqueue *first_free; /* first free siginfo queue entry */ int signal_pending; /* non zero if a signal may be pending */ uint8_t stack[]; From 5abfac277d25feb5f12332422c03ea1cb21c6aa1 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Tue, 21 Sep 2021 16:20:52 -0600 Subject: [PATCH 0604/1334] bsd-user/signal: Create a dummy signal queueing function Create dummy signal queueing function so we can start to integrate other architectures (at the cost of signals remaining broken) to tame the dependency graph a bit and to bring in signals in a more controlled fashion. Log unimplemented events to it in the mean time. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson Reviewed-by: Kyle Evans --- bsd-user/qemu.h | 2 +- bsd-user/signal.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index ba15b1b56f..1b3b974afe 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -17,7 +17,6 @@ #ifndef QEMU_H #define QEMU_H - #include "qemu/osdep.h" #include "cpu.h" #include "qemu/units.h" @@ -209,6 +208,7 @@ void process_pending_signals(CPUArchState *cpu_env); void signal_init(void); long do_sigreturn(CPUArchState *env); long do_rt_sigreturn(CPUArchState *env); +void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); /* mmap.c */ diff --git a/bsd-user/signal.c b/bsd-user/signal.c index ad6d935569..0c1093deb1 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -16,10 +16,19 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#include "qemu/osdep.h" +#include "qemu/osdep.h" #include "qemu.h" +/* + * Queue a signal so that it will be send to the virtual CPU as soon as + * possible. + */ +void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) +{ + qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); +} + void signal_init(void) { } From 20171ea8950c619f00dc5cfa6136fd489998ffc5 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Thu, 9 Sep 2021 09:18:08 +0200 Subject: [PATCH 0605/1334] multifd: Implement yank for multifd send side To: qemu-devel Cc: "Dr. David Alan Gilbert" , Juan Quintela , Peter Xu , Leonardo Bras Soares Passos Date: Wed, 1 Sep 2021 17:58:57 +0200 (1 week, 15 hours, 17 minutes ago) [[PGP Signed Part:No public key for 35AB0B289C5DB258 created at 2021-09-01T17:58:57+0200 using RSA]] When introducing yank functionality in the migration code I forgot to cover the multifd send side. Signed-off-by: Lukas Straub Tested-by: Leonardo Bras Reviewed-by: Leonardo Bras Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/multifd.c | 6 +++++- migration/multifd.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/migration/multifd.c b/migration/multifd.c index 377da78f5b..5a4f158f3c 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -546,6 +546,9 @@ void multifd_save_cleanup(void) MultiFDSendParams *p = &multifd_send_state->params[i]; Error *local_err = NULL; + if (p->registered_yank) { + migration_ioc_unregister_yank(p->c); + } socket_send_channel_destroy(p->c); p->c = NULL; qemu_mutex_destroy(&p->mutex); @@ -813,7 +816,8 @@ static bool multifd_channel_connect(MultiFDSendParams *p, return false; } } else { - /* update for tls qio channel */ + migration_ioc_register_yank(ioc); + p->registered_yank = true; p->c = ioc; qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, QEMU_THREAD_JOINABLE); diff --git a/migration/multifd.h b/migration/multifd.h index 8d6751f5ed..16c4d112d1 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -85,6 +85,8 @@ typedef struct { bool running; /* should this thread finish */ bool quit; + /* is the yank function registered */ + bool registered_yank; /* thread has work to do */ int pending_job; /* array of pages to sent */ From e9ab82b858c14aa76dac5235a99d1723ec069407 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Thu, 9 Sep 2021 09:19:45 +0200 Subject: [PATCH 0606/1334] multifd: Unconditionally unregister yank function To: qemu-devel Cc: "Dr. David Alan Gilbert" , Juan Quintela , Peter Xu , Leonardo Bras Soares Passos Date: Wed, 4 Aug 2021 21:26:32 +0200 (5 weeks, 11 hours, 52 minutes ago) [[PGP Signed Part:No public key for 35AB0B289C5DB258 created at 2021-08-04T21:26:32+0200 using RSA]] Unconditionally unregister yank function in multifd_load_cleanup(). If it is not unregistered here, it will leak and cause a crash in yank_unregister_instance(). Now if the ioc is still in use afterwards, it will only lead to qemu not being able to recover from a hang related to that ioc. After checking the code, i am pretty sure that ref is always 1 when arriving here. So all this currently does is remove the unneeded check. Signed-off-by: Lukas Straub Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/multifd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 5a4f158f3c..efd424bc97 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -991,10 +991,7 @@ int multifd_load_cleanup(Error **errp) for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; - if (OBJECT(p->c)->ref == 1) { - migration_ioc_unregister_yank(p->c); - } - + migration_ioc_unregister_yank(p->c); object_unref(OBJECT(p->c)); p->c = NULL; qemu_mutex_destroy(&p->mutex); From 1230a25f6fa3048c56d0f7f4e70e451330f4d33c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Sat, 4 Sep 2021 18:09:07 +0200 Subject: [PATCH 0607/1334] migration/ram: Don't passs RAMState to migration_clear_memory_region_dirty_bitmap_*() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parameter is unused, let's drop it. Reviewed-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Signed-off-by: David Hildenbrand Signed-off-by: Juan Quintela --- migration/ram.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 7a43bfd7af..bb908822d5 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -789,8 +789,7 @@ unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, return find_next_bit(bitmap, size, start); } -static void migration_clear_memory_region_dirty_bitmap(RAMState *rs, - RAMBlock *rb, +static void migration_clear_memory_region_dirty_bitmap(RAMBlock *rb, unsigned long page) { uint8_t shift; @@ -818,8 +817,7 @@ static void migration_clear_memory_region_dirty_bitmap(RAMState *rs, } static void -migration_clear_memory_region_dirty_bitmap_range(RAMState *rs, - RAMBlock *rb, +migration_clear_memory_region_dirty_bitmap_range(RAMBlock *rb, unsigned long start, unsigned long npages) { @@ -832,7 +830,7 @@ migration_clear_memory_region_dirty_bitmap_range(RAMState *rs, * exclusive. */ for (i = chunk_start; i < chunk_end; i += chunk_pages) { - migration_clear_memory_region_dirty_bitmap(rs, rb, i); + migration_clear_memory_region_dirty_bitmap(rb, i); } } @@ -850,7 +848,7 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, * the page in the chunk we clear the remote dirty bitmap for all. * Clearing it earlier won't be a problem, but too late will. */ - migration_clear_memory_region_dirty_bitmap(rs, rb, page); + migration_clear_memory_region_dirty_bitmap(rb, page); ret = test_and_clear_bit(page, rb->bmap); if (ret) { @@ -2777,8 +2775,7 @@ void qemu_guest_free_page_hint(void *addr, size_t len) * are initially set. Otherwise those skipped pages will be sent in * the next round after syncing from the memory region bitmap. */ - migration_clear_memory_region_dirty_bitmap_range(ram_state, block, - start, npages); + migration_clear_memory_region_dirty_bitmap_range(block, start, npages); ram_state->migration_dirty_pages -= bitmap_count_one_with_offset(block->bmap, start, npages); bitmap_clear(block->bmap, start, npages); From b7acd65707623a8b728b622f4c9dd96c2085c05b Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 9 Sep 2021 09:29:50 +0200 Subject: [PATCH 0608/1334] migration: allow multifd for socket protocol only To: , , CC: Li Zhijian Date: Sat, 31 Jul 2021 22:05:51 +0800 (5 weeks, 4 days, 17 hours ago) multifd with unsupported protocol will cause a segment fault. (gdb) bt #0 0x0000563b4a93faf8 in socket_connect (addr=0x0, errp=0x7f7f02675410) at ../util/qemu-sockets.c:1190 #1 0x0000563b4a797a03 in qio_channel_socket_connect_sync (ioc=0x563b4d16e8c0, addr=0x0, errp=0x7f7f02675410) at ../io/channel-socket.c:145 #2 0x0000563b4a797abf in qio_channel_socket_connect_worker (task=0x563b4cd86c30, opaque=0x0) at ../io/channel-socket.c:168 #3 0x0000563b4a792631 in qio_task_thread_worker (opaque=0x563b4cd86c30) at ../io/task.c:124 #4 0x0000563b4a91da69 in qemu_thread_start (args=0x563b4c44bb80) at ../util/qemu-thread-posix.c:541 #5 0x00007f7fe9b5b3f9 in ?? () #6 0x0000000000000000 in ?? () It's enough to check migrate_multifd_is_allowed() in multifd cleanup() and multifd setup() though there are so many other places using migrate_use_multifd(). Signed-off-by: Li Zhijian Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 4 ++++ migration/multifd.c | 24 ++++++++++++++++++++++-- migration/multifd.h | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 6ac807ef3d..f13b07c638 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -453,10 +453,12 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) { const char *p = NULL; + migrate_protocol_allow_multifd(false); /* reset it anyway */ qapi_event_send_migration(MIGRATION_STATUS_SETUP); if (strstart(uri, "tcp:", &p) || strstart(uri, "unix:", NULL) || strstart(uri, "vsock:", NULL)) { + migrate_protocol_allow_multifd(true); socket_start_incoming_migration(p ? p : uri, errp); #ifdef CONFIG_RDMA } else if (strstart(uri, "rdma:", &p)) { @@ -2280,9 +2282,11 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, } } + migrate_protocol_allow_multifd(false); if (strstart(uri, "tcp:", &p) || strstart(uri, "unix:", NULL) || strstart(uri, "vsock:", NULL)) { + migrate_protocol_allow_multifd(true); socket_start_outgoing_migration(s, p ? p : uri, &local_err); #ifdef CONFIG_RDMA } else if (strstart(uri, "rdma:", &p)) { diff --git a/migration/multifd.c b/migration/multifd.c index efd424bc97..283f672bf0 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -531,7 +531,7 @@ void multifd_save_cleanup(void) { int i; - if (!migrate_use_multifd()) { + if (!migrate_use_multifd() || !migrate_multifd_is_allowed()) { return; } multifd_send_terminate_threads(NULL); @@ -868,6 +868,17 @@ cleanup: multifd_new_send_channel_cleanup(p, sioc, local_err); } +static bool migrate_allow_multifd; +void migrate_protocol_allow_multifd(bool allow) +{ + migrate_allow_multifd = allow; +} + +bool migrate_multifd_is_allowed(void) +{ + return migrate_allow_multifd; +} + int multifd_save_setup(Error **errp) { int thread_count; @@ -878,6 +889,11 @@ int multifd_save_setup(Error **errp) if (!migrate_use_multifd()) { return 0; } + if (!migrate_multifd_is_allowed()) { + error_setg(errp, "multifd is not supported by current protocol"); + return -1; + } + s = migrate_get_current(); thread_count = migrate_multifd_channels(); multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); @@ -971,7 +987,7 @@ int multifd_load_cleanup(Error **errp) { int i; - if (!migrate_use_multifd()) { + if (!migrate_use_multifd() || !migrate_multifd_is_allowed()) { return 0; } multifd_recv_terminate_threads(NULL); @@ -1120,6 +1136,10 @@ int multifd_load_setup(Error **errp) if (!migrate_use_multifd()) { return 0; } + if (!migrate_multifd_is_allowed()) { + error_setg(errp, "multifd is not supported by current protocol"); + return -1; + } thread_count = migrate_multifd_channels(); multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state)); multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count); diff --git a/migration/multifd.h b/migration/multifd.h index 16c4d112d1..15c50ca0b2 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -13,6 +13,8 @@ #ifndef QEMU_MIGRATION_MULTIFD_H #define QEMU_MIGRATION_MULTIFD_H +bool migrate_multifd_is_allowed(void); +void migrate_protocol_allow_multifd(bool allow); int multifd_save_setup(Error **errp); void multifd_save_cleanup(void); int multifd_load_setup(Error **errp); From 5ad15e8614b0877225af42aa28a7195ed2fb74e4 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 9 Sep 2021 09:30:52 +0200 Subject: [PATCH 0609/1334] migration: allow enabling mutilfd for specific protocol only To: , , CC: Li Zhijian Date: Sat, 31 Jul 2021 22:05:52 +0800 (5 weeks, 4 days, 17 hours ago) And change the default to true so that in '-incoming defer' case, user is able to change multifd capability. Signed-off-by: Li Zhijian Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 8 ++++++++ migration/multifd.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index f13b07c638..9172686b89 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1237,6 +1237,14 @@ static bool migrate_caps_check(bool *cap_list, } } + /* incoming side only */ + if (runstate_check(RUN_STATE_INMIGRATE) && + !migrate_multifd_is_allowed() && + cap_list[MIGRATION_CAPABILITY_MULTIFD]) { + error_setg(errp, "multifd is not supported by current protocol"); + return false; + } + return true; } diff --git a/migration/multifd.c b/migration/multifd.c index 283f672bf0..7c9deb1921 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -868,7 +868,7 @@ cleanup: multifd_new_send_channel_cleanup(p, sioc, local_err); } -static bool migrate_allow_multifd; +static bool migrate_allow_multifd = true; void migrate_protocol_allow_multifd(bool allow) { migrate_allow_multifd = allow; From e2daccb0d0375717efed80b772e9fd1e4c51ae5b Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 10 Sep 2021 15:02:54 +0800 Subject: [PATCH 0610/1334] migration/rdma: Try to register On-Demand Paging memory region Previously, for the fsdax mem-backend-file, it will register failed with Operation not supported. In this case, we can try to register it with On-Demand Paging[1] like what rpma_mr_reg() does on rpma[2]. [1]: https://community.mellanox.com/s/article/understanding-on-demand-paging--odp-x [2]: http://pmem.io/rpma/manpages/v0.9.0/rpma_mr_reg.3 CC: Marcel Apfelbaum Signed-off-by: Li Zhijian Reviewed-by: Marcel Apfelbaum Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/rdma.c | 73 ++++++++++++++++++++++++++++++------------ migration/trace-events | 1 + 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index 5c2d113aa9..eb80431aae 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -1117,19 +1117,47 @@ static int qemu_rdma_alloc_qp(RDMAContext *rdma) return 0; } +/* Check whether On-Demand Paging is supported by RDAM device */ +static bool rdma_support_odp(struct ibv_context *dev) +{ + struct ibv_device_attr_ex attr = {0}; + int ret = ibv_query_device_ex(dev, NULL, &attr); + if (ret) { + return false; + } + + if (attr.odp_caps.general_caps & IBV_ODP_SUPPORT) { + return true; + } + + return false; +} + static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) { int i; RDMALocalBlocks *local = &rdma->local_ram_blocks; for (i = 0; i < local->nb_blocks; i++) { + int access = IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE; + local->block[i].mr = ibv_reg_mr(rdma->pd, local->block[i].local_host_addr, - local->block[i].length, - IBV_ACCESS_LOCAL_WRITE | - IBV_ACCESS_REMOTE_WRITE + local->block[i].length, access ); + + if (!local->block[i].mr && + errno == ENOTSUP && rdma_support_odp(rdma->verbs)) { + access |= IBV_ACCESS_ON_DEMAND; + /* register ODP mr */ + local->block[i].mr = + ibv_reg_mr(rdma->pd, + local->block[i].local_host_addr, + local->block[i].length, access); + trace_qemu_rdma_register_odp_mr(local->block[i].block_name); + } + if (!local->block[i].mr) { perror("Failed to register local dest ram block!"); break; @@ -1215,28 +1243,33 @@ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, */ if (!block->pmr[chunk]) { uint64_t len = chunk_end - chunk_start; + int access = rkey ? IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE : + 0; trace_qemu_rdma_register_and_get_keys(len, chunk_start); - block->pmr[chunk] = ibv_reg_mr(rdma->pd, - chunk_start, len, - (rkey ? (IBV_ACCESS_LOCAL_WRITE | - IBV_ACCESS_REMOTE_WRITE) : 0)); - - if (!block->pmr[chunk]) { - perror("Failed to register chunk!"); - fprintf(stderr, "Chunk details: block: %d chunk index %d" - " start %" PRIuPTR " end %" PRIuPTR - " host %" PRIuPTR - " local %" PRIuPTR " registrations: %d\n", - block->index, chunk, (uintptr_t)chunk_start, - (uintptr_t)chunk_end, host_addr, - (uintptr_t)block->local_host_addr, - rdma->total_registrations); - return -1; + block->pmr[chunk] = ibv_reg_mr(rdma->pd, chunk_start, len, access); + if (!block->pmr[chunk] && + errno == ENOTSUP && rdma_support_odp(rdma->verbs)) { + access |= IBV_ACCESS_ON_DEMAND; + /* register ODP mr */ + block->pmr[chunk] = ibv_reg_mr(rdma->pd, chunk_start, len, access); + trace_qemu_rdma_register_odp_mr(block->block_name); } - rdma->total_registrations++; } + if (!block->pmr[chunk]) { + perror("Failed to register chunk!"); + fprintf(stderr, "Chunk details: block: %d chunk index %d" + " start %" PRIuPTR " end %" PRIuPTR + " host %" PRIuPTR + " local %" PRIuPTR " registrations: %d\n", + block->index, chunk, (uintptr_t)chunk_start, + (uintptr_t)chunk_end, host_addr, + (uintptr_t)block->local_host_addr, + rdma->total_registrations); + return -1; + } + rdma->total_registrations++; if (lkey) { *lkey = block->pmr[chunk]->lkey; diff --git a/migration/trace-events b/migration/trace-events index a1c0f034ab..5f6aa580de 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -212,6 +212,7 @@ qemu_rdma_poll_write(const char *compstr, int64_t comp, int left, uint64_t block qemu_rdma_poll_other(const char *compstr, int64_t comp, int left) "other completion %s (%" PRId64 ") received left %d" qemu_rdma_post_send_control(const char *desc) "CONTROL: sending %s.." qemu_rdma_register_and_get_keys(uint64_t len, void *start) "Registering %" PRIu64 " bytes @ %p" +qemu_rdma_register_odp_mr(const char *name) "Try to register On-Demand Paging memory region: %s" qemu_rdma_registration_handle_compress(int64_t length, int index, int64_t offset) "Zapping zero chunk: %" PRId64 " bytes, index %d, offset %" PRId64 qemu_rdma_registration_handle_finished(void) "" qemu_rdma_registration_handle_ram_blocks(void) "" From 911965ace9386e35ca022a65bb45a32fd421af3e Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 10 Sep 2021 15:02:55 +0800 Subject: [PATCH 0611/1334] migration/rdma: advise prefetch write for ODP region The responder mr registering with ODP will sent RNR NAK back to the requester in the face of the page fault. --------- ibv_poll_cq wc.status=13 RNR retry counter exceeded! ibv_poll_cq wrid=WRITE RDMA! --------- ibv_advise_mr(3) helps to make pages present before the actual IO is conducted so that the responder does page fault as little as possible. Signed-off-by: Li Zhijian Reviewed-by: Marcel Apfelbaum Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- meson.build | 6 ++++++ migration/rdma.c | 42 ++++++++++++++++++++++++++++++++++++++++++ migration/trace-events | 1 + 3 files changed, 49 insertions(+) diff --git a/meson.build b/meson.build index 5e7946776d..9ed9a993e2 100644 --- a/meson.build +++ b/meson.build @@ -1530,6 +1530,12 @@ config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range')) config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util)) config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul')) config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include ')) +if rdma.found() + config_host_data.set('HAVE_IBV_ADVISE_MR', + cc.has_function('ibv_advise_mr', + args: config_host['RDMA_LIBS'].split(), + prefix: '#include ')) +endif # has_header_symbol config_host_data.set('CONFIG_BYTESWAP_H', diff --git a/migration/rdma.c b/migration/rdma.c index eb80431aae..2a3c7889b9 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -1133,6 +1133,32 @@ static bool rdma_support_odp(struct ibv_context *dev) return false; } +/* + * ibv_advise_mr to avoid RNR NAK error as far as possible. + * The responder mr registering with ODP will sent RNR NAK back to + * the requester in the face of the page fault. + */ +static void qemu_rdma_advise_prefetch_mr(struct ibv_pd *pd, uint64_t addr, + uint32_t len, uint32_t lkey, + const char *name, bool wr) +{ +#ifdef HAVE_IBV_ADVISE_MR + int ret; + int advice = wr ? IBV_ADVISE_MR_ADVICE_PREFETCH_WRITE : + IBV_ADVISE_MR_ADVICE_PREFETCH; + struct ibv_sge sg_list = {.lkey = lkey, .addr = addr, .length = len}; + + ret = ibv_advise_mr(pd, advice, + IBV_ADVISE_MR_FLAG_FLUSH, &sg_list, 1); + /* ignore the error */ + if (ret) { + trace_qemu_rdma_advise_mr(name, len, addr, strerror(errno)); + } else { + trace_qemu_rdma_advise_mr(name, len, addr, "successed"); + } +#endif +} + static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) { int i; @@ -1156,6 +1182,15 @@ static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) local->block[i].local_host_addr, local->block[i].length, access); trace_qemu_rdma_register_odp_mr(local->block[i].block_name); + + if (local->block[i].mr) { + qemu_rdma_advise_prefetch_mr(rdma->pd, + (uintptr_t)local->block[i].local_host_addr, + local->block[i].length, + local->block[i].mr->lkey, + local->block[i].block_name, + true); + } } if (!local->block[i].mr) { @@ -1255,6 +1290,13 @@ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, /* register ODP mr */ block->pmr[chunk] = ibv_reg_mr(rdma->pd, chunk_start, len, access); trace_qemu_rdma_register_odp_mr(block->block_name); + + if (block->pmr[chunk]) { + qemu_rdma_advise_prefetch_mr(rdma->pd, (uintptr_t)chunk_start, + len, block->pmr[chunk]->lkey, + block->block_name, rkey); + + } } } if (!block->pmr[chunk]) { diff --git a/migration/trace-events b/migration/trace-events index 5f6aa580de..a8ae163707 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -213,6 +213,7 @@ qemu_rdma_poll_other(const char *compstr, int64_t comp, int left) "other complet qemu_rdma_post_send_control(const char *desc) "CONTROL: sending %s.." qemu_rdma_register_and_get_keys(uint64_t len, void *start) "Registering %" PRIu64 " bytes @ %p" qemu_rdma_register_odp_mr(const char *name) "Try to register On-Demand Paging memory region: %s" +qemu_rdma_advise_mr(const char *name, uint32_t len, uint64_t addr, const char *res) "Try to advise block %s prefetch at %" PRIu32 "@0x%" PRIx64 ": %s" qemu_rdma_registration_handle_compress(int64_t length, int index, int64_t offset) "Zapping zero chunk: %" PRId64 " bytes, index %d, offset %" PRId64 qemu_rdma_registration_handle_finished(void) "" qemu_rdma_registration_handle_ram_blocks(void) "" From 685db13a38f7599fabd353382ff65d3c244ea641 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:37 -0400 Subject: [PATCH 0612/1334] tests: acpi: dump table with failed checksum Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-2-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/acpi-utils.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/qtest/acpi-utils.c b/tests/qtest/acpi-utils.c index d2a202efca..766c48e3a6 100644 --- a/tests/qtest/acpi-utils.c +++ b/tests/qtest/acpi-utils.c @@ -98,6 +98,20 @@ void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len, ACPI_ASSERT_CMP(**aml, sig); } if (verify_checksum) { + if (acpi_calc_checksum(*aml, *aml_len)) { + gint fd, ret; + char *fname = NULL; + GError *error = NULL; + + fprintf(stderr, "Invalid '%.4s'(%d)\n", *aml, *aml_len); + fd = g_file_open_tmp("malformed-XXXXXX.dat", &fname, &error); + g_assert_no_error(error); + fprintf(stderr, "Dumping invalid table into '%s'\n", fname); + ret = qemu_write_full(fd, *aml, *aml_len); + g_assert(ret == *aml_len); + close(fd); + g_free(fname); + } g_assert(!acpi_calc_checksum(*aml, *aml_len)); } } From e741aff0f43343d6d91242fee1072fee376d5cce Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:38 -0400 Subject: [PATCH 0613/1334] tests: qtest: add qtest_has_accel() to check if tested binary supports accelerator Currently it is not possible to create tests that have KVM as a hard requirement on a host that doesn't support KVM for tested target binary (modulo going through the trouble of compiling out the offending test case). Following scenario makes test fail when it's run on non x86 host: qemu-system-x86_64 -enable-kvm -M q35,kernel-irqchip=on -smp 1,maxcpus=288 This patch introduces qtest_has_accel() to let users check if accel is available in advance and avoid executing non run-able test-cases. It implements detection of TCG and KVM only, the rest could be added later on, when we actually start testing them in qtest. Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-3-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- meson.build | 6 ++++++ tests/qtest/libqos/libqtest.h | 8 ++++++++ tests/qtest/libqtest.c | 27 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/meson.build b/meson.build index 9ed9a993e2..2c5b53cbe2 100644 --- a/meson.build +++ b/meson.build @@ -75,6 +75,12 @@ else kvm_targets = [] endif +kvm_targets_c = '' +if not get_option('kvm').disabled() and targetos == 'linux' + kvm_targets_c = '"' + '" ,"'.join(kvm_targets) + '"' +endif +config_host_data.set('CONFIG_KVM_TARGETS', kvm_targets_c) + accelerator_targets = { 'CONFIG_KVM': kvm_targets } if cpu in ['aarch64'] diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h index a68dcd79d4..59e9271195 100644 --- a/tests/qtest/libqos/libqtest.h +++ b/tests/qtest/libqos/libqtest.h @@ -588,6 +588,14 @@ bool qtest_big_endian(QTestState *s); */ const char *qtest_get_arch(void); +/** + * qtest_has_accel: + * @accel_name: Accelerator name to check for. + * + * Returns: true if the accelerator is built in. + */ +bool qtest_has_accel(const char *accel_name); + /** * qtest_add_func: * @str: Test case path. diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 73f6b977a6..25aeea385b 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -922,6 +922,33 @@ const char *qtest_get_arch(void) return end + 1; } +bool qtest_has_accel(const char *accel_name) +{ + if (g_str_equal(accel_name, "tcg")) { +#if defined(CONFIG_TCG) + return true; +#else + return false; +#endif + } else if (g_str_equal(accel_name, "kvm")) { + int i; + const char *arch = qtest_get_arch(); + const char *targets[] = { CONFIG_KVM_TARGETS }; + + for (i = 0; i < ARRAY_SIZE(targets); i++) { + if (!strncmp(targets[i], arch, strlen(arch))) { + if (!access("/dev/kvm", R_OK | W_OK)) { + return true; + } + } + } + } else { + /* not implemented */ + g_assert_not_reached(); + } + return false; +} + bool qtest_get_irq(QTestState *s, int num) { /* dummy operation in order to make sure irq is up to date */ From c306cdb0cc5327f336fbf9a7411f319f0717ee75 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:39 -0400 Subject: [PATCH 0614/1334] tests: acpi: whitelist expected tables for acpi/q35/xapic testcase Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-4-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.xapic | 0 tests/data/acpi/q35/DSDT.xapic | 0 tests/data/acpi/q35/FACP.xapic | 0 tests/data/acpi/q35/SRAT.xapic | 0 tests/qtest/bios-tables-test-allowed-diff.h | 4 ++++ 5 files changed, 4 insertions(+) create mode 100644 tests/data/acpi/q35/APIC.xapic create mode 100644 tests/data/acpi/q35/DSDT.xapic create mode 100644 tests/data/acpi/q35/FACP.xapic create mode 100644 tests/data/acpi/q35/SRAT.xapic diff --git a/tests/data/acpi/q35/APIC.xapic b/tests/data/acpi/q35/APIC.xapic new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/q35/DSDT.xapic new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/FACP.xapic b/tests/data/acpi/q35/FACP.xapic new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/SRAT.xapic b/tests/data/acpi/q35/SRAT.xapic new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..344eee3acc 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,5 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/DSDT.xapic", +"tests/data/acpi/q35/SRAT.xapic", +"tests/data/acpi/q35/FACP.xapic", +"tests/data/acpi/q35/APIC.xapic", From 80a2f3387c239d06d4a4b6838b511a88838ddc0f Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:40 -0400 Subject: [PATCH 0615/1334] tests: acpi: q35: test for x2APIC entries in SRAT Set -smp 1,maxcpus=288 to test for ACPI code that deal with CPUs with large APIC ID (>255). PS: Test requires KVM and in-kernel irqchip support, so skip test if KVM is not available. Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-5-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 4f11d03055..87a94a59fa 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1033,6 +1033,19 @@ static void test_acpi_q35_tcg_numamem(void) free_test_data(&data); } +static void test_acpi_q35_kvm_xapic(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".xapic"; + test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" + " -numa node -numa node,memdev=ram0" + " -machine kernel-irqchip=on -smp 1,maxcpus=288", &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_nosmm(void) { test_data data; @@ -1509,6 +1522,7 @@ static void test_acpi_oem_fields_virt(void) int main(int argc, char *argv[]) { const char *arch = qtest_get_arch(); + const bool has_kvm = qtest_has_accel("kvm"); int ret; g_test_init(&argc, &argv, NULL); @@ -1567,6 +1581,9 @@ int main(int argc, char *argv[]) if (strcmp(arch, "x86_64") == 0) { qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); } + if (has_kvm) { + qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic); + } } else if (strcmp(arch, "aarch64") == 0) { qtest_add_func("acpi/virt", test_acpi_virt_tcg); qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); From b59a898458aea328618521be7dc180d2396c0bf4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:41 -0400 Subject: [PATCH 0616/1334] tests: acpi: update expected tables blobs Update adds CPU entries to MADT/SRAT/FACP and DSDT to cover 288 CPUs. Notable changes are that CPUs with APIC ID 255 and higher use 'Processor Local x2APIC Affinity' structure in SRAT and "Device" element in DSDT. FACP: - Use APIC Cluster Model (V4) : 0 + Use APIC Cluster Model (V4) : 1 SRAT: ... +[1010h 4112 1] Subtable Type : 00 [Processor Local APIC/SAPIC Affinity] +[1011h 4113 1] Length : 10 + +[1012h 4114 1] Proximity Domain Low(8) : 00 +[1013h 4115 1] Apic ID : FE +[1014h 4116 4] Flags (decoded below) : 00000001 + Enabled : 1 +[1018h 4120 1] Local Sapic EID : 00 +[1019h 4121 3] Proximity Domain High(24) : 000000 +[101Ch 4124 4] Clock Domain : 00000000 + +[1020h 4128 1] Subtable Type : 02 [Processor Local x2APIC Affinity] +[1021h 4129 1] Length : 18 + +[1022h 4130 2] Reserved1 : 0000 +[1024h 4132 4] Proximity Domain : 00000001 +[1028h 4136 4] Apic ID : 000000FF +[102Ch 4140 4] Flags (decoded below) : 00000001 + Enabled : 1 +[1030h 4144 4] Clock Domain : 00000000 +[1034h 4148 4] Reserved2 : 00000000 ... +[1320h 4896 1] Subtable Type : 02 [Processor Local x2APIC Affinity] +[1321h 4897 1] Length : 18 + +[1322h 4898 2] Reserved1 : 0000 +[1324h 4900 4] Proximity Domain : 00000001 +[1328h 4904 4] Apic ID : 0000011F +[132Ch 4908 4] Flags (decoded below) : 00000001 + Enabled : 1 +[1330h 4912 4] Clock Domain : 00000000 +[1334h 4916 4] Reserved2 : 00000000 DSDT: ... + Processor (C0FE, 0xFE, 0x00000000, 0x00) + { ... + } + + Device (C0FF) + { + Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID + Name (_UID, 0xFF) // _UID: Unique ID ... + } + Device (C11F) + { + Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID + Name (_UID, 0x011F) // _UID: Unique ID ... + } APIC: +[034h 0052 1] Subtable Type : 00 [Processor Local APIC] +[035h 0053 1] Length : 08 +[036h 0054 1] Processor ID : 01 +[037h 0055 1] Local Apic ID : 01 +[038h 0056 4] Flags (decoded below) : 00000000 + Processor Enabled : 0 ... +[81Ch 2076 1] Subtable Type : 00 [Processor Local APIC] +[81Dh 2077 1] Length : 08 +[81Eh 2078 1] Processor ID : FE +[81Fh 2079 1] Local Apic ID : FE +[820h 2080 4] Flags (decoded below) : 00000000 + Processor Enabled : 0 + +[824h 2084 1] Subtable Type : 09 [Processor Local x2APIC] +[825h 2085 1] Length : 10 +[826h 2086 2] Reserved : 0000 +[828h 2088 4] Processor x2Apic ID : 000000FF +[82Ch 2092 4] Flags (decoded below) : 00000000 + Processor Enabled : 0 +[830h 2096 4] Processor UID : 000000FF ... +[A24h 2596 1] Subtable Type : 09 [Processor Local x2APIC] +[A25h 2597 1] Length : 10 +[A26h 2598 2] Reserved : 0000 +[A28h 2600 4] Processor x2Apic ID : 0000011F +[A2Ch 2604 4] Flags (decoded below) : 00000000 + Processor Enabled : 0 +[A30h 2608 4] Processor UID : 0000011F + +[A34h 2612 1] Subtable Type : 01 [I/O APIC] +[A35h 2613 1] Length : 0C +[A36h 2614 1] I/O Apic ID : 00 +[A37h 2615 1] Reserved : 00 +[A38h 2616 4] Address : FEC00000 +[A3Ch 2620 4] Interrupt : 00000000 + +[A40h 2624 1] Subtable Type : 02 [Interrupt Source Override] +[A41h 2625 1] Length : 0A +[A42h 2626 1] Bus : 00 +[A43h 2627 1] Source : 00 +[A44h 2628 4] Interrupt : 00000002 +[A48h 2632 2] Flags (decoded below) : 0000 Polarity : 0 Trigger Mode : 0 -[04Ah 0074 1] Subtable Type : 02 [Interrupt Source Override] -[04Bh 0075 1] Length : 0A -[04Ch 0076 1] Bus : 00 -[04Dh 0077 1] Source : 05 -[04Eh 0078 4] Interrupt : 00000005 -[052h 0082 2] Flags (decoded below) : 000D +[A4Ah 2634 1] Subtable Type : 02 [Interrupt Source Override] +[A4Bh 2635 1] Length : 0A +[A4Ch 2636 1] Bus : 00 +[A4Dh 2637 1] Source : 05 +[A4Eh 2638 4] Interrupt : 00000005 +[A52h 2642 2] Flags (decoded below) : 000D Polarity : 1 Trigger Mode : 3 -[054h 0084 1] Subtable Type : 02 [Interrupt Source Override] -[055h 0085 1] Length : 0A -[056h 0086 1] Bus : 00 -[057h 0087 1] Source : 09 -[058h 0088 4] Interrupt : 00000009 -[05Ch 0092 2] Flags (decoded below) : 000D +[A54h 2644 1] Subtable Type : 02 [Interrupt Source Override] +[A55h 2645 1] Length : 0A +[A56h 2646 1] Bus : 00 +[A57h 2647 1] Source : 09 +[A58h 2648 4] Interrupt : 00000009 +[A5Ch 2652 2] Flags (decoded below) : 000D Polarity : 1 Trigger Mode : 3 -[05Eh 0094 1] Subtable Type : 02 [Interrupt Source Override] -[05Fh 0095 1] Length : 0A -[060h 0096 1] Bus : 00 -[061h 0097 1] Source : 0A -[062h 0098 4] Interrupt : 0000000A -[066h 0102 2] Flags (decoded below) : 000D +[A5Eh 2654 1] Subtable Type : 02 [Interrupt Source Override] +[A5Fh 2655 1] Length : 0A +[A60h 2656 1] Bus : 00 +[A61h 2657 1] Source : 0A +[A62h 2658 4] Interrupt : 0000000A +[A66h 2662 2] Flags (decoded below) : 000D Polarity : 1 Trigger Mode : 3 -[068h 0104 1] Subtable Type : 02 [Interrupt Source Override] -[069h 0105 1] Length : 0A -[06Ah 0106 1] Bus : 00 -[06Bh 0107 1] Source : 0B -[06Ch 0108 4] Interrupt : 0000000B -[070h 0112 2] Flags (decoded below) : 000D +[A68h 2664 1] Subtable Type : 02 [Interrupt Source Override] +[A69h 2665 1] Length : 0A +[A6Ah 2666 1] Bus : 00 +[A6Bh 2667 1] Source : 0B +[A6Ch 2668 4] Interrupt : 0000000B +[A70h 2672 2] Flags (decoded below) : 000D Polarity : 1 Trigger Mode : 3 -[072h 0114 1] Subtable Type : 04 [Local APIC NMI] -[073h 0115 1] Length : 06 -[074h 0116 1] Processor ID : FF -[075h 0117 2] Flags (decoded below) : 0000 +[A72h 2674 1] Subtable Type : 0A [Local x2APIC NMI] +[A73h 2675 1] Length : 0C +[A74h 2676 2] Flags (decoded below) : 0000 Polarity : 0 Trigger Mode : 0 -[077h 0119 1] Interrupt Input LINT : 01 +[A76h 2678 4] Processor UID : FFFFFFFF +[A7Ah 2682 1] Interrupt Input LINT : 01 +[A7Bh 2683 3] Reserved : 000000 Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-6-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.xapic | Bin 0 -> 2686 bytes tests/data/acpi/q35/DSDT.xapic | Bin 0 -> 35652 bytes tests/data/acpi/q35/FACP.xapic | Bin 0 -> 244 bytes tests/data/acpi/q35/SRAT.xapic | Bin 0 -> 5080 bytes tests/qtest/bios-tables-test-allowed-diff.h | 4 ---- 5 files changed, 4 deletions(-) diff --git a/tests/data/acpi/q35/APIC.xapic b/tests/data/acpi/q35/APIC.xapic index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c1969c35aa12b61d25e0134bbb8d2187ba42d663 100644 GIT binary patch literal 2686 zcmXZeQ+OOv7=Yo~aI?wAcAeU0vPDuQZHm;k?bNny+g57ZPHkIh=b!JKoA-S43@*-G z{Lu+-N@3~J zSf&g{M`KJ3+H6?1ES4*W<;!D*3K$!UadB9&B37z|l`CVFDp<8D#>Znq0#>Vr)vIG- zBG#yZHEUw6T3EX_CM98=I#{tllk*svisYJ`m&W0NM>v?(@ghRvH}ix$|j zCAMmXty^Q8HrTc;wrhv&+hd0g*s&va>V%y;W0x-2wJUb(hTXekj~>{wC-&-vy?bMy zKG?S}_UniJ`{RHCn4F9Q2jZYXICwA)8G=KH;;>;jd^nC6fg?xas8Kk2G>#dAW5?pS zaX5ZFPMClbC*q_@IC(NonSxWN;#ZrFeuH{zyExOp>f z*@9cQ;hfjf8Nu3fl$H}2Vkd-vkLeYk%=9yovp4`NCR9y)}F595&|c=RY9 zJBG)P- zElRtp7Ny-(i_%urqO`keQQAYbDDA0Ql=f0BN_(porF~S3(!Q!iX+PDXw7+UmIzY83 z9jIEA4pJ>j2dfsPLsW~>p{hmcFx8@TxN1>4LbYgele0}R{tY{ee%8iO>pNrYvugUz Y81RgpG2$6JW5}~=7X9ge`jQv^2Net*5C8xG literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/q35/DSDT.xapic index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..119fc90f1f8a7b6934df6fd95609446e627ce15d 100644 GIT binary patch literal 35652 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 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/FACP.xapic b/tests/data/acpi/q35/FACP.xapic index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2d3659c9c6753d07c3d48742343cb8e8cc034de7 100644 GIT binary patch literal 244 zcmZ>BbPo8!z`($~)yd!4BUr&HBEVSz2pEB4AU24G0Y(N+hD|^Y6El!tgNU*~X%LSC z$X0-fGcm9T0LA|E|L2FOWMD7?GM2WmFfej3F#P0!h{7ddihwku0+2v57svwxMxcSn X_QAxFX+{NzJ3wNL4G8yu_%Hwf>QN2> literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/SRAT.xapic b/tests/data/acpi/q35/SRAT.xapic index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1a91cfa65fdbebe1756d63d80022c2af767e5d3f 100644 GIT binary patch literal 5080 zcmaLbd6bS-9KiAS9m_Bb!whB^ODQIzkhDk{WhpVupdq1%l!~-aq1~F4H7%5Avt?^h zSz6I1OVggM&?eD}ni*#ppIrBx=iKM>{oOzBdCz;Etjy*eauOqv=+gA|Em~*QNYBn_ zQ6qOnBauknd)}V+99jwgbCh`uE`p2VVz@Yt#U*e_9EVHc(l{QM!DVp*E{DtGM4W^x z;EK2sPR1#?GOmJCaaCLmSI7BxqtJagj`{JpCO!e5h-=~6I1QhK>)^WhWLyu|$EV;^ zaRb~CH^Qgk#`tvH1fPM=#Ao5NaZ`K_Zidgr=i%l!9q0ds6uK|xGrs`0#I0~^+y-BW zFT!o{#kd`Ak2CNkI1^{#4!9%kgfGR}xHIm8FT-8&<+vN}j(gxM@RhhH?uDTCW zHSUYA!Pnw`_&R(&z5(~g1MrP_AifFTjBmlW;z4*Yz75}w@4!RwP&^FZiSNR9<9qPE z_&$6;9*#%gk$4myjmO{z@L2pH9)};o599H80-lH`;mLRkegsd&kK)Jh zf&avR;lJ@ecpu)64-|Gx^1*-Z|K@H->v{2LJsRjSfnFrgiw1hJKrbHXv4LJ9&`Sn- zT%eZ<^wNPIALwNQy= Date: Thu, 2 Sep 2021 07:35:42 -0400 Subject: [PATCH 0617/1334] tests: acpi: whitelist new expected table tests/data/acpi/q35/DMAR.dmar Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-7-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/DMAR.dmar | 0 tests/qtest/bios-tables-test-allowed-diff.h | 1 + 2 files changed, 1 insertion(+) create mode 100644 tests/data/acpi/q35/DMAR.dmar diff --git a/tests/data/acpi/q35/DMAR.dmar b/tests/data/acpi/q35/DMAR.dmar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..a2843335c8 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/DMAR.dmar", From 0ff92b6d99011c8de57321503c0eb655c461a217 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:43 -0400 Subject: [PATCH 0618/1334] tests: acpi: add testcase for intel_iommu (DMAR table) Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-8-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 87a94a59fa..0361c84c5e 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1090,6 +1090,18 @@ static void test_acpi_q35_tcg_nohpet(void) free_test_data(&data); } +static void test_acpi_q35_kvm_dmar(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".dmar"; + test_acpi_one("-machine kernel-irqchip=split -accel kvm" + " -device intel-iommu,intremap=on,device-iotlb=on", &data); + free_test_data(&data); +} + static void test_acpi_piix4_tcg_numamem(void) { test_data data; @@ -1583,6 +1595,7 @@ int main(int argc, char *argv[]) } if (has_kvm) { qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic); + qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar); } } else if (strcmp(arch, "aarch64") == 0) { qtest_add_func("acpi/virt", test_acpi_virt_tcg); From 44d3bdd8a6f1ae2a5ca417251736a033900d4c08 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:44 -0400 Subject: [PATCH 0619/1334] tests: acpi: add expected blob for DMAR table [000h 0000 4] Signature : "DMAR" [DMA Remapping table] [004h 0004 4] Table Length : 00000078 [008h 0008 1] Revision : 01 [009h 0009 1] Checksum : 15 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 1] Host Address Width : 26 [025h 0037 1] Flags : 01 [026h 0038 10] Reserved : 00 00 00 00 00 00 00 00 00 00 [030h 0048 2] Subtable Type : 0000 [Hardware Unit Definition] [032h 0050 2] Length : 0040 [034h 0052 1] Flags : 00 [035h 0053 1] Reserved : 00 [036h 0054 2] PCI Segment Number : 0000 [038h 0056 8] Register Base Address : 00000000FED90000 [040h 0064 1] Device Scope Type : 03 [IOAPIC Device] [041h 0065 1] Entry Length : 08 [042h 0066 2] Reserved : 0000 [044h 0068 1] Enumeration ID : 00 [045h 0069 1] PCI Bus Number : FF [046h 0070 2] PCI Path : 00,00 [048h 0072 1] Device Scope Type : 01 [PCI Endpoint Device] [049h 0073 1] Entry Length : 08 [04Ah 0074 2] Reserved : 0000 [04Ch 0076 1] Enumeration ID : 00 [04Dh 0077 1] PCI Bus Number : 00 [04Eh 0078 2] PCI Path : 00,00 [050h 0080 1] Device Scope Type : 01 [PCI Endpoint Device] [051h 0081 1] Entry Length : 08 [052h 0082 2] Reserved : 0000 [054h 0084 1] Enumeration ID : 00 [055h 0085 1] PCI Bus Number : 00 [056h 0086 2] PCI Path : 01,00 [058h 0088 1] Device Scope Type : 01 [PCI Endpoint Device] [059h 0089 1] Entry Length : 08 [05Ah 0090 2] Reserved : 0000 [05Ch 0092 1] Enumeration ID : 00 [05Dh 0093 1] PCI Bus Number : 00 [05Eh 0094 2] PCI Path : 1F,00 [060h 0096 1] Device Scope Type : 01 [PCI Endpoint Device] [061h 0097 1] Entry Length : 08 [062h 0098 2] Reserved : 0000 [064h 0100 1] Enumeration ID : 00 [065h 0101 1] PCI Bus Number : 00 [066h 0102 2] PCI Path : 1F,02 [068h 0104 1] Device Scope Type : 01 [PCI Endpoint Device] [069h 0105 1] Entry Length : 08 [06Ah 0106 2] Reserved : 0000 [06Ch 0108 1] Enumeration ID : 00 [06Dh 0109 1] PCI Bus Number : 00 [06Eh 0110 2] PCI Path : 1F,03 [070h 0112 2] Subtable Type : 0002 [Root Port ATS Capability] [072h 0114 2] Length : 0008 [074h 0116 1] Flags : 01 [075h 0117 1] Reserved : 00 [076h 0118 2] PCI Segment Number : 0000 Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-9-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/DMAR.dmar | Bin 0 -> 120 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/q35/DMAR.dmar b/tests/data/acpi/q35/DMAR.dmar index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0dca6e68ad8a8ca5b981bcfbc745385a63e9f216 100644 GIT binary patch literal 120 zcmZ?qbquOtU|?Vrb@F%i2v%^42yj*a0!E-1hz+9EKm;5(Kv_5cff&pjK;l18KZphb W2+atk Date: Thu, 2 Sep 2021 07:35:45 -0400 Subject: [PATCH 0620/1334] tests: acpi: whitelist expected blobs for new acpi/q35/ivrs testcase Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-10-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/DSDT.ivrs | 0 tests/data/acpi/q35/IVRS.ivrs | 0 tests/qtest/bios-tables-test-allowed-diff.h | 2 ++ 3 files changed, 2 insertions(+) create mode 100644 tests/data/acpi/q35/DSDT.ivrs create mode 100644 tests/data/acpi/q35/IVRS.ivrs diff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/q35/DSDT.ivrs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/IVRS.ivrs b/tests/data/acpi/q35/IVRS.ivrs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..4fe8e8272a 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,3 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/DSDT.ivrs", +"tests/data/acpi/q35/IVRS.ivrs", From a4c730cbb284479ec2f7799bbbd7521525553593 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:46 -0400 Subject: [PATCH 0621/1334] tests: acpi: add testcase for amd-iommu (IVRS table) Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-11-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 0361c84c5e..ef28bb41f3 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1102,6 +1102,18 @@ static void test_acpi_q35_kvm_dmar(void) free_test_data(&data); } +static void test_acpi_q35_tcg_ivrs(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".ivrs"; + data.tcg_only = true, + test_acpi_one(" -device amd-iommu", &data); + free_test_data(&data); +} + static void test_acpi_piix4_tcg_numamem(void) { test_data data; @@ -1581,6 +1593,7 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/smm-compat-nosmm", test_acpi_q35_tcg_smm_compat_nosmm); qtest_add_func("acpi/q35/nohpet", test_acpi_q35_tcg_nohpet); + qtest_add_func("acpi/q35/ivrs", test_acpi_q35_tcg_ivrs); qtest_add_func("acpi/piix4/dimmpxm", test_acpi_piix4_tcg_dimm_pxm); qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); qtest_add_func("acpi/piix4/acpihmat", test_acpi_piix4_tcg_acpi_hmat); From ef422a14226fd0078bdf61657fdd63099cb49099 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:47 -0400 Subject: [PATCH 0622/1334] tests: acpi: update expected blobs DSDT: + Device (S10) + { + Name (_ADR, 0x00020000) // _ADR: Address + } New IVRS table: [000h 0000 4] Signature : "IVRS" [I/O Virtualization Reporting Structure] [004h 0004 4] Table Length : 00000068 [008h 0008 1] Revision : 01 [009h 0009 1] Checksum : 43 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 4] Virtualization Info : 00002800 [028h 0040 8] Reserved : 0000000000000000 [030h 0048 1] Subtable Type : 10 [Hardware Definition Block] [031h 0049 1] Flags : D1 [032h 0050 2] Length : 0038 [034h 0052 2] DeviceId : 0010 [036h 0054 2] Capability Offset : 0040 [038h 0056 8] Base Address : 00000000FED80000 [040h 0064 2] PCI Segment Group : 0000 [042h 0066 2] Virtualization Info : 0000 [044h 0068 4] Reserved : 00000044 [048h 0072 1] Entry Type : 02 [049h 0073 2] Device ID : 0000 [04Bh 0075 1] Data Setting : 00 [04Ch 0076 1] Entry Type : 02 [04Dh 0077 2] Device ID : 0008 [04Fh 0079 1] Data Setting : 00 [050h 0080 1] Entry Type : 02 [051h 0081 2] Device ID : 0010 [053h 0083 1] Data Setting : 00 [054h 0084 1] Entry Type : 02 [055h 0085 2] Device ID : 00F8 [057h 0087 1] Data Setting : 00 [058h 0088 1] Entry Type : 02 [059h 0089 2] Device ID : 00FA [05Bh 0091 1] Data Setting : 00 [05Ch 0092 1] Entry Type : 02 [05Dh 0093 2] Device ID : 00FB [05Fh 0095 1] Data Setting : 00 [060h 0096 1] Entry Type : 48 [061h 0097 2] Device ID : 0000 [063h 0099 1] Data Setting : 00 [064h 0100 1] Handle : 00 [065h 0101 2] Source Used Device ID : 00A0 [067h 0103 1] Variety : 01 Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-12-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/DSDT.ivrs | Bin 0 -> 8306 bytes tests/data/acpi/q35/IVRS.ivrs | Bin 0 -> 104 bytes tests/qtest/bios-tables-test-allowed-diff.h | 2 -- 3 files changed, 2 deletions(-) diff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/q35/DSDT.ivrs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b0eafe90e5832935557ec5e6802c0147c88f379c 100644 GIT binary patch literal 8306 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 Date: Thu, 2 Sep 2021 07:35:48 -0400 Subject: [PATCH 0623/1334] tests: acpi: arm/virt: drop redundant test_acpi_one() in test_acpi_virt_tcg() follow up call with smbios options generates the same ACPI tables, so there is no need to run smbios-less variant at all. Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-13-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index ef28bb41f3..873eb9c037 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1430,9 +1430,6 @@ static void test_acpi_virt_tcg(void) .scan_len = 128ULL * 1024 * 1024, }; - test_acpi_one("-cpu cortex-a57", &data); - free_test_data(&data); - data.smbios_cpu_max_speed = 2900; data.smbios_cpu_curr_speed = 2700; test_acpi_one("-cpu cortex-a57 " From 07e6ed2c79b311aad4d2c87740753752ad1503ba Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:49 -0400 Subject: [PATCH 0624/1334] tests: arm-cpu-features: use qtest_has_kvm() API and drop custom function that were doing the job Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-14-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/arm-cpu-features.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 90a87f0ea9..f76652143a 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -26,21 +26,6 @@ " 'arguments': { 'type': 'full', " #define QUERY_TAIL "}}" -static bool kvm_enabled(QTestState *qts) -{ - QDict *resp, *qdict; - bool enabled; - - resp = qtest_qmp(qts, "{ 'execute': 'query-kvm' }"); - g_assert(qdict_haskey(resp, "return")); - qdict = qdict_get_qdict(resp, "return"); - g_assert(qdict_haskey(qdict, "enabled")); - enabled = qdict_get_bool(qdict, "enabled"); - qobject_unref(resp); - - return enabled; -} - static QDict *do_query_no_props(QTestState *qts, const char *cpu_type) { return qtest_qmp(qts, QUERY_HEAD "'model': { 'name': %s }" @@ -506,14 +491,6 @@ static void test_query_cpu_model_expansion_kvm(const void *data) qts = qtest_init(MACHINE_KVM "-cpu max"); - /* - * These tests target the 'host' CPU type, so KVM must be enabled. - */ - if (!kvm_enabled(qts)) { - qtest_quit(qts); - return; - } - /* Enabling and disabling kvm-no-adjvtime should always work. */ assert_has_feature_disabled(qts, "host", "kvm-no-adjvtime"); assert_set_feature(qts, "host", "kvm-no-adjvtime", true); @@ -637,7 +614,11 @@ int main(int argc, char **argv) * order avoid attempting to run an AArch32 QEMU with KVM on * AArch64 hosts. That won't work and isn't easy to detect. */ - if (g_str_equal(qtest_get_arch(), "aarch64")) { + if (g_str_equal(qtest_get_arch(), "aarch64") && qtest_has_accel("kvm")) { + /* + * This tests target the 'host' CPU type, so register it only if + * KVM is available. + */ qtest_add_data_func("/arm/kvm/query-cpu-model-expansion", NULL, test_query_cpu_model_expansion_kvm); } From b72c76033de766d4ff891c582514caf12edbbfb4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:50 -0400 Subject: [PATCH 0625/1334] tests: migration-test: use qtest_has_accel() API Signed-off-by: Igor Mammedov Suggested-by: Thomas Huth Message-Id: <20210902113551.461632-15-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/migration-test.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index cc5e83d98a..7b42f6fd90 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1420,6 +1420,7 @@ static bool kvm_dirty_ring_supported(void) int main(int argc, char **argv) { char template[] = "/tmp/migration-test-XXXXXX"; + const bool has_kvm = qtest_has_accel("kvm"); int ret; g_test_init(&argc, &argv, NULL); @@ -1434,8 +1435,7 @@ int main(int argc, char **argv) * some reason) */ if (g_str_equal(qtest_get_arch(), "ppc64") && - (access("/sys/module/kvm_hv", F_OK) || - access("/dev/kvm", R_OK | W_OK))) { + (!has_kvm || access("/sys/module/kvm_hv", F_OK))) { g_test_message("Skipping test: kvm_hv not available"); return g_test_run(); } @@ -1444,16 +1444,9 @@ int main(int argc, char **argv) * Similar to ppc64, s390x seems to be touchy with TCG, so disable it * there until the problems are resolved */ - if (g_str_equal(qtest_get_arch(), "s390x")) { -#if defined(HOST_S390X) - if (access("/dev/kvm", R_OK | W_OK)) { - g_test_message("Skipping test: kvm not available"); - return g_test_run(); - } -#else - g_test_message("Skipping test: Need s390x host to work properly"); + if (g_str_equal(qtest_get_arch(), "s390x") && !has_kvm) { + g_test_message("Skipping test: s390x host with KVM is required"); return g_test_run(); -#endif } tmpfs = mkdtemp(template); From 220ffd949bf2c157665905f7744bdbf201333e1f Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 2 Sep 2021 07:35:51 -0400 Subject: [PATCH 0626/1334] tests: bios-tables-test: use qtest_has_accel() API to register TCG only tests .. only if TCG is available Signed-off-by: Igor Mammedov Message-Id: <20210902113551.461632-16-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 873eb9c037..6e21a650d2 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -722,13 +722,6 @@ static void test_acpi_one(const char *params, test_data *data) char *args; bool use_uefi = data->uefi_fl1 && data->uefi_fl2; -#ifndef CONFIG_TCG - if (data->tcg_only) { - g_test_skip("TCG disabled, skipping ACPI tcg_only test"); - return; - } -#endif /* CONFIG_TCG */ - args = test_acpi_create_args(data, params, use_uefi); data->qts = qtest_init(args); test_acpi_load_tables(data, use_uefi); @@ -1544,6 +1537,7 @@ int main(int argc, char *argv[]) { const char *arch = qtest_get_arch(); const bool has_kvm = qtest_has_accel("kvm"); + const bool has_tcg = qtest_has_accel("tcg"); int ret; g_test_init(&argc, &argv, NULL); @@ -1590,7 +1584,6 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/smm-compat-nosmm", test_acpi_q35_tcg_smm_compat_nosmm); qtest_add_func("acpi/q35/nohpet", test_acpi_q35_tcg_nohpet); - qtest_add_func("acpi/q35/ivrs", test_acpi_q35_tcg_ivrs); qtest_add_func("acpi/piix4/dimmpxm", test_acpi_piix4_tcg_dimm_pxm); qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); qtest_add_func("acpi/piix4/acpihmat", test_acpi_piix4_tcg_acpi_hmat); @@ -1600,19 +1593,24 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/microvm/rtc", test_acpi_microvm_rtc_tcg); qtest_add_func("acpi/microvm/ioapic2", test_acpi_microvm_ioapic2_tcg); qtest_add_func("acpi/microvm/oem-fields", test_acpi_oem_fields_microvm); - if (strcmp(arch, "x86_64") == 0) { - qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); + if (has_tcg) { + qtest_add_func("acpi/q35/ivrs", test_acpi_q35_tcg_ivrs); + if (strcmp(arch, "x86_64") == 0) { + qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); + } } if (has_kvm) { qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic); qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar); } } else if (strcmp(arch, "aarch64") == 0) { - qtest_add_func("acpi/virt", test_acpi_virt_tcg); - qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); - qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); - qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb); - qtest_add_func("acpi/virt/oem-fields", test_acpi_oem_fields_virt); + if (has_tcg) { + qtest_add_func("acpi/virt", test_acpi_virt_tcg); + qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); + qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); + qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb); + qtest_add_func("acpi/virt/oem-fields", test_acpi_oem_fields_virt); + } } ret = g_test_run(); boot_sector_cleanup(disk); From c64038c93e171697058d96871f87a4438b613662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 14 Oct 2021 16:12:34 +0200 Subject: [PATCH 0627/1334] vdpa: Skip protected ram IOMMU mappings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the logic of commit 56918a126ae ("memory: Add RAM_PROTECTED flag to skip IOMMU mappings") with VFIO, skip memory sections inaccessible via normal mechanisms, including DMA. Signed-off-by: Eugenio Pérez Acked-by: Jason Wang Message-Id: <20211014141236.923287-2-eperezma@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella --- hw/virtio/vhost-vdpa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 47d7a5a23d..ea1aa71ad8 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -28,6 +28,7 @@ static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section) { return (!memory_region_is_ram(section->mr) && !memory_region_is_iommu(section->mr)) || + memory_region_is_protected(section->mr) || /* vhost-vDPA doesn't allow MMIO to be mapped */ memory_region_is_ram_device(section->mr) || /* From 032e4d686e9c6f8ae3b9206c720ae3614e28d8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 14 Oct 2021 16:12:35 +0200 Subject: [PATCH 0628/1334] vdpa: Add vhost_vdpa_section_end MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Abstract this operation, that will be reused when validating the region against the iova range that the device supports. Signed-off-by: Eugenio Pérez Acked-by: Jason Wang Message-Id: <20211014141236.923287-3-eperezma@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella --- hw/virtio/vhost-vdpa.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index ea1aa71ad8..be7c63b4ba 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -24,6 +24,19 @@ #include "trace.h" #include "qemu-common.h" +/* + * Return one past the end of the end of section. Be careful with uint64_t + * conversions! + */ +static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section) +{ + Int128 llend = int128_make64(section->offset_within_address_space); + llend = int128_add(llend, section->size); + llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + + return llend; +} + static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section) { return (!memory_region_is_ram(section->mr) && @@ -160,10 +173,7 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, } iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - llend = int128_make64(section->offset_within_address_space); - llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); - + llend = vhost_vdpa_section_end(section); if (int128_ge(int128_make64(iova), llend)) { return; } @@ -221,9 +231,7 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, } iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - llend = int128_make64(section->offset_within_address_space); - llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + llend = vhost_vdpa_section_end(section); trace_vhost_vdpa_listener_region_del(v, iova, int128_get64(llend)); From 013108b6e51e1c10b78859e2e091202364aa7a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 14 Oct 2021 16:12:36 +0200 Subject: [PATCH 0629/1334] vdpa: Check for iova range at mappings changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check vdpa device range before updating memory regions so we don't add any outside of it, and report the invalid change if any. Signed-off-by: Eugenio Pérez Message-Id: <20211014141236.923287-4-eperezma@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang Reviewed-by: Stefano Garzarella --- hw/virtio/trace-events | 1 + hw/virtio/vhost-vdpa.c | 62 ++++++++++++++++++++++++++-------- include/hw/virtio/vhost-vdpa.h | 2 ++ 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 8ed19e9d0c..650e521e35 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -52,6 +52,7 @@ vhost_vdpa_set_vring_call(void *dev, unsigned int index, int fd) "dev: %p index: vhost_vdpa_get_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRIx64 vhost_vdpa_set_owner(void *dev) "dev: %p" vhost_vdpa_vq_get_addr(void *dev, void *vq, uint64_t desc_user_addr, uint64_t avail_user_addr, uint64_t used_user_addr) "dev: %p vq: %p desc_user_addr: 0x%"PRIx64" avail_user_addr: 0x%"PRIx64" used_user_addr: 0x%"PRIx64 +vhost_vdpa_get_iova_range(void *dev, uint64_t first, uint64_t last) "dev: %p first: 0x%"PRIx64" last: 0x%"PRIx64 # virtio.c virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u" diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index be7c63b4ba..47c48f4ba8 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -37,20 +37,36 @@ static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section) return llend; } -static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section) +static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, + uint64_t iova_min, + uint64_t iova_max) { - return (!memory_region_is_ram(section->mr) && - !memory_region_is_iommu(section->mr)) || - memory_region_is_protected(section->mr) || - /* vhost-vDPA doesn't allow MMIO to be mapped */ - memory_region_is_ram_device(section->mr) || - /* - * Sizing an enabled 64-bit BAR can cause spurious mappings to - * addresses in the upper part of the 64-bit address space. These - * are never accessed by the CPU and beyond the address width of - * some IOMMU hardware. TODO: VDPA should tell us the IOMMU width. - */ - section->offset_within_address_space & (1ULL << 63); + Int128 llend; + + if ((!memory_region_is_ram(section->mr) && + !memory_region_is_iommu(section->mr)) || + memory_region_is_protected(section->mr) || + /* vhost-vDPA doesn't allow MMIO to be mapped */ + memory_region_is_ram_device(section->mr)) { + return true; + } + + if (section->offset_within_address_space < iova_min) { + error_report("RAM section out of device range (min=0x%" PRIx64 + ", addr=0x%" HWADDR_PRIx ")", + iova_min, section->offset_within_address_space); + return true; + } + + llend = vhost_vdpa_section_end(section); + if (int128_gt(llend, int128_make64(iova_max))) { + error_report("RAM section out of device range (max=0x%" PRIx64 + ", end addr=0x%" PRIx64 ")", + iova_max, int128_get64(llend)); + return true; + } + + return false; } static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, @@ -162,7 +178,8 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, void *vaddr; int ret; - if (vhost_vdpa_listener_skipped_section(section)) { + if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first, + v->iova_range.last)) { return; } @@ -220,7 +237,8 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, Int128 llend, llsize; int ret; - if (vhost_vdpa_listener_skipped_section(section)) { + if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first, + v->iova_range.last)) { return; } @@ -288,6 +306,19 @@ static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status) vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &s); } +static void vhost_vdpa_get_iova_range(struct vhost_vdpa *v) +{ + int ret = vhost_vdpa_call(v->dev, VHOST_VDPA_GET_IOVA_RANGE, + &v->iova_range); + if (ret != 0) { + v->iova_range.first = 0; + v->iova_range.last = UINT64_MAX; + } + + trace_vhost_vdpa_get_iova_range(v->dev, v->iova_range.first, + v->iova_range.last); +} + static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) { struct vhost_vdpa *v; @@ -300,6 +331,7 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) v->listener = vhost_vdpa_memory_listener; v->msg_type = VHOST_IOTLB_MSG_V2; + vhost_vdpa_get_iova_range(v); vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index a8963da2d9..c288cf7ecb 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -13,6 +13,7 @@ #define HW_VIRTIO_VHOST_VDPA_H #include "hw/virtio/virtio.h" +#include "standard-headers/linux/vhost_types.h" typedef struct VhostVDPAHostNotifier { MemoryRegion mr; @@ -24,6 +25,7 @@ typedef struct vhost_vdpa { uint32_t msg_type; bool iotlb_batch_begin_sent; MemoryListener listener; + struct vhost_vdpa_iova_range iova_range; struct vhost_dev *dev; VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX]; } VhostVDPA; From 819bbda81fd6a08bea79d56a6ca27092ec29719b Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Wed, 13 Oct 2021 15:17:54 -0400 Subject: [PATCH 0630/1334] virtio-iommu: Remove the non transitional name Remove the non transitional name for virtio iommu. Like other devices introduced after 1.0 spec, the virtio-iommu does not need it. Signed-off-by: Eric Auger Reported-by: Andrea Bolognani Reviewed-by: Cornelia Huck Message-Id: <20211013191755.767468-2-eric.auger@redhat.com> Reviewed-by: Jean-Philippe Brucker Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-iommu-pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 770c286be7..86fa4e6c28 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -100,7 +100,6 @@ static void virtio_iommu_pci_instance_init(Object *obj) static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = { .base_name = TYPE_VIRTIO_IOMMU_PCI, .generic_name = "virtio-iommu-pci", - .non_transitional_name = "virtio-iommu-pci-non-transitional", .instance_size = sizeof(VirtIOIOMMUPCI), .instance_init = virtio_iommu_pci_instance_init, .class_init = virtio_iommu_pci_class_init, From 19d20e910a586f503994acf590d5f41c314fa4c3 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Wed, 13 Oct 2021 15:17:55 -0400 Subject: [PATCH 0631/1334] virtio-iommu: Drop base_name and change generic_name Drop base_name and turn generic_name into "virtio-iommu-pci". This is more in line with other modern-only devices. Signed-off-by: Eric Auger Suggested-by: Cornelia Huck Reviewed-by: Cornelia Huck Message-Id: <20211013191755.767468-3-eric.auger@redhat.com> Reviewed-by: Jean-Philippe Brucker Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-iommu-pci.c | 3 +-- include/hw/virtio/virtio-iommu.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 86fa4e6c28..a160ae6b41 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -98,8 +98,7 @@ static void virtio_iommu_pci_instance_init(Object *obj) } static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = { - .base_name = TYPE_VIRTIO_IOMMU_PCI, - .generic_name = "virtio-iommu-pci", + .generic_name = TYPE_VIRTIO_IOMMU_PCI, .instance_size = sizeof(VirtIOIOMMUPCI), .instance_init = virtio_iommu_pci_instance_init, .class_init = virtio_iommu_pci_class_init, diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 273e35c04b..e2339e5b72 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -26,7 +26,7 @@ #include "qom/object.h" #define TYPE_VIRTIO_IOMMU "virtio-iommu-device" -#define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-device-base" +#define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-pci" OBJECT_DECLARE_SIMPLE_TYPE(VirtIOIOMMU, VIRTIO_IOMMU) #define TYPE_VIRTIO_IOMMU_MEMORY_REGION "virtio-iommu-memory-region" From 6889eb2d431ae962e3e083b57bff47cd573cb1c4 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 11 Oct 2021 22:10:47 +0200 Subject: [PATCH 0632/1334] libvhost-user: fix VHOST_USER_REM_MEM_REG skipping mmap_addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We end up not copying the mmap_addr of all existing regions, resulting in a SEGFAULT once we actually try to map/access anything within our memory regions. Fixes: 875b9fd97b34 ("Support individual region unmap in libvhost-user") Cc: qemu-stable@nongnu.org Cc: Michael S. Tsirkin Cc: Raphael Norwitz Cc: "Marc-André Lureau" Cc: Stefan Hajnoczi Cc: Paolo Bonzini Cc: Coiby Xu Signed-off-by: David Hildenbrand Message-Id: <20211011201047.62587-1-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Raphael Norwitz Reviewed-by: Stefan Hajnoczi --- subprojects/libvhost-user/libvhost-user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index bf09693255..787f4d2d4f 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -816,6 +816,7 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { shadow_regions[j].gpa = dev->regions[i].gpa; shadow_regions[j].size = dev->regions[i].size; shadow_regions[j].qva = dev->regions[i].qva; + shadow_regions[j].mmap_addr = dev->regions[i].mmap_addr; shadow_regions[j].mmap_offset = dev->regions[i].mmap_offset; j++; } else { From 821d28b88f850e5fbc66ee62bdd155eb2d474a29 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 12 Oct 2021 14:59:02 -0600 Subject: [PATCH 0633/1334] vhost-user-rng: Add vhost-user-rng implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a random number generator (RNG) backend that communicates with a vhost-user server to retrieve entropy. That way other VMM that comply with the vhost user protocl can use the same vhost-user daemon without having to write yet another RNG driver. Reviewed-by: Alex Bennée Signed-off-by: Mathieu Poirier Message-Id: <20211012205904.4106769-2-mathieu.poirier@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/Kconfig | 5 + hw/virtio/meson.build | 1 + hw/virtio/vhost-user-rng.c | 289 +++++++++++++++++++++++++++++ include/hw/virtio/vhost-user-rng.h | 33 ++++ 4 files changed, 328 insertions(+) create mode 100644 hw/virtio/vhost-user-rng.c create mode 100644 include/hw/virtio/vhost-user-rng.h diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index 35ab45e209..c144d42f9b 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -63,3 +63,8 @@ config VHOST_USER_I2C bool default y depends on VIRTIO && VHOST_USER + +config VHOST_USER_RNG + bool + default y + depends on VIRTIO && VHOST_USER diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index bc352a6009..ae6b2cde10 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -27,6 +27,7 @@ virtio_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu.c')) virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c')) virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_I2C'], if_true: files('vhost-user-i2c-pci.c')) +virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c')) virtio_pci_ss = ss.source_set() virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c')) diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c new file mode 100644 index 0000000000..209ee5bf9a --- /dev/null +++ b/hw/virtio/vhost-user-rng.c @@ -0,0 +1,289 @@ +/* + * Vhost-user RNG virtio device + * + * Copyright (c) 2021 Mathieu Poirier + * + * Implementation seriously tailored on vhost-user-i2c.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-rng.h" +#include "qemu/error-report.h" +#include "standard-headers/linux/virtio_ids.h" + +static void vu_rng_start(VirtIODevice *vdev) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + int i; + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return; + } + + ret = vhost_dev_enable_notifiers(&rng->vhost_dev, vdev); + if (ret < 0) { + error_report("Error enabling host notifiers: %d", -ret); + return; + } + + ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier: %d", -ret); + goto err_host_notifiers; + } + + rng->vhost_dev.acked_features = vdev->guest_features; + ret = vhost_dev_start(&rng->vhost_dev, vdev); + if (ret < 0) { + error_report("Error starting vhost-user-rng: %d", -ret); + goto err_guest_notifiers; + } + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < rng->vhost_dev.nvqs; i++) { + vhost_virtqueue_mask(&rng->vhost_dev, vdev, i, false); + } + + return; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); +} + +static void vu_rng_stop(VirtIODevice *vdev) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + + if (!k->set_guest_notifiers) { + return; + } + + vhost_dev_stop(&rng->vhost_dev, vdev); + + ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return; + } + + vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); +} + +static void vu_rng_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; + + if (!vdev->vm_running) { + should_start = false; + } + + if (rng->vhost_dev.started == should_start) { + return; + } + + if (should_start) { + vu_rng_start(vdev); + } else { + vu_rng_stop(vdev); + } +} + +static uint64_t vu_rng_get_features(VirtIODevice *vdev, + uint64_t requested_features, Error **errp) +{ + /* No feature bits used yet */ + return requested_features; +} + +static void vu_rng_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* + * Not normally called; it's the daemon that handles the queue; + * however virtio's cleanup path can call this. + */ +} + +static void vu_rng_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + + vhost_virtqueue_mask(&rng->vhost_dev, vdev, idx, mask); +} + +static bool vu_rng_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + + return vhost_virtqueue_pending(&rng->vhost_dev, idx); +} + +static void vu_rng_connect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + + if (rng->connected) { + return; + } + + rng->connected = true; + + /* restore vhost state */ + if (virtio_device_started(vdev, vdev->status)) { + vu_rng_start(vdev); + } +} + +static void vu_rng_disconnect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + + if (!rng->connected) { + return; + } + + rng->connected = false; + + if (rng->vhost_dev.started) { + vu_rng_stop(vdev); + } +} + +static void vu_rng_event(void *opaque, QEMUChrEvent event) +{ + DeviceState *dev = opaque; + + switch (event) { + case CHR_EVENT_OPENED: + vu_rng_connect(dev); + break; + case CHR_EVENT_CLOSED: + vu_rng_disconnect(dev); + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static void vu_rng_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRNG *rng = VHOST_USER_RNG(dev); + int ret; + + if (!rng->chardev.chr) { + error_setg(errp, "missing chardev"); + return; + } + + if (!vhost_user_init(&rng->vhost_user, &rng->chardev, errp)) { + return; + } + + virtio_init(vdev, "vhost-user-rng", VIRTIO_ID_RNG, 0); + + rng->req_vq = virtio_add_queue(vdev, 4, vu_rng_handle_output); + if (!rng->req_vq) { + error_setg_errno(errp, -1, "virtio_add_queue() failed"); + goto virtio_add_queue_failed; + } + + rng->vhost_dev.nvqs = 1; + rng->vhost_dev.vqs = g_new0(struct vhost_virtqueue, rng->vhost_dev.nvqs); + ret = vhost_dev_init(&rng->vhost_dev, &rng->vhost_user, + VHOST_BACKEND_TYPE_USER, 0, errp); + if (ret < 0) { + error_setg_errno(errp, -ret, "vhost_dev_init() failed"); + goto vhost_dev_init_failed; + } + + qemu_chr_fe_set_handlers(&rng->chardev, NULL, NULL, vu_rng_event, NULL, + dev, NULL, true); + + return; + +vhost_dev_init_failed: + virtio_delete_queue(rng->req_vq); +virtio_add_queue_failed: + virtio_cleanup(vdev); + vhost_user_cleanup(&rng->vhost_user); +} + +static void vu_rng_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRNG *rng = VHOST_USER_RNG(dev); + + vu_rng_set_status(vdev, 0); + + vhost_dev_cleanup(&rng->vhost_dev); + g_free(rng->vhost_dev.vqs); + rng->vhost_dev.vqs = NULL; + virtio_delete_queue(rng->req_vq); + virtio_cleanup(vdev); + vhost_user_cleanup(&rng->vhost_user); +} + +static const VMStateDescription vu_rng_vmstate = { + .name = "vhost-user-rng", + .unmigratable = 1, +}; + +static Property vu_rng_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserRNG, chardev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vu_rng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + device_class_set_props(dc, vu_rng_properties); + dc->vmsd = &vu_rng_vmstate; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + + vdc->realize = vu_rng_device_realize; + vdc->unrealize = vu_rng_device_unrealize; + vdc->get_features = vu_rng_get_features; + vdc->set_status = vu_rng_set_status; + vdc->guest_notifier_mask = vu_rng_guest_notifier_mask; + vdc->guest_notifier_pending = vu_rng_guest_notifier_pending; +} + +static const TypeInfo vu_rng_info = { + .name = TYPE_VHOST_USER_RNG, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VHostUserRNG), + .class_init = vu_rng_class_init, +}; + +static void vu_rng_register_types(void) +{ + type_register_static(&vu_rng_info); +} + +type_init(vu_rng_register_types) diff --git a/include/hw/virtio/vhost-user-rng.h b/include/hw/virtio/vhost-user-rng.h new file mode 100644 index 0000000000..071539996d --- /dev/null +++ b/include/hw/virtio/vhost-user-rng.h @@ -0,0 +1,33 @@ +/* + * Vhost-user RNG virtio device + * + * Copyright (c) 2021 Mathieu Poirier + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _QEMU_VHOST_USER_RNG_H +#define _QEMU_VHOST_USER_RNG_H + +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "chardev/char-fe.h" + +#define TYPE_VHOST_USER_RNG "vhost-user-rng" +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserRNG, VHOST_USER_RNG) + +struct VHostUserRNG { + /*< private >*/ + VirtIODevice parent; + CharBackend chardev; + struct vhost_virtqueue *vhost_vq; + struct vhost_dev vhost_dev; + VhostUserState vhost_user; + VirtQueue *req_vq; + bool connected; + + /*< public >*/ +}; + +#endif /* _QEMU_VHOST_USER_RNG_H */ From c7160fff7d48cead5d673ef2032b60c363c84e27 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 12 Oct 2021 14:59:03 -0600 Subject: [PATCH 0634/1334] vhost-user-rng-pci: Add vhost-user-rng-pci implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch provides a PCI bus interface to the vhost-user-rng backend. Reviewed-by: Alex Bennée Signed-off-by: Mathieu Poirier Message-Id: <20211012205904.4106769-3-mathieu.poirier@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/meson.build | 1 + hw/virtio/vhost-user-rng-pci.c | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 hw/virtio/vhost-user-rng-pci.c diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index ae6b2cde10..521f7d64a8 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -28,6 +28,7 @@ virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c')) virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_I2C'], if_true: files('vhost-user-i2c-pci.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c')) +virtio_ss.add(when: ['CONFIG_VHOST_USER_RNG', 'CONFIG_VIRTIO_PCI'], if_true: files('vhost-user-rng-pci.c')) virtio_pci_ss = ss.source_set() virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c')) diff --git a/hw/virtio/vhost-user-rng-pci.c b/hw/virtio/vhost-user-rng-pci.c new file mode 100644 index 0000000000..c83dc86813 --- /dev/null +++ b/hw/virtio/vhost-user-rng-pci.c @@ -0,0 +1,79 @@ +/* + * Vhost-user RNG virtio device PCI glue + * + * Copyright (c) 2021 Mathieu Poirier + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/vhost-user-rng.h" +#include "virtio-pci.h" + +struct VHostUserRNGPCI { + VirtIOPCIProxy parent_obj; + VHostUserRNG vdev; +}; + +typedef struct VHostUserRNGPCI VHostUserRNGPCI; + +#define TYPE_VHOST_USER_RNG_PCI "vhost-user-rng-pci-base" + +DECLARE_INSTANCE_CHECKER(VHostUserRNGPCI, VHOST_USER_RNG_PCI, + TYPE_VHOST_USER_RNG_PCI) + +static Property vhost_user_rng_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_user_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserRNGPCI *dev = VHOST_USER_RNG_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 1; + } + + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); +} + +static void vhost_user_rng_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vhost_user_rng_pci_realize; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + device_class_set_props(dc, vhost_user_rng_pci_properties); + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */ + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_OTHERS; +} + +static void vhost_user_rng_pci_instance_init(Object *obj) +{ + VHostUserRNGPCI *dev = VHOST_USER_RNG_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_RNG); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_rng_pci_info = { + .base_name = TYPE_VHOST_USER_RNG_PCI, + .non_transitional_name = "vhost-user-rng-pci", + .instance_size = sizeof(VHostUserRNGPCI), + .instance_init = vhost_user_rng_pci_instance_init, + .class_init = vhost_user_rng_pci_class_init, +}; + +static void vhost_user_rng_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_rng_pci_info); +} + +type_init(vhost_user_rng_pci_register); From c47d4fa0c8f0a28614cc0ce849dc9bbaa5c6878f Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 12 Oct 2021 14:59:04 -0600 Subject: [PATCH 0635/1334] docs: Add documentation for vhost based RNG implementation Add description and example for the vhost-user based RNG implementation. Signed-off-by: Mathieu Poirier Message-Id: <20211012205904.4106769-4-mathieu.poirier@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/system/device-emulation.rst | 1 + docs/system/devices/vhost-user-rng.rst | 39 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 docs/system/devices/vhost-user-rng.rst diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index 7afcfd8064..19944f526c 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -88,3 +88,4 @@ Emulated Devices devices/usb.rst devices/vhost-user.rst devices/virtio-pmem.rst + devices/vhost-user-rng.rst diff --git a/docs/system/devices/vhost-user-rng.rst b/docs/system/devices/vhost-user-rng.rst new file mode 100644 index 0000000000..a145d4105c --- /dev/null +++ b/docs/system/devices/vhost-user-rng.rst @@ -0,0 +1,39 @@ +QEMU vhost-user-rng - RNG emulation +=================================== + +Background +---------- + +What follows builds on the material presented in vhost-user.rst - it should +be reviewed before moving forward with the content in this file. + +Description +----------- + +The vhost-user-rng device implementation was designed to work with a random +number generator daemon such as the one found in the vhost-device crate of +the rust-vmm project available on github [1]. + +[1]. https://github.com/rust-vmm/vhost-device + +Examples +-------- + +The daemon should be started first: + +:: + + host# vhost-device-rng --socket-path=rng.sock -c 1 -m 512 -p 1000 + +The QEMU invocation needs to create a chardev socket the device can +use to communicate as well as share the guests memory over a memfd. + +:: + + host# qemu-system \ + -chardev socket,path=$(PATH)/rng.sock,id=rng0 \ + -device vhost-user-rng-pci,chardev=rng0 \ + -m 4096 \ + -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ + -numa node,memdev=mem \ + ... From a1ed9ef1de87c3e86ff68589604298ec90875a14 Mon Sep 17 00:00:00 2001 From: Xueming Li Date: Fri, 8 Oct 2021 16:02:15 +0800 Subject: [PATCH 0636/1334] vhost-user: fix duplicated notifier MR init In case of device resume after suspend, VQ notifier MR still valid. Duplicated registrations explode memory block list and slow down device resume. Fixes: 44866521bd6e ("vhost-user: support registering external host notifiers") Cc: tiwei.bie@intel.com Cc: qemu-stable@nongnu.org Cc: Yuwei Zhang Signed-off-by: Xueming Li Message-Id: <20211008080215.590292-1-xuemingl@nvidia.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 2c8556237f..bf6e50223c 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1526,8 +1526,9 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]", user, queue_idx); - memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name, - page_size, addr); + if (!n->mr.ram) /* Don't init again after suspend. */ + memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name, + page_size, addr); g_free(name); if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) { From 7fe7791e3f652ac31ef98dcc94a8f2a317ab846b Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 19 Oct 2021 09:15:31 +0200 Subject: [PATCH 0637/1334] failover: fix a regression introduced by JSON'ification of -device The hide_device helper can be called several times for the same devices as it shouldn't change any state and should only return an information. But not to rely anymore on QemuOpts we have introduced a new field to store the parameters of the device and don't allow to update it once it is done. And as the function is called several times, we ends with: warning: Cannot attach more than one primary device to 'virtio0' That is not only a warning as it prevents to hide the device and breaks failover. Fix that by checking the device id. Now, we fail only if the virtio-net device is really used by two different devices, for instance: -device virtio-net-pci,id=virtio0,failover=on,... \ -device vfio-pci,id=hostdev0,failover_pair_id=virtio0,... \ -device e1000e,id=e1000e0,failover_pair_id=virtio0,... \ will exit with: Cannot attach more than one primary device to 'virtio0': 'hostdev0' and 'e1000e0' Fixes: 259a10dbcb4f ("virtio-net: Store failover primary opts pointer locally") Cc: kwolf@redhat.com Signed-off-by: Laurent Vivier Message-Id: <20211019071532.682717-2-lvivier@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Kevin Wolf Reviewed-by: Kevin Wolf --- hw/net/virtio-net.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 09e173a558..83642c85b2 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3304,15 +3304,27 @@ static bool failover_hide_primary_device(DeviceListener *listener, return false; } + /* + * The hide helper can be called several times for a given device. + * Check there is only one primary for a virtio-net device but + * don't duplicate the qdict several times if it's called for the same + * device. + */ if (n->primary_opts) { - error_setg(errp, "Cannot attach more than one primary device to '%s'", - n->netclient_name); - return false; + const char *old, *new; + /* devices with failover_pair_id always have an id */ + old = qdict_get_str(n->primary_opts, "id"); + new = qdict_get_str(device_opts, "id"); + if (strcmp(old, new) != 0) { + error_setg(errp, "Cannot attach more than one primary device to " + "'%s': '%s' and '%s'", n->netclient_name, old, new); + return false; + } + } else { + n->primary_opts = qdict_clone_shallow(device_opts); + n->primary_opts_from_json = from_json; } - n->primary_opts = qdict_clone_shallow(device_opts); - n->primary_opts_from_json = from_json; - /* failover_primary_hidden is set during feature negotiation */ return qatomic_read(&n->failover_primary_hidden); } From 515efffc2fd0928317a0a1ec47b28a972c40ddad Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 19 Oct 2021 14:56:55 +0100 Subject: [PATCH 0638/1334] vhost-user-blk-test: pass vhost-user socket fds to QSD qemu-storage-daemon is launched with the vhost-user listen socket path. The path is first unlinked before opening the listen socket. This prevents stale UNIX domain socket files from stopping socket initialization. This behavior is undesirable in vhost-user-blk-test and the cause of a bug: There is a race condition in vhost-user-blk-test when QEMU launches before QSD. It connects to the old socket that QSD unlinks and the vhost-user connection is never serviced, resulting in a hang. Pass the listen socket fd to QSD to maintain listen socket continuity and prevent the lost connection. Fixes: 806952026df41939680abe92b329715b9b4e01cc ("test: new qTest case to test the vhost-user-blk-server") Cc: Raphael Norwitz Cc: Michael S. Tsirkin Cc: Thomas Huth Cc: Coiby Xu Signed-off-by: Stefan Hajnoczi Message-Id: <20211019135655.83067-1-stefanha@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/vhost-user-blk-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c index 6f108a1b62..62e670f39b 100644 --- a/tests/qtest/vhost-user-blk-test.c +++ b/tests/qtest/vhost-user-blk-test.c @@ -906,9 +906,9 @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances, img_path = drive_create(); g_string_append_printf(storage_daemon_command, "--blockdev driver=file,node-name=disk%d,filename=%s " - "--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s," + "--export type=vhost-user-blk,id=disk%d,addr.type=fd,addr.str=%d," "node-name=disk%i,writable=on,num-queues=%d ", - i, img_path, i, sock_path, i, num_queues); + i, img_path, i, fd, i, num_queues); g_string_append_printf(cmd_line, "-chardev socket,id=char%d,path=%s ", i + 1, sock_path); From bcfc906be47a88803307cd6b665dc4c26fdd6dd2 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 19 Oct 2021 09:15:32 +0200 Subject: [PATCH 0639/1334] qdev/qbus: remove failover specific code Commit f3a850565693 ("qdev/qbus: add hidden device support") has introduced a generic way to hide a device but it has modified qdev_device_add() to check a specific option of the failover device, "failover_pair_id", before calling the generic mechanism. It's not needed (and not generic) to do that in qdev_device_add() because this is also checked by the failover_hide_primary_device() function that uses the generic mechanism to hide the device. Cc: Jens Freimann Signed-off-by: Laurent Vivier Message-Id: <20211019071532.682717-3-lvivier@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Kevin Wolf --- hw/net/virtio-net.c | 12 +++++++++++- softmmu/qdev-monitor.c | 18 ++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 83642c85b2..3dd2896ff9 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3299,7 +3299,17 @@ static bool failover_hide_primary_device(DeviceListener *listener, if (!device_opts) { return false; } - standby_id = qdict_get_try_str(device_opts, "failover_pair_id"); + + if (!qdict_haskey(device_opts, "failover_pair_id")) { + return false; + } + + if (!qdict_haskey(device_opts, "id")) { + error_setg(errp, "Device with failover_pair_id needs to have id"); + return false; + } + + standby_id = qdict_get_str(device_opts, "failover_pair_id"); if (g_strcmp0(standby_id, n->netclient_name) != 0) { return false; } diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 89c473cb22..4851de51a5 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -639,19 +639,13 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } } - if (qdict_haskey(opts, "failover_pair_id")) { - if (!qdict_haskey(opts, "id")) { - error_setg(errp, "Device with failover_pair_id don't have id"); - return NULL; - } - if (qdev_should_hide_device(opts, from_json, errp)) { - if (bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); - } - return NULL; - } else if (*errp) { - return NULL; + if (qdev_should_hide_device(opts, from_json, errp)) { + if (bus && !qbus_is_hotpluggable(bus)) { + error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); } + return NULL; + } else if (*errp) { + return NULL; } if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) { From 71352aa95bf49d7a0ddab4e685147974c32ebc2c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 18 Oct 2021 17:14:28 -0400 Subject: [PATCH 0640/1334] rebuild-expected-aml.sh: allow partial target list Only rebuild AML for configured targets. Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/rebuild-expected-aml.sh | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/data/acpi/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh index fc78770544..dcf2e2f221 100755 --- a/tests/data/acpi/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -12,7 +12,7 @@ # This work is licensed under the terms of the GNU GPLv2. # See the COPYING.LIB file in the top-level directory. -qemu_bins="./qemu-system-x86_64 ./qemu-system-aarch64" +qemu_arches="x86_64 aarch64" if [ ! -e "tests/qtest/bios-tables-test" ]; then echo "Test: bios-tables-test is required! Run make check before this script." @@ -20,6 +20,26 @@ if [ ! -e "tests/qtest/bios-tables-test" ]; then exit 1; fi +if grep TARGET_DIRS= config-host.mak; then + for arch in $qemu_arches; do + if grep TARGET_DIRS= config-host.mak | grep "$arch"-softmmu; + then + qemu_bins="$qemu_bins ./qemu-system-$arch" + fi + done +else + echo "config-host.mak missing!" + echo "Run this script from the build directory." + exit 1; +fi + +if [ -z "$qemu_bins" ]; then + echo "Only the following architectures are currently supported: $qemu_arches" + echo "None of these configured!" + echo "To fix, run configure --target-list=x86_64-softmmu,aarch64-softmmu" + exit 1; +fi + for qemu in $qemu_bins; do if [ ! -e $qemu ]; then echo "Run 'make' to build the following QEMU executables: $qemu_bins" From 81d7228647e0bd17d4f4f0434b8ed410fe5f7497 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 19 Oct 2021 06:07:45 -0400 Subject: [PATCH 0641/1334] bios-tables-test: don't disassemble empty files A recommended way to populate new tables is to have an empty expected file. In this case, attempts to disassemble will fail but it is useful to disassemble the actual files. Detect and skip decompile step in this case. Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 6e21a650d2..798f68c737 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -271,19 +271,28 @@ static void dump_aml_files(test_data *data, bool rebuild) } } +static bool create_tmp_asl(AcpiSdtTable *sdt) +{ + GError *error = NULL; + gint fd; + + fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error); + g_assert_no_error(error); + close(fd); + + return false; +} + static bool load_asl(GArray *sdts, AcpiSdtTable *sdt) { AcpiSdtTable *temp; GError *error = NULL; GString *command_line = g_string_new(iasl); - gint fd; gchar *out, *out_err; gboolean ret; int i; - fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error); - g_assert_no_error(error); - close(fd); + create_tmp_asl(sdt); /* build command line */ g_string_append_printf(command_line, " -p %s ", sdt->asl_file); @@ -463,11 +472,20 @@ static void test_acpi_asl(test_data *data) err = load_asl(data->tables, sdt); asl = normalize_asl(sdt->asl); - exp_err = load_asl(exp_data.tables, exp_sdt); - exp_asl = normalize_asl(exp_sdt->asl); + /* + * If expected file is empty - it's likely that it was a stub just + * created for step 1 above: we do want to decompile the actual one. + */ + if (exp_sdt->aml_len) { + exp_err = load_asl(exp_data.tables, exp_sdt); + exp_asl = normalize_asl(exp_sdt->asl); + } else { + exp_err = create_tmp_asl(exp_sdt); + exp_asl = g_string_new(""); + } /* TODO: check for warnings */ - g_assert(!err || exp_err); + g_assert(!err || exp_err || !exp_sdt->aml_len); if (g_strcmp0(asl->str, exp_asl->str)) { sdt->tmp_files_retain = true; From 7327813d17086fadabe3b27ba2d9737c8a33c5b0 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:51 +0800 Subject: [PATCH 0642/1334] vhost-vdpa: open device fd in net_init_vhost_vdpa() This patch switches to open device fd in net_init_vhost_vpda(). This is used to prepare for the multiqueue support. Reviewed-by: Stefano Garzarella Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-2-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 6dc68d8677..fd4ff5a0fb 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -170,24 +170,19 @@ static NetClientInfo net_vhost_vdpa_info = { }; static int net_vhost_vdpa_init(NetClientState *peer, const char *device, - const char *name, const char *vhostdev) + const char *name, int vdpa_device_fd) { NetClientState *nc = NULL; VhostVDPAState *s; - int vdpa_device_fd = -1; int ret = 0; assert(name); nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); s = DO_UPCAST(VhostVDPAState, nc, nc); - vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); - if (vdpa_device_fd == -1) { - return -errno; - } + s->vhost_vdpa.device_fd = vdpa_device_fd; ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); if (ret) { - qemu_close(vdpa_device_fd); qemu_del_net_client(nc); } return ret; @@ -197,8 +192,20 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { const NetdevVhostVDPAOptions *opts; + int vdpa_device_fd, ret; assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); opts = &netdev->u.vhost_vdpa; - return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); + + vdpa_device_fd = qemu_open_old(opts->vhostdev, O_RDWR); + if (vdpa_device_fd == -1) { + return -errno; + } + + ret = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, vdpa_device_fd); + if (ret) { + qemu_close(vdpa_device_fd); + } + + return ret; } From 4d191cfdc7de958d06f821173f721c56bc131620 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:52 +0800 Subject: [PATCH 0643/1334] vhost-vdpa: classify one time request Vhost-vdpa uses one device multiqueue queue (pairs) model. So we need to classify the one time request (e.g SET_OWNER) and make sure those request were only called once per device. This is used for multiqueue support. Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-3-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 53 ++++++++++++++++++++++++++++++---- include/hw/virtio/vhost-vdpa.h | 1 + 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 47c48f4ba8..ceb53613ba 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -319,6 +319,13 @@ static void vhost_vdpa_get_iova_range(struct vhost_vdpa *v) v->iova_range.last); } +static bool vhost_vdpa_one_time_request(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + + return v->index != 0; +} + static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) { struct vhost_vdpa *v; @@ -332,6 +339,11 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) v->msg_type = VHOST_IOTLB_MSG_V2; vhost_vdpa_get_iova_range(v); + + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); @@ -442,6 +454,10 @@ static int vhost_vdpa_memslots_limit(struct vhost_dev *dev) static int vhost_vdpa_set_mem_table(struct vhost_dev *dev, struct vhost_memory *mem) { + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + trace_vhost_vdpa_set_mem_table(dev, mem->nregions, mem->padding); if (trace_event_get_state_backends(TRACE_VHOST_VDPA_SET_MEM_TABLE) && trace_event_get_state_backends(TRACE_VHOST_VDPA_DUMP_REGIONS)) { @@ -465,6 +481,11 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev, uint64_t features) { int ret; + + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + trace_vhost_vdpa_set_features(dev, features); ret = vhost_vdpa_call(dev, VHOST_SET_FEATURES, &features); uint8_t status = 0; @@ -489,9 +510,12 @@ static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) } features &= f; - r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); - if (r) { - return -EFAULT; + + if (vhost_vdpa_one_time_request(dev)) { + r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); + if (r) { + return -EFAULT; + } } dev->backend_cap = features; @@ -600,11 +624,21 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) { struct vhost_vdpa *v = dev->opaque; trace_vhost_vdpa_dev_start(dev, started); + + if (started) { + vhost_vdpa_host_notifiers_init(dev); + vhost_vdpa_set_vring_ready(dev); + } else { + vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); + } + + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + if (started) { uint8_t status = 0; memory_listener_register(&v->listener, &address_space_memory); - vhost_vdpa_host_notifiers_init(dev); - vhost_vdpa_set_vring_ready(dev); vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &status); @@ -613,7 +647,6 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) vhost_vdpa_reset_device(dev); vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); - vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); memory_listener_unregister(&v->listener); return 0; @@ -623,6 +656,10 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, struct vhost_log *log) { + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + trace_vhost_vdpa_set_log_base(dev, base, log->size, log->refcnt, log->fd, log->log); return vhost_vdpa_call(dev, VHOST_SET_LOG_BASE, &base); @@ -688,6 +725,10 @@ static int vhost_vdpa_get_features(struct vhost_dev *dev, static int vhost_vdpa_set_owner(struct vhost_dev *dev) { + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + trace_vhost_vdpa_set_owner(dev); return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); } diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index c288cf7ecb..3ce79a646d 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -22,6 +22,7 @@ typedef struct VhostVDPAHostNotifier { typedef struct vhost_vdpa { int device_fd; + int index; uint32_t msg_type; bool iotlb_batch_begin_sent; MemoryListener listener; From 353244d8b96767659e6c95c0c87dfe4372fe8c12 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:53 +0800 Subject: [PATCH 0644/1334] vhost-vdpa: prepare for the multiqueue support Unlike vhost-kernel, vhost-vdpa adapts a single device multiqueue model. So we need to simply use virtqueue index as the vhost virtqueue index. This is a must for multiqueue to work for vhost-vdpa. Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-4-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index ceb53613ba..8948fd316b 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -546,8 +546,8 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx) { assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); - trace_vhost_vdpa_get_vq_index(dev, idx, idx - dev->vq_index); - return idx - dev->vq_index; + trace_vhost_vdpa_get_vq_index(dev, idx, idx); + return idx; } static int vhost_vdpa_set_vring_ready(struct vhost_dev *dev) From 654790b65b8435a7d409b1873b566e8db9e91b9b Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:54 +0800 Subject: [PATCH 0645/1334] vhost-vdpa: let net_vhost_vdpa_init() returns NetClientState * This patch switches to let net_vhost_vdpa_init() to return NetClientState *. This is used for the callers to allocate multiqueue NetClientState for multiqueue support. Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-5-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index fd4ff5a0fb..151f60184d 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -169,8 +169,10 @@ static NetClientInfo net_vhost_vdpa_info = { .check_peer_type = vhost_vdpa_check_peer_type, }; -static int net_vhost_vdpa_init(NetClientState *peer, const char *device, - const char *name, int vdpa_device_fd) +static NetClientState *net_vhost_vdpa_init(NetClientState *peer, + const char *device, + const char *name, + int vdpa_device_fd) { NetClientState *nc = NULL; VhostVDPAState *s; @@ -184,15 +186,17 @@ static int net_vhost_vdpa_init(NetClientState *peer, const char *device, ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); if (ret) { qemu_del_net_client(nc); + return NULL; } - return ret; + return nc; } int net_init_vhost_vdpa(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { const NetdevVhostVDPAOptions *opts; - int vdpa_device_fd, ret; + int vdpa_device_fd; + NetClientState *nc; assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); opts = &netdev->u.vhost_vdpa; @@ -202,10 +206,11 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, return -errno; } - ret = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, vdpa_device_fd); - if (ret) { + nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, vdpa_device_fd); + if (!nc) { qemu_close(vdpa_device_fd); + return -1; } - return ret; + return 0; } From 2f849dbdb2ab2c70715520a8129524f197b9a8ba Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:55 +0800 Subject: [PATCH 0646/1334] net: introduce control client This patch introduces a boolean for the device has control queue which can accepts control command via network queue. The first user would be the control virtqueue support for vhost. Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-6-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/net/net.h | 5 +++++ net/net.c | 24 +++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/include/net/net.h b/include/net/net.h index 986288eb07..523136c7ac 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -105,6 +105,7 @@ struct NetClientState { int vnet_hdr_len; bool is_netdev; bool do_not_pad; /* do not pad to the minimum ethernet frame length */ + bool is_datapath; QTAILQ_HEAD(, NetFilterState) filters; }; @@ -136,6 +137,10 @@ NetClientState *qemu_new_net_client(NetClientInfo *info, NetClientState *peer, const char *model, const char *name); +NetClientState *qemu_new_net_control_client(NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name); NICState *qemu_new_nic(NetClientInfo *info, NICConf *conf, const char *model, diff --git a/net/net.c b/net/net.c index 52c99196c6..f0d14dbfc1 100644 --- a/net/net.c +++ b/net/net.c @@ -239,7 +239,8 @@ static void qemu_net_client_setup(NetClientState *nc, NetClientState *peer, const char *model, const char *name, - NetClientDestructor *destructor) + NetClientDestructor *destructor, + bool is_datapath) { nc->info = info; nc->model = g_strdup(model); @@ -258,6 +259,7 @@ static void qemu_net_client_setup(NetClientState *nc, nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc); nc->destructor = destructor; + nc->is_datapath = is_datapath; QTAILQ_INIT(&nc->filters); } @@ -272,7 +274,23 @@ NetClientState *qemu_new_net_client(NetClientInfo *info, nc = g_malloc0(info->size); qemu_net_client_setup(nc, info, peer, model, name, - qemu_net_client_destructor); + qemu_net_client_destructor, true); + + return nc; +} + +NetClientState *qemu_new_net_control_client(NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name) +{ + NetClientState *nc; + + assert(info->size >= sizeof(NetClientState)); + + nc = g_malloc0(info->size); + qemu_net_client_setup(nc, info, peer, model, name, + qemu_net_client_destructor, false); return nc; } @@ -297,7 +315,7 @@ NICState *qemu_new_nic(NetClientInfo *info, for (i = 0; i < queues; i++) { qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name, - NULL); + NULL, true); nic->ncs[i].queue_index = i; } From 05ba3f63d174a716b42aa31c9af5d0ef64fff515 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:56 +0800 Subject: [PATCH 0647/1334] vhost-net: control virtqueue support We assume there's no cvq in the past, this is not true when we need control virtqueue support for vhost-user backends. So this patch implements the control virtqueue support for vhost-net. As datapath, the control virtqueue is also required to be coupled with the NetClientState. The vhost_net_start/stop() are tweaked to accept the number of datapath queue pairs plus the the number of control virtqueue for us to start and stop the vhost device. Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-7-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/vhost_net-stub.c | 4 ++-- hw/net/vhost_net.c | 43 ++++++++++++++++++++++++++++++----------- hw/net/virtio-net.c | 4 ++-- include/net/vhost_net.h | 6 ++++-- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index a7f4252630..89d71cfb8e 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -33,13 +33,13 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, - int total_queues) + int data_queue_pairs, int cvq) { return -ENOSYS; } void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, - int total_queues) + int data_queue_pairs, int cvq) { } diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 386ec2eaa2..e1e9d1ec89 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -315,11 +315,14 @@ static void vhost_net_stop_one(struct vhost_net *net, } int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, - int total_queues) + int data_queue_pairs, int cvq) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); + int total_notifiers = data_queue_pairs * 2 + cvq; + VirtIONet *n = VIRTIO_NET(dev); + int nvhosts = data_queue_pairs + cvq; struct vhost_net *net; int r, e, i; NetClientState *peer; @@ -329,9 +332,14 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, return -ENOSYS; } - for (i = 0; i < total_queues; i++) { + for (i = 0; i < nvhosts; i++) { + + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { /* Control Virtqueue */ + peer = qemu_get_peer(ncs, n->max_queues); + } - peer = qemu_get_peer(ncs, i); net = get_vhost_net(peer); vhost_net_set_vq_index(net, i * 2); @@ -344,14 +352,18 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } } - r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); + r = k->set_guest_notifiers(qbus->parent, total_notifiers, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); goto err; } - for (i = 0; i < total_queues; i++) { - peer = qemu_get_peer(ncs, i); + for (i = 0; i < nvhosts; i++) { + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { + peer = qemu_get_peer(ncs, n->max_queues); + } r = vhost_net_start_one(get_vhost_net(peer), dev); if (r < 0) { @@ -375,7 +387,7 @@ err_start: peer = qemu_get_peer(ncs , i); vhost_net_stop_one(get_vhost_net(peer), dev); } - e = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); + e = k->set_guest_notifiers(qbus->parent, total_notifiers, false); if (e < 0) { fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); fflush(stderr); @@ -385,18 +397,27 @@ err: } void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, - int total_queues) + int data_queue_pairs, int cvq) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); + VirtIONet *n = VIRTIO_NET(dev); + NetClientState *peer; + int total_notifiers = data_queue_pairs * 2 + cvq; + int nvhosts = data_queue_pairs + cvq; int i, r; - for (i = 0; i < total_queues; i++) { - vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); + for (i = 0; i < nvhosts; i++) { + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { + peer = qemu_get_peer(ncs, n->max_queues); + } + vhost_net_stop_one(get_vhost_net(peer), dev); } - r = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); + r = k->set_guest_notifiers(qbus->parent, total_notifiers, false); if (r < 0) { fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); fflush(stderr); diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 3dd2896ff9..5ee6729662 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -285,14 +285,14 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) } n->vhost_started = 1; - r = vhost_net_start(vdev, n->nic->ncs, queues); + r = vhost_net_start(vdev, n->nic->ncs, queues, 0); if (r < 0) { error_report("unable to start vhost net: %d: " "falling back on userspace virtio", -r); n->vhost_started = 0; } } else { - vhost_net_stop(vdev, n->nic->ncs, queues); + vhost_net_stop(vdev, n->nic->ncs, queues, 0); n->vhost_started = 0; } } diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index fba40cf695..387e913e4e 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -21,8 +21,10 @@ typedef struct VhostNetOptions { uint64_t vhost_net_get_max_queues(VHostNetState *net); struct vhost_net *vhost_net_init(VhostNetOptions *options); -int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues); -void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, int total_queues); +int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, + int data_queue_pairs, int cvq); +void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, + int data_queue_pairs, int cvq); void vhost_net_cleanup(VHostNetState *net); From 441537f1ce0153978b4c9ee1cc4d4152147aa16f Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:57 +0800 Subject: [PATCH 0648/1334] virtio-net: use "queue_pairs" instead of "queues" when possible Most of the time, "queues" really means queue pairs. So this patch switch to use "queue_pairs" to avoid confusion. Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-8-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/vhost_net.c | 6 +- hw/net/virtio-net.c | 150 ++++++++++++++++----------------- include/hw/virtio/virtio-net.h | 4 +- 3 files changed, 80 insertions(+), 80 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index e1e9d1ec89..2b594b4642 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -337,7 +337,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, if (i < data_queue_pairs) { peer = qemu_get_peer(ncs, i); } else { /* Control Virtqueue */ - peer = qemu_get_peer(ncs, n->max_queues); + peer = qemu_get_peer(ncs, n->max_queue_pairs); } net = get_vhost_net(peer); @@ -362,7 +362,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, if (i < data_queue_pairs) { peer = qemu_get_peer(ncs, i); } else { - peer = qemu_get_peer(ncs, n->max_queues); + peer = qemu_get_peer(ncs, n->max_queue_pairs); } r = vhost_net_start_one(get_vhost_net(peer), dev); @@ -412,7 +412,7 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, if (i < data_queue_pairs) { peer = qemu_get_peer(ncs, i); } else { - peer = qemu_get_peer(ncs, n->max_queues); + peer = qemu_get_peer(ncs, n->max_queue_pairs); } vhost_net_stop_one(get_vhost_net(peer), dev); } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 5ee6729662..7594f7ea92 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -54,7 +54,7 @@ #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256 -/* for now, only allow larger queues; with virtio-1, guest can downsize */ +/* for now, only allow larger queue_pairs; with virtio-1, guest can downsize */ #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE @@ -131,7 +131,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) int ret = 0; memset(&netcfg, 0 , sizeof(struct virtio_net_config)); virtio_stw_p(vdev, &netcfg.status, n->status); - virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); + virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queue_pairs); virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu); memcpy(netcfg.mac, n->mac, ETH_ALEN); virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed); @@ -243,7 +243,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) { VirtIODevice *vdev = VIRTIO_DEVICE(n); NetClientState *nc = qemu_get_queue(n->nic); - int queues = n->multiqueue ? n->max_queues : 1; + int queue_pairs = n->multiqueue ? n->max_queue_pairs : 1; if (!get_vhost_net(nc->peer)) { return; @@ -266,7 +266,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) /* Any packets outstanding? Purge them to avoid touching rings * when vhost is running. */ - for (i = 0; i < queues; i++) { + for (i = 0; i < queue_pairs; i++) { NetClientState *qnc = qemu_get_subqueue(n->nic, i); /* Purge both directions: TX and RX. */ @@ -285,14 +285,14 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) } n->vhost_started = 1; - r = vhost_net_start(vdev, n->nic->ncs, queues, 0); + r = vhost_net_start(vdev, n->nic->ncs, queue_pairs, 0); if (r < 0) { error_report("unable to start vhost net: %d: " "falling back on userspace virtio", -r); n->vhost_started = 0; } } else { - vhost_net_stop(vdev, n->nic->ncs, queues, 0); + vhost_net_stop(vdev, n->nic->ncs, queue_pairs, 0); n->vhost_started = 0; } } @@ -309,11 +309,11 @@ static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev, } static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs, - int queues, bool enable) + int queue_pairs, bool enable) { int i; - for (i = 0; i < queues; i++) { + for (i = 0; i < queue_pairs; i++) { if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 && enable) { while (--i >= 0) { @@ -330,7 +330,7 @@ static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs, static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) { VirtIODevice *vdev = VIRTIO_DEVICE(n); - int queues = n->multiqueue ? n->max_queues : 1; + int queue_pairs = n->multiqueue ? n->max_queue_pairs : 1; if (virtio_net_started(n, status)) { /* Before using the device, we tell the network backend about the @@ -339,14 +339,14 @@ static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) * virtio-net code. */ n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs, - queues, true); + queue_pairs, true); } else if (virtio_net_started(n, vdev->status)) { /* After using the device, we need to reset the network backend to * the default (guest native endianness), otherwise the guest may * lose network connectivity if it is rebooted into a different * endianness. */ - virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false); + virtio_net_set_vnet_endian(vdev, n->nic->ncs, queue_pairs, false); } } @@ -368,12 +368,12 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) virtio_net_vnet_endian_status(n, status); virtio_net_vhost_status(n, status); - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { NetClientState *ncs = qemu_get_subqueue(n->nic, i); bool queue_started; q = &n->vqs[i]; - if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { + if ((!n->multiqueue && i != 0) || i >= n->curr_queue_pairs) { queue_status = 0; } else { queue_status = status; @@ -540,7 +540,7 @@ static void virtio_net_reset(VirtIODevice *vdev) n->nouni = 0; n->nobcast = 0; /* multiqueue is disabled by default */ - n->curr_queues = 1; + n->curr_queue_pairs = 1; timer_del(n->announce_timer.tm); n->announce_timer.round = 0; n->status &= ~VIRTIO_NET_S_ANNOUNCE; @@ -556,7 +556,7 @@ static void virtio_net_reset(VirtIODevice *vdev) memset(n->vlans, 0, MAX_VLAN >> 3); /* Flush any async TX */ - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { NetClientState *nc = qemu_get_subqueue(n->nic, i); if (nc->peer) { @@ -610,7 +610,7 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, sizeof(struct virtio_net_hdr); } - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { nc = qemu_get_subqueue(n->nic, i); if (peer_has_vnet_hdr(n) && @@ -655,7 +655,7 @@ static int peer_attach(VirtIONet *n, int index) return 0; } - if (n->max_queues == 1) { + if (n->max_queue_pairs == 1) { return 0; } @@ -681,7 +681,7 @@ static int peer_detach(VirtIONet *n, int index) return tap_disable(nc->peer); } -static void virtio_net_set_queues(VirtIONet *n) +static void virtio_net_set_queue_pairs(VirtIONet *n) { int i; int r; @@ -690,8 +690,8 @@ static void virtio_net_set_queues(VirtIONet *n) return; } - for (i = 0; i < n->max_queues; i++) { - if (i < n->curr_queues) { + for (i = 0; i < n->max_queue_pairs; i++) { + if (i < n->curr_queue_pairs) { r = peer_attach(n, i); assert(!r); } else { @@ -905,7 +905,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) virtio_net_apply_guest_offloads(n); } - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { NetClientState *nc = qemu_get_subqueue(n->nic, i); if (!get_vhost_net(nc->peer)) { @@ -1232,7 +1232,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, VirtIODevice *vdev = VIRTIO_DEVICE(n); struct virtio_net_rss_config cfg; size_t s, offset = 0, size_get; - uint16_t queues, i; + uint16_t queue_pairs, i; struct { uint16_t us; uint8_t b; @@ -1274,7 +1274,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, } n->rss_data.default_queue = do_rss ? virtio_lduw_p(vdev, &cfg.unclassified_queue) : 0; - if (n->rss_data.default_queue >= n->max_queues) { + if (n->rss_data.default_queue >= n->max_queue_pairs) { err_msg = "Invalid default queue"; err_value = n->rss_data.default_queue; goto error; @@ -1303,14 +1303,14 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, size_get = sizeof(temp); s = iov_to_buf(iov, iov_cnt, offset, &temp, size_get); if (s != size_get) { - err_msg = "Can't get queues"; + err_msg = "Can't get queue_pairs"; err_value = (uint32_t)s; goto error; } - queues = do_rss ? virtio_lduw_p(vdev, &temp.us) : n->curr_queues; - if (queues == 0 || queues > n->max_queues) { - err_msg = "Invalid number of queues"; - err_value = queues; + queue_pairs = do_rss ? virtio_lduw_p(vdev, &temp.us) : n->curr_queue_pairs; + if (queue_pairs == 0 || queue_pairs > n->max_queue_pairs) { + err_msg = "Invalid number of queue_pairs"; + err_value = queue_pairs; goto error; } if (temp.b > VIRTIO_NET_RSS_MAX_KEY_SIZE) { @@ -1325,7 +1325,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, } if (!temp.b && !n->rss_data.hash_types) { virtio_net_disable_rss(n); - return queues; + return queue_pairs; } offset += size_get; size_get = temp.b; @@ -1358,7 +1358,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, trace_virtio_net_rss_enable(n->rss_data.hash_types, n->rss_data.indirections_len, temp.b); - return queues; + return queue_pairs; error: trace_virtio_net_rss_error(err_msg, err_value); virtio_net_disable_rss(n); @@ -1369,15 +1369,15 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { VirtIODevice *vdev = VIRTIO_DEVICE(n); - uint16_t queues; + uint16_t queue_pairs; virtio_net_disable_rss(n); if (cmd == VIRTIO_NET_CTRL_MQ_HASH_CONFIG) { - queues = virtio_net_handle_rss(n, iov, iov_cnt, false); - return queues ? VIRTIO_NET_OK : VIRTIO_NET_ERR; + queue_pairs = virtio_net_handle_rss(n, iov, iov_cnt, false); + return queue_pairs ? VIRTIO_NET_OK : VIRTIO_NET_ERR; } if (cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) { - queues = virtio_net_handle_rss(n, iov, iov_cnt, true); + queue_pairs = virtio_net_handle_rss(n, iov, iov_cnt, true); } else if (cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { struct virtio_net_ctrl_mq mq; size_t s; @@ -1388,24 +1388,24 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, if (s != sizeof(mq)) { return VIRTIO_NET_ERR; } - queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); + queue_pairs = virtio_lduw_p(vdev, &mq.virtqueue_pairs); } else { return VIRTIO_NET_ERR; } - if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || - queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || - queues > n->max_queues || + if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || + queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || + queue_pairs > n->max_queue_pairs || !n->multiqueue) { return VIRTIO_NET_ERR; } - n->curr_queues = queues; - /* stop the backend before changing the number of queues to avoid handling a + n->curr_queue_pairs = queue_pairs; + /* stop the backend before changing the number of queue_pairs to avoid handling a * disabled queue */ virtio_net_set_status(vdev, vdev->status); - virtio_net_set_queues(n); + virtio_net_set_queue_pairs(n); return VIRTIO_NET_OK; } @@ -1483,7 +1483,7 @@ static bool virtio_net_can_receive(NetClientState *nc) return false; } - if (nc->queue_index >= n->curr_queues) { + if (nc->queue_index >= n->curr_queue_pairs) { return false; } @@ -2763,11 +2763,11 @@ static void virtio_net_del_queue(VirtIONet *n, int index) virtio_del_queue(vdev, index * 2 + 1); } -static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues) +static void virtio_net_change_num_queue_pairs(VirtIONet *n, int new_max_queue_pairs) { VirtIODevice *vdev = VIRTIO_DEVICE(n); int old_num_queues = virtio_get_num_queues(vdev); - int new_num_queues = new_max_queues * 2 + 1; + int new_num_queues = new_max_queue_pairs * 2 + 1; int i; assert(old_num_queues >= 3); @@ -2800,12 +2800,12 @@ static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues) static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) { - int max = multiqueue ? n->max_queues : 1; + int max = multiqueue ? n->max_queue_pairs : 1; n->multiqueue = multiqueue; - virtio_net_change_num_queues(n, max); + virtio_net_change_num_queue_pairs(n, max); - virtio_net_set_queues(n); + virtio_net_set_queue_pairs(n); } static int virtio_net_post_load_device(void *opaque, int version_id) @@ -2838,7 +2838,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id) */ n->saved_guest_offloads = n->curr_guest_offloads; - virtio_net_set_queues(n); + virtio_net_set_queue_pairs(n); /* Find the first multicast entry in the saved MAC filter */ for (i = 0; i < n->mac_table.in_use; i++) { @@ -2851,7 +2851,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id) /* nc.link_down can't be migrated, so infer link_down according * to link status bit in n->status */ link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { qemu_get_subqueue(n->nic, i)->link_down = link_down; } @@ -2916,9 +2916,9 @@ static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = { }, }; -static bool max_queues_gt_1(void *opaque, int version_id) +static bool max_queue_pairs_gt_1(void *opaque, int version_id) { - return VIRTIO_NET(opaque)->max_queues > 1; + return VIRTIO_NET(opaque)->max_queue_pairs > 1; } static bool has_ctrl_guest_offloads(void *opaque, int version_id) @@ -2943,13 +2943,13 @@ static bool mac_table_doesnt_fit(void *opaque, int version_id) struct VirtIONetMigTmp { VirtIONet *parent; VirtIONetQueue *vqs_1; - uint16_t curr_queues_1; + uint16_t curr_queue_pairs_1; uint8_t has_ufo; uint32_t has_vnet_hdr; }; /* The 2nd and subsequent tx_waiting flags are loaded later than - * the 1st entry in the queues and only if there's more than one + * the 1st entry in the queue_pairs and only if there's more than one * entry. We use the tmp mechanism to calculate a temporary * pointer and count and also validate the count. */ @@ -2959,9 +2959,9 @@ static int virtio_net_tx_waiting_pre_save(void *opaque) struct VirtIONetMigTmp *tmp = opaque; tmp->vqs_1 = tmp->parent->vqs + 1; - tmp->curr_queues_1 = tmp->parent->curr_queues - 1; - if (tmp->parent->curr_queues == 0) { - tmp->curr_queues_1 = 0; + tmp->curr_queue_pairs_1 = tmp->parent->curr_queue_pairs - 1; + if (tmp->parent->curr_queue_pairs == 0) { + tmp->curr_queue_pairs_1 = 0; } return 0; @@ -2974,9 +2974,9 @@ static int virtio_net_tx_waiting_pre_load(void *opaque) /* Reuse the pointer setup from save */ virtio_net_tx_waiting_pre_save(opaque); - if (tmp->parent->curr_queues > tmp->parent->max_queues) { - error_report("virtio-net: curr_queues %x > max_queues %x", - tmp->parent->curr_queues, tmp->parent->max_queues); + if (tmp->parent->curr_queue_pairs > tmp->parent->max_queue_pairs) { + error_report("virtio-net: curr_queue_pairs %x > max_queue_pairs %x", + tmp->parent->curr_queue_pairs, tmp->parent->max_queue_pairs); return -EINVAL; } @@ -2990,7 +2990,7 @@ static const VMStateDescription vmstate_virtio_net_tx_waiting = { .pre_save = virtio_net_tx_waiting_pre_save, .fields = (VMStateField[]) { VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp, - curr_queues_1, + curr_queue_pairs_1, vmstate_virtio_net_queue_tx_waiting, struct VirtIONetQueue), VMSTATE_END_OF_LIST() @@ -3132,9 +3132,9 @@ static const VMStateDescription vmstate_virtio_net_device = { VMSTATE_UINT8(nobcast, VirtIONet), VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, vmstate_virtio_net_has_ufo), - VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0, + VMSTATE_SINGLE_TEST(max_queue_pairs, VirtIONet, max_queue_pairs_gt_1, 0, vmstate_info_uint16_equal, uint16_t), - VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1), + VMSTATE_UINT16_TEST(curr_queue_pairs, VirtIONet, max_queue_pairs_gt_1), VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, vmstate_virtio_net_tx_waiting), VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet, @@ -3411,16 +3411,16 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) return; } - n->max_queues = MAX(n->nic_conf.peers.queues, 1); - if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) { - error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " + n->max_queue_pairs = MAX(n->nic_conf.peers.queues, 1); + if (n->max_queue_pairs * 2 + 1 > VIRTIO_QUEUE_MAX) { + error_setg(errp, "Invalid number of queue_pairs (= %" PRIu32 "), " "must be a positive integer less than %d.", - n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2); + n->max_queue_pairs, (VIRTIO_QUEUE_MAX - 1) / 2); virtio_cleanup(vdev); return; } - n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues); - n->curr_queues = 1; + n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queue_pairs); + n->curr_queue_pairs = 1; n->tx_timeout = n->net_conf.txtimer; if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer") @@ -3434,7 +3434,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n), n->net_conf.tx_queue_size); - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { virtio_net_add_queue(n, i); } @@ -3458,13 +3458,13 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) object_get_typename(OBJECT(dev)), dev->id, n); } - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { n->nic->ncs[i].do_not_pad = true; } peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); } n->host_hdr_len = sizeof(struct virtio_net_hdr); @@ -3506,7 +3506,7 @@ static void virtio_net_device_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIONet *n = VIRTIO_NET(dev); - int i, max_queues; + int i, max_queue_pairs; if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) { virtio_net_unload_ebpf(n); @@ -3531,12 +3531,12 @@ static void virtio_net_device_unrealize(DeviceState *dev) assert(n->primary_opts == NULL); } - max_queues = n->multiqueue ? n->max_queues : 1; - for (i = 0; i < max_queues; i++) { + max_queue_pairs = n->multiqueue ? n->max_queue_pairs : 1; + for (i = 0; i < max_queue_pairs; i++) { virtio_net_del_queue(n, i); } /* delete also control vq */ - virtio_del_queue(vdev, max_queues * 2); + virtio_del_queue(vdev, max_queue_pairs * 2); qemu_announce_timer_del(&n->announce_timer, false); g_free(n->vqs); qemu_del_nic(n->nic); diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 74a10ebe85..2903b79a92 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -194,8 +194,8 @@ struct VirtIONet { NICConf nic_conf; DeviceState *qdev; int multiqueue; - uint16_t max_queues; - uint16_t curr_queues; + uint16_t max_queue_pairs; + uint16_t curr_queue_pairs; size_t config_size; char *netclient_name; char *netclient_type; From 049eb15b5fc9c54ae76254a196b695000127da7a Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:58 +0800 Subject: [PATCH 0649/1334] vhost: record the last virtqueue index for the virtio device This patch introduces a new field in the vhost_dev structure to record the last virtqueue index for the virtio device. This will be useful for the vhost backends with 1:N model to start or stop the device after all the vhost_dev structures were started or stopped. Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-9-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/vhost_net.c | 12 +++++++++--- include/hw/virtio/vhost.h | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 2b594b4642..3aabab06ea 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -231,9 +231,11 @@ fail: return NULL; } -static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index) +static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index, + int last_index) { net->dev.vq_index = vq_index; + net->dev.last_index = last_index; } static int vhost_net_start_one(struct vhost_net *net, @@ -324,9 +326,13 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, VirtIONet *n = VIRTIO_NET(dev); int nvhosts = data_queue_pairs + cvq; struct vhost_net *net; - int r, e, i; + int r, e, i, last_index = data_qps * 2; NetClientState *peer; + if (!cvq) { + last_index -= 1; + } + if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); return -ENOSYS; @@ -341,7 +347,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } net = get_vhost_net(peer); - vhost_net_set_vq_index(net, i * 2); + vhost_net_set_vq_index(net, i * 2, last_index); /* Suppress the masking guest notifiers on vhost user * because vhost user doesn't interrupt masking/unmasking diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 1a9fc65089..3fa0b554ef 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -74,6 +74,8 @@ struct vhost_dev { unsigned int nvqs; /* the first virtqueue which would be used by this vhost dev */ int vq_index; + /* the last vq index for the virtio device (not vhost) */ + int last_index; /* if non-zero, minimum required value for max_queues */ int num_queues; uint64_t features; From 22288fe5a3f2dc4cb5c8826646d466019ad67682 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:55:59 +0800 Subject: [PATCH 0650/1334] virtio-net: vhost control virtqueue support This patch implements the control virtqueue support for vhost. This requires virtio-net to figure out the datapath queue pairs and control virtqueue via is_datapath and pass the number of those two types of virtqueues to vhost_net_start()/vhost_net_stop(). Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-10-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/vhost_net.c | 2 +- hw/net/virtio-net.c | 23 +++++++++++++++++++---- include/hw/virtio/virtio-net.h | 1 + 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 3aabab06ea..0d888f29a6 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -326,7 +326,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, VirtIONet *n = VIRTIO_NET(dev); int nvhosts = data_queue_pairs + cvq; struct vhost_net *net; - int r, e, i, last_index = data_qps * 2; + int r, e, i, last_index = data_queue_pairs * 2; NetClientState *peer; if (!cvq) { diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 7594f7ea92..f2014d5ea0 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -244,6 +244,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) VirtIODevice *vdev = VIRTIO_DEVICE(n); NetClientState *nc = qemu_get_queue(n->nic); int queue_pairs = n->multiqueue ? n->max_queue_pairs : 1; + int cvq = n->max_ncs - n->max_queue_pairs; if (!get_vhost_net(nc->peer)) { return; @@ -285,14 +286,14 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) } n->vhost_started = 1; - r = vhost_net_start(vdev, n->nic->ncs, queue_pairs, 0); + r = vhost_net_start(vdev, n->nic->ncs, queue_pairs, cvq); if (r < 0) { error_report("unable to start vhost net: %d: " "falling back on userspace virtio", -r); n->vhost_started = 0; } } else { - vhost_net_stop(vdev, n->nic->ncs, queue_pairs, 0); + vhost_net_stop(vdev, n->nic->ncs, queue_pairs, cvq); n->vhost_started = 0; } } @@ -3411,9 +3412,23 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) return; } - n->max_queue_pairs = MAX(n->nic_conf.peers.queues, 1); + n->max_ncs = MAX(n->nic_conf.peers.queues, 1); + + /* + * Figure out the datapath queue pairs since the backend could + * provide control queue via peers as well. + */ + if (n->nic_conf.peers.queues) { + for (i = 0; i < n->max_ncs; i++) { + if (n->nic_conf.peers.ncs[i]->is_datapath) { + ++n->max_queue_pairs; + } + } + } + n->max_queue_pairs = MAX(n->max_queue_pairs, 1); + if (n->max_queue_pairs * 2 + 1 > VIRTIO_QUEUE_MAX) { - error_setg(errp, "Invalid number of queue_pairs (= %" PRIu32 "), " + error_setg(errp, "Invalid number of queue pairs (= %" PRIu32 "), " "must be a positive integer less than %d.", n->max_queue_pairs, (VIRTIO_QUEUE_MAX - 1) / 2); virtio_cleanup(vdev); diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 2903b79a92..eb87032627 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -196,6 +196,7 @@ struct VirtIONet { int multiqueue; uint16_t max_queue_pairs; uint16_t curr_queue_pairs; + uint16_t max_ncs; size_t config_size; char *netclient_name; char *netclient_type; From 402378407dbdce79ce745a13f5c84815f929cfdd Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 20 Oct 2021 12:56:00 +0800 Subject: [PATCH 0651/1334] vhost-vdpa: multiqueue support This patch implements the multiqueue support for vhost-vdpa. This is done simply by reading the number of queue pairs from the config space and initialize the datapath and control path net client. Signed-off-by: Jason Wang Message-Id: <20211020045600.16082-11-jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 2 +- net/vhost-vdpa.c | 105 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 10 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 8948fd316b..12661fd5b1 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -632,7 +632,7 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); } - if (vhost_vdpa_one_time_request(dev)) { + if (dev->vq_index + dev->nvqs != dev->last_index) { return 0; } diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 151f60184d..49ab322511 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -18,6 +18,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "qapi/error.h" +#include #include #include #include "standard-headers/linux/virtio_net.h" @@ -51,6 +52,14 @@ const int vdpa_feature_bits[] = { VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_MTU, + VIRTIO_NET_F_CTRL_RX, + VIRTIO_NET_F_CTRL_RX_EXTRA, + VIRTIO_NET_F_CTRL_VLAN, + VIRTIO_NET_F_GUEST_ANNOUNCE, + VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_F_RSS, + VIRTIO_NET_F_MQ, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, VIRTIO_NET_F_RSS, @@ -81,7 +90,8 @@ static int vhost_vdpa_net_check_device_id(struct vhost_net *net) return ret; } -static int vhost_vdpa_add(NetClientState *ncs, void *be) +static int vhost_vdpa_add(NetClientState *ncs, void *be, + int queue_pair_index, int nvqs) { VhostNetOptions options; struct vhost_net *net = NULL; @@ -94,7 +104,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be) options.net_backend = ncs; options.opaque = be; options.busyloop_timeout = 0; - options.nvqs = 2; + options.nvqs = nvqs; net = vhost_net_init(&options); if (!net) { @@ -172,18 +182,28 @@ static NetClientInfo net_vhost_vdpa_info = { static NetClientState *net_vhost_vdpa_init(NetClientState *peer, const char *device, const char *name, - int vdpa_device_fd) + int vdpa_device_fd, + int queue_pair_index, + int nvqs, + bool is_datapath) { NetClientState *nc = NULL; VhostVDPAState *s; int ret = 0; assert(name); - nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); + if (is_datapath) { + nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, + name); + } else { + nc = qemu_new_net_control_client(&net_vhost_vdpa_info, peer, + device, name); + } snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); s = DO_UPCAST(VhostVDPAState, nc, nc); s->vhost_vdpa.device_fd = vdpa_device_fd; - ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); + s->vhost_vdpa.index = queue_pair_index; + ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa, queue_pair_index, nvqs); if (ret) { qemu_del_net_client(nc); return NULL; @@ -191,12 +211,52 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer, return nc; } +static int vhost_vdpa_get_max_queue_pairs(int fd, int *has_cvq, Error **errp) +{ + unsigned long config_size = offsetof(struct vhost_vdpa_config, buf); + struct vhost_vdpa_config *config; + __virtio16 *max_queue_pairs; + uint64_t features; + int ret; + + ret = ioctl(fd, VHOST_GET_FEATURES, &features); + if (ret) { + error_setg(errp, "Fail to query features from vhost-vDPA device"); + return ret; + } + + if (features & (1 << VIRTIO_NET_F_CTRL_VQ)) { + *has_cvq = 1; + } else { + *has_cvq = 0; + } + + if (features & (1 << VIRTIO_NET_F_MQ)) { + config = g_malloc0(config_size + sizeof(*max_queue_pairs)); + config->off = offsetof(struct virtio_net_config, max_virtqueue_pairs); + config->len = sizeof(*max_queue_pairs); + + ret = ioctl(fd, VHOST_VDPA_GET_CONFIG, config); + if (ret) { + error_setg(errp, "Fail to get config from vhost-vDPA device"); + return -ret; + } + + max_queue_pairs = (__virtio16 *)&config->buf; + + return lduw_le_p(max_queue_pairs); + } + + return 1; +} + int net_init_vhost_vdpa(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { const NetdevVhostVDPAOptions *opts; int vdpa_device_fd; - NetClientState *nc; + NetClientState **ncs, *nc; + int queue_pairs, i, has_cvq = 0; assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); opts = &netdev->u.vhost_vdpa; @@ -206,11 +266,38 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, return -errno; } - nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, vdpa_device_fd); - if (!nc) { + queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, + &has_cvq, errp); + if (queue_pairs < 0) { qemu_close(vdpa_device_fd); - return -1; + return queue_pairs; } + ncs = g_malloc0(sizeof(*ncs) * queue_pairs); + + for (i = 0; i < queue_pairs; i++) { + ncs[i] = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, + vdpa_device_fd, i, 2, true); + if (!ncs[i]) + goto err; + } + + if (has_cvq) { + nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, + vdpa_device_fd, i, 1, false); + if (!nc) + goto err; + } + + g_free(ncs); return 0; + +err: + if (i) { + qemu_del_net_client(ncs[0]); + } + qemu_close(vdpa_device_fd); + g_free(ncs); + + return -1; } From 0e464f7d993113119f0fd17b890831440734ce15 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 20 Oct 2021 05:48:54 -0400 Subject: [PATCH 0652/1334] pci: fix PCI resource reserve capability on BE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI resource reserve capability should use LE format as all other PCI things. If we don't then seabios won't boot: === PCI new allocation pass #1 === PCI: check devices PCI: QEMU resource reserve cap: size 10000000000000 type io PCI: secondary bus 1 size 10000000000000 type io PCI: secondary bus 1 size 00200000 type mem PCI: secondary bus 1 size 00200000 type prefmem === PCI new allocation pass #2 === PCI: out of I/O address space This became more important since we started reserving IO by default, previously no one noticed. Fixes: e2a6290aab ("hw/pcie-root-port: Fix hotplug for PCI devices requiring IO") Cc: marcel.apfelbaum@gmail.com Fixes: 226263fb5c ("hw/pci: add QEMU-specific PCI capability to the Generic PCI Express Root Port") Cc: zuban32s@gmail.com Fixes: 6755e618d0 ("hw/pci: add PCI resource reserve capability to legacy PCI bridge") Cc: jing2.liu@linux.intel.com Tested-by: Thomas Huth Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- hw/pci/pci_bridge.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index d1f902ee86..da34c8ebcd 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -448,11 +448,11 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, PCIBridgeQemuCap cap = { .len = cap_len, .type = REDHAT_PCI_CAP_RESOURCE_RESERVE, - .bus_res = res_reserve.bus, - .io = res_reserve.io, - .mem = res_reserve.mem_non_pref, - .mem_pref_32 = res_reserve.mem_pref_32, - .mem_pref_64 = res_reserve.mem_pref_64 + .bus_res = cpu_to_le32(res_reserve.bus), + .io = cpu_to_le64(res_reserve.io), + .mem = cpu_to_le32(res_reserve.mem_non_pref), + .mem_pref_32 = cpu_to_le32(res_reserve.mem_pref_32), + .mem_pref_64 = cpu_to_le64(res_reserve.mem_pref_64) }; int offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, From 6dcb1cc9512c6b4cd8f85abc537abaf6f6c0738b Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Thu, 7 Oct 2021 19:27:48 +0530 Subject: [PATCH 0653/1334] tests/acpi/bios-tables-test: add and allow changes to a new q35 DSDT table blob We are adding a new unit test to cover the acpi hotplug support in q35 for multi-function bridges. This test uses a new table DSDT.multi-bridge. We need to allow changes in DSDT acpi table for addition of this new unit test. Signed-off-by: Ani Sinha Message-Id: <20211007135750.1277213-2-ani@anisinha.ca> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Igor Mammedov --- tests/data/acpi/q35/DSDT.multi-bridge | 0 tests/qtest/bios-tables-test-allowed-diff.h | 1 + 2 files changed, 1 insertion(+) create mode 100644 tests/data/acpi/q35/DSDT.multi-bridge diff --git a/tests/data/acpi/q35/DSDT.multi-bridge b/tests/data/acpi/q35/DSDT.multi-bridge new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..dabc024f53 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/DSDT.multi-bridge", From 04dd78b9e85720226a148eef54b45cb02b463034 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Thu, 7 Oct 2021 19:27:49 +0530 Subject: [PATCH 0654/1334] tests/acpi/pcihp: add unit tests for hotplug on multifunction bridges for q35 commit d7346e614f4ec ("acpi: x86: pcihp: add support hotplug on multifunction bridges") added ACPI hotplug descriptions for cold plugged bridges for functions other than 0. For all other devices, the ACPI hotplug descriptions are limited to function 0 only. This change adds unit tests for this feature. This test adds the following devices to qemu and then checks the changes introduced in the DSDT table due to the addition of the following devices: (a) a multifunction bridge device (b) a bridge device with function 1 (c) a non-bridge device with function 2 In the DSDT table, we should see AML hotplug descriptions for (a) and (b). For (a) we should find a hotplug AML description for function 0. The following diff compares the DSDT table AML with the new unit test before and after the change d7346e614f4ec is introduced. In other words, this diff reflects the changes that occurs in the DSDT table due to the change d7346e614f4ec . @@ -1,60 +1,38 @@ /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20190509 (64-bit version) * Copyright (c) 2000 - 2019 Intel Corporation * * Disassembling to symbolic ASL+ operators * - * Disassembly of tests/data/acpi/q35/DSDT.multi-bridge, Thu Oct 7 18:56:05 2021 + * Disassembly of /tmp/aml-AN0DA1, Thu Oct 7 18:56:05 2021 * * Original Table Header: * Signature "DSDT" - * Length 0x000020FE (8446) + * Length 0x00002187 (8583) * Revision 0x01 **** 32-bit table (V1), no 64-bit math support - * Checksum 0xDE + * Checksum 0x8D * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) * Compiler ID "BXPC" * Compiler Version 0x00000001 (1) */ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) { - /* - * iASL Warning: There was 1 external control method found during - * disassembly, but only 0 were resolved (1 unresolved). Additional - * ACPI tables may be required to properly disassemble the code. This - * resulting disassembler output file may not compile because the - * disassembler did not know how many arguments to assign to the - * unresolved methods. Note: SSDTs can be dynamically loaded at - * runtime and may or may not be available via the host OS. - * - * In addition, the -fe option can be used to specify a file containing - * control method external declarations with the associated method - * argument counts. Each line of the file must be of the form: - * External (, MethodObj, ) - * Invocation: - * iasl -fe refs.txt -d dsdt.aml - * - * The following methods were unresolved and many not compile properly - * because the disassembler had to guess at the number of arguments - * required for each: - */ - External (_SB_.PCI0.S19_.PCNT, MethodObj) // Warning: Unknown method, guessing 1 arguments - Scope (\) { OperationRegion (DBG, SystemIO, 0x0402, One) Field (DBG, ByteAcc, NoLock, Preserve) { DBGB, 8 } Method (DBUG, 1, NotSerialized) { ToHexString (Arg0, Local0) ToBuffer (Local0, Local0) Local1 = (SizeOf (Local0) - One) Local2 = Zero While ((Local2 < Local1)) { @@ -3322,24 +3300,60 @@ Method (DVNT, 2, NotSerialized) { If ((Arg0 & One)) { Notify (S00, Arg1) } } Method (PCNT, 0, NotSerialized) { BNUM = One DVNT (PCIU, One) DVNT (PCID, 0x03) } } + Device (S19) + { + Name (_ADR, 0x00030001) // _ADR: Address + Name (BSEL, Zero) + Device (S00) + { + Name (_SUN, Zero) // _SUN: Slot User Number + Name (_ADR, Zero) // _ADR: Address + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + PCEJ (BSEL, _SUN) + } + + Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method + { + Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, _SUN)) + } + } + + Method (DVNT, 2, NotSerialized) + { + If ((Arg0 & One)) + { + Notify (S00, Arg1) + } + } + + Method (PCNT, 0, NotSerialized) + { + BNUM = Zero + DVNT (PCIU, One) + DVNT (PCID, 0x03) + } + } + Method (PCNT, 0, NotSerialized) { - ^S19.PCNT (^S10.PCNT ()) + ^S19.PCNT () + ^S10.PCNT () } } } } Signed-off-by: Ani Sinha Message-Id: <20211007135750.1277213-3-ani@anisinha.ca> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Igor Mammedov --- tests/qtest/bios-tables-test.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 798f68c737..258874167e 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -870,6 +870,23 @@ static void test_acpi_q35_tcg_bridge(void) free_test_data(&data); } +static void test_acpi_q35_multif_bridge(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".multi-bridge", + }; + test_acpi_one("-device pcie-root-port,id=pcie-root-port-0," + "multifunction=on," + "port=0x0,chassis=1,addr=0x2,bus=pcie.0 " + "-device pcie-root-port,id=pcie-root-port-1," + "port=0x1,chassis=2,addr=0x3.0x1,bus=pcie.0 " + "-device virtio-balloon,id=balloon0," + "bus=pcie.0,addr=0x4.0x2", + &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_mmio64(void) { test_data data = { @@ -1581,6 +1598,7 @@ int main(int argc, char *argv[]) test_acpi_piix4_no_acpi_pci_hotplug); qtest_add_func("acpi/q35", test_acpi_q35_tcg); qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); + qtest_add_func("acpi/q35/multif-bridge", test_acpi_q35_multif_bridge); qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64); qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); From a8339e07f94a47f99560baef59d65a9e039aaf45 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Thu, 7 Oct 2021 19:27:50 +0530 Subject: [PATCH 0655/1334] tests/acpi/bios-tables-test: update DSDT blob for multifunction bridge test We added a new unit test for testing acpi hotplug on multifunction bridges in q35 machines. Here, we update the DSDT table gloden master blob for this unit test. The test adds the following devices to qemu and then checks the changes introduced in the DSDT table due to the addition of the following devices: (a) a multifunction bridge device (b) a bridge device with function 1 (c) a non-bridge device with function 2 In the DSDT table, we should see AML hotplug descriptions for (a) and (b). For (a) we should find a hotplug AML description for function 0. Following is the ASL diff between the original DSDT table and the modified DSDT table due to the unit test. We see that multifunction bridge on bus 2 and single function bridge on bus 3 function 1 are described, not the non-bridge balloon device on bus 4, function 2. @@ -1,30 +1,30 @@ /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20190509 (64-bit version) * Copyright (c) 2000 - 2019 Intel Corporation * * Disassembling to symbolic ASL+ operators * - * Disassembly of tests/data/acpi/q35/DSDT, Thu Oct 7 18:29:19 2021 + * Disassembly of /tmp/aml-C7JCA1, Thu Oct 7 18:29:19 2021 * * Original Table Header: * Signature "DSDT" - * Length 0x00002061 (8289) + * Length 0x00002187 (8583) * Revision 0x01 **** 32-bit table (V1), no 64-bit math support - * Checksum 0xF9 + * Checksum 0x8D * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) * Compiler ID "BXPC" * Compiler Version 0x00000001 (1) */ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) { Scope (\) { OperationRegion (DBG, SystemIO, 0x0402, One) Field (DBG, ByteAcc, NoLock, Preserve) { DBGB, 8 } @@ -3265,23 +3265,95 @@ Method (_S1D, 0, NotSerialized) // _S1D: S1 Device State { Return (Zero) } Method (_S2D, 0, NotSerialized) // _S2D: S2 Device State { Return (Zero) } Method (_S3D, 0, NotSerialized) // _S3D: S3 Device State { Return (Zero) } } + Device (S10) + { + Name (_ADR, 0x00020000) // _ADR: Address + Name (BSEL, One) + Device (S00) + { + Name (_SUN, Zero) // _SUN: Slot User Number + Name (_ADR, Zero) // _ADR: Address + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + PCEJ (BSEL, _SUN) + } + + Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method + { + Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, _SUN)) + } + } + + Method (DVNT, 2, NotSerialized) + { + If ((Arg0 & One)) + { + Notify (S00, Arg1) + } + } + + Method (PCNT, 0, NotSerialized) + { + BNUM = One + DVNT (PCIU, One) + DVNT (PCID, 0x03) + } + } + + Device (S19) + { + Name (_ADR, 0x00030001) // _ADR: Address + Name (BSEL, Zero) + Device (S00) + { + Name (_SUN, Zero) // _SUN: Slot User Number + Name (_ADR, Zero) // _ADR: Address + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + PCEJ (BSEL, _SUN) + } + + Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method + { + Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, _SUN)) + } + } + + Method (DVNT, 2, NotSerialized) + { + If ((Arg0 & One)) + { + Notify (S00, Arg1) + } + } + + Method (PCNT, 0, NotSerialized) + { + BNUM = Zero + DVNT (PCIU, One) + DVNT (PCID, 0x03) + } + } + Method (PCNT, 0, NotSerialized) { + ^S19.PCNT () + ^S10.PCNT () } } } } Signed-off-by: Ani Sinha Message-Id: <20211007135750.1277213-4-ani@anisinha.ca> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Igor Mammedov --- tests/data/acpi/q35/DSDT.multi-bridge | Bin 0 -> 8583 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/q35/DSDT.multi-bridge b/tests/data/acpi/q35/DSDT.multi-bridge index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a24c713d22102a1a1583b5c902edffe1694e5cfe 100644 GIT binary patch literal 8583 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 P)}F_He7!|yIezv(me~h+ literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dabc024f53..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/q35/DSDT.multi-bridge", From e976459b3b127838befaef57f1587770452a0827 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 14:41:24 +0100 Subject: [PATCH 0656/1334] mac_via: update comment for VIA1B_vMystery bit According to both Linux and NetBSD, port B bit 6 is used on the Quadra 800 to configure the GLUE logic in A/UX mode. Whilst the name VIA1B_vMystery isn't particularly descriptive, the patch leaves this to ensure that the constants in mac_via.c remain in sync with Linux's mac_via.h. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211020134131.4392-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/misc/mac_via.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 993bac017d..7a53a8b4c0 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -130,6 +130,10 @@ * On SE/30, vertical sync interrupt enable. * 0=enabled. This vSync interrupt shows up * as a slot $E interrupt. + * On Quadra 800 this bit toggles A/UX mode which + * configures the glue logic to deliver some IRQs + * at different levels compared to a classic + * Mac. */ #define VIA1B_vADBS2 0x20 /* ADB state input bit 1 (unused on IIfx) */ #define VIA1B_vADBS1 0x10 /* ADB state input bit 0 (unused on IIfx) */ From 39950b16ecc21bce3bbea3fddcdf1aaefa0d6cef Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 14:41:25 +0100 Subject: [PATCH 0657/1334] q800: move VIA1 IRQ from level 1 to level 6 On a Quadra 800 machine Linux sets via_alt_mapping to 1 and clears port B bit 6 to ensure that the VIA1 IRQ is delivered at level 6 rather than level 1. Even though QEMU doesn't yet emulate this behaviour, Linux still installs the VIA1 level 1 IRQ handler regardless of the value of via_alt_mapping which is why the kernel has been able to boot until now. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211020134131.4392-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index fd4855047e..15f3067811 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -284,7 +284,7 @@ static void q800_init(MachineState *machine) sysbus = SYS_BUS_DEVICE(via1_dev); sysbus_realize_and_unref(sysbus, &error_fatal); sysbus_mmio_map(sysbus, 1, VIA_BASE); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 0)); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 5)); adb_bus = qdev_get_child_bus(via1_dev, "adb.0"); dev = qdev_new(TYPE_ADB_KEYBOARD); From 91ff5e4dcd855c6f93b91ef8c5d43b18a1d080d0 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 14:41:26 +0100 Subject: [PATCH 0658/1334] q800: use GLUE IRQ numbers instead of IRQ level for GLUE IRQs In order to allow dynamic routing of IRQs to different IRQ levels on the CPU depending upon port B bit 6, use GLUE IRQ numbers and map them to the the corresponding CPU IRQ level accordingly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211020134131.4392-4-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 15f3067811..81c335bf16 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -102,11 +102,34 @@ struct GLUEState { uint8_t ipr; }; +#define GLUE_IRQ_IN_VIA1 0 +#define GLUE_IRQ_IN_VIA2 1 +#define GLUE_IRQ_IN_SONIC 2 +#define GLUE_IRQ_IN_ESCC 3 + static void GLUE_set_irq(void *opaque, int irq, int level) { GLUEState *s = opaque; int i; + switch (irq) { + case GLUE_IRQ_IN_VIA1: + irq = 5; + break; + + case GLUE_IRQ_IN_VIA2: + irq = 1; + break; + + case GLUE_IRQ_IN_SONIC: + irq = 2; + break; + + case GLUE_IRQ_IN_ESCC: + irq = 3; + break; + } + if (level) { s->ipr |= 1 << irq; } else { @@ -284,7 +307,7 @@ static void q800_init(MachineState *machine) sysbus = SYS_BUS_DEVICE(via1_dev); sysbus_realize_and_unref(sysbus, &error_fatal); sysbus_mmio_map(sysbus, 1, VIA_BASE); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 5)); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA1)); adb_bus = qdev_get_child_bus(via1_dev, "adb.0"); dev = qdev_new(TYPE_ADB_KEYBOARD); @@ -297,7 +320,7 @@ static void q800_init(MachineState *machine) sysbus = SYS_BUS_DEVICE(via2_dev); sysbus_realize_and_unref(sysbus, &error_fatal); sysbus_mmio_map(sysbus, 1, VIA_BASE + VIA_SIZE); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 1)); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA2)); /* MACSONIC */ @@ -330,7 +353,7 @@ static void q800_init(MachineState *machine) sysbus = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sysbus, &error_fatal); sysbus_mmio_map(sysbus, 0, SONIC_BASE); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 2)); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_SONIC)); memory_region_init_rom(dp8393x_prom, NULL, "dp8393x-q800.prom", SONIC_PROM_SIZE, &error_fatal); @@ -366,7 +389,8 @@ static void q800_init(MachineState *machine) qdev_realize_and_unref(escc_orgate, NULL, &error_fatal); sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(escc_orgate, 0)); sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(escc_orgate, 1)); - qdev_connect_gpio_out(DEVICE(escc_orgate), 0, qdev_get_gpio_in(glue, 3)); + qdev_connect_gpio_out(DEVICE(escc_orgate), 0, + qdev_get_gpio_in(glue, GLUE_IRQ_IN_ESCC)); sysbus_mmio_map(sysbus, 0, SCC_BASE); /* SCSI */ From 291bc1809a0ec75f283717850003c08953d80e7c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 14:41:27 +0100 Subject: [PATCH 0659/1334] mac_via: add GPIO for A/UX mode Add a new auxmode GPIO that is updated when port B bit 6 is changed indicating whether the hardware is configured for A/UX mode. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211020134131.4392-5-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/misc/mac_via.c | 19 +++++++++++++++++++ hw/misc/trace-events | 1 + include/hw/misc/mac_via.h | 1 + 3 files changed, 21 insertions(+) diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 7a53a8b4c0..b378e6b305 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -880,6 +880,21 @@ static void via1_adb_update(MOS6522Q800VIA1State *v1s) } } +static void via1_auxmode_update(MOS6522Q800VIA1State *v1s) +{ + MOS6522State *s = MOS6522(v1s); + int oldirq, irq; + + oldirq = (v1s->last_b & VIA1B_vMystery) ? 1 : 0; + irq = (s->b & VIA1B_vMystery) ? 1 : 0; + + /* Check to see if the A/UX mode bit has changed */ + if (irq != oldirq) { + trace_via1_auxmode(irq); + qemu_set_irq(v1s->auxmode_irq, irq); + } +} + static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size) { MOS6522Q800VIA1State *s = MOS6522_Q800_VIA1(opaque); @@ -902,6 +917,7 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, case VIA_REG_B: via1_rtc_update(v1s); via1_adb_update(v1s); + via1_auxmode_update(v1s); v1s->last_b = ms->b; break; @@ -1046,6 +1062,9 @@ static void mos6522_q800_via1_init(Object *obj) TYPE_ADB_BUS, DEVICE(v1s), "adb.0"); qdev_init_gpio_in(DEVICE(obj), via1_irq_request, VIA1_IRQ_NB); + + /* A/UX mode */ + qdev_init_gpio_out(DEVICE(obj), &v1s->auxmode_irq, 1); } static const VMStateDescription vmstate_q800_via1 = { diff --git a/hw/misc/trace-events b/hw/misc/trace-events index ede413965b..2da96d167a 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -228,6 +228,7 @@ via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "secto via1_adb_send(const char *state, uint8_t data, const char *vadbint) "state %s data=0x%02x vADBInt=%s" via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int status, int index, int size) "state %s data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size) "data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" +via1_auxmode(int mode) "setting auxmode to %d" # grlib_ahb_apb_pnp.c grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x" diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index 4506abe5d0..b445565866 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -43,6 +43,7 @@ struct MOS6522Q800VIA1State { MemoryRegion via_mem; qemu_irq irqs[VIA1_IRQ_NB]; + qemu_irq auxmode_irq; uint8_t last_b; /* RTC */ From a85d18aabdd4632bb0eeb496c97472746ba35d3c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 14:41:28 +0100 Subject: [PATCH 0660/1334] q800: wire up auxmode GPIO to GLUE This enables the GLUE logic to change its CPU level IRQ routing depending upon whether the hardware has been configured for A/UX mode. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211020134131.4392-6-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 81c335bf16..0093872d89 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -100,6 +100,7 @@ struct GLUEState { SysBusDevice parent_obj; M68kCPU *cpu; uint8_t ipr; + uint8_t auxmode; }; #define GLUE_IRQ_IN_VIA1 0 @@ -145,11 +146,19 @@ static void GLUE_set_irq(void *opaque, int irq, int level) m68k_set_irq_level(s->cpu, 0, 0); } +static void glue_auxmode_set_irq(void *opaque, int irq, int level) +{ + GLUEState *s = GLUE(opaque); + + s->auxmode = level; +} + static void glue_reset(DeviceState *dev) { GLUEState *s = GLUE(dev); s->ipr = 0; + s->auxmode = 0; } static const VMStateDescription vmstate_glue = { @@ -158,6 +167,7 @@ static const VMStateDescription vmstate_glue = { .minimum_version_id = 0, .fields = (VMStateField[]) { VMSTATE_UINT8(ipr, GLUEState), + VMSTATE_UINT8(auxmode, GLUEState), VMSTATE_END_OF_LIST(), }, }; @@ -178,6 +188,7 @@ static void glue_init(Object *obj) DeviceState *dev = DEVICE(obj); qdev_init_gpio_in(dev, GLUE_set_irq, 8); + qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1); } static void glue_class_init(ObjectClass *klass, void *data) @@ -308,6 +319,9 @@ static void q800_init(MachineState *machine) sysbus_realize_and_unref(sysbus, &error_fatal); sysbus_mmio_map(sysbus, 1, VIA_BASE); sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA1)); + /* A/UX mode */ + qdev_connect_gpio_out(via1_dev, 0, + qdev_get_gpio_in_named(glue, "auxmode", 0)); adb_bus = qdev_get_child_bus(via1_dev, "adb.0"); dev = qdev_new(TYPE_ADB_KEYBOARD); From f7c6e12e24a736f7f3f0bf2c4e34a598e3274130 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 14:41:29 +0100 Subject: [PATCH 0661/1334] q800: route SONIC on-board Ethernet IRQ via nubus IRQ 9 in classic mode When the hardware is operating in classic mode the SONIC on-board Ethernet IRQ is routed to nubus IRQ 9 instead of directly to the CPU at level 3. This does not affect the framebuffer which although it exists in slot 9, has its own dedicated IRQ on the Quadra 800 hardware. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211020134131.4392-7-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 51 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 0093872d89..7a8de089f4 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -101,6 +101,7 @@ struct GLUEState { M68kCPU *cpu; uint8_t ipr; uint8_t auxmode; + qemu_irq irqs[1]; }; #define GLUE_IRQ_IN_VIA1 0 @@ -108,27 +109,40 @@ struct GLUEState { #define GLUE_IRQ_IN_SONIC 2 #define GLUE_IRQ_IN_ESCC 3 +#define GLUE_IRQ_NUBUS_9 0 + static void GLUE_set_irq(void *opaque, int irq, int level) { GLUEState *s = opaque; int i; - switch (irq) { - case GLUE_IRQ_IN_VIA1: - irq = 5; - break; + if (s->auxmode) { + /* Classic mode */ + switch (irq) { + case GLUE_IRQ_IN_SONIC: + /* Route to VIA2 instead */ + qemu_set_irq(s->irqs[GLUE_IRQ_NUBUS_9], level); + return; + } + } else { + /* A/UX mode */ + switch (irq) { + case GLUE_IRQ_IN_VIA1: + irq = 5; + break; - case GLUE_IRQ_IN_VIA2: - irq = 1; - break; + case GLUE_IRQ_IN_VIA2: + irq = 1; + break; - case GLUE_IRQ_IN_SONIC: - irq = 2; - break; + case GLUE_IRQ_IN_SONIC: + irq = 2; + break; - case GLUE_IRQ_IN_ESCC: - irq = 3; - break; + case GLUE_IRQ_IN_ESCC: + irq = 3; + break; + } } if (level) { @@ -186,9 +200,12 @@ static Property glue_properties[] = { static void glue_init(Object *obj) { DeviceState *dev = DEVICE(obj); + GLUEState *s = GLUE(dev); qdev_init_gpio_in(dev, GLUE_set_irq, 8); qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1); + + qdev_init_gpio_out(dev, s->irqs, 1); } static void glue_class_init(ObjectClass *klass, void *data) @@ -454,6 +471,14 @@ static void q800_init(MachineState *machine) VIA2_NUBUS_IRQ_9 + i)); } + /* + * Since the framebuffer in slot 0x9 uses a separate IRQ, wire the unused + * IRQ via GLUE for use by SONIC Ethernet in classic mode + */ + qdev_connect_gpio_out(glue, GLUE_IRQ_NUBUS_9, + qdev_get_gpio_in_named(via2_dev, "nubus-irq", + VIA2_NUBUS_IRQ_9)); + nubus = &NUBUS_BRIDGE(dev)->bus; /* framebuffer in nubus slot #9 */ From c7710c1ebf0b316ac0ab517fa8ceaa2824904474 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 14:41:30 +0100 Subject: [PATCH 0662/1334] q800: wire up remaining IRQs in classic mode Explicitly wire up the remaining IRQs in classic mode to enable the use of g_assert_not_reached() in the default case to detect any unexpected IRQs. Add a comment explaining the IRQ routing differences in A/UX mode based upon the comments in NetBSD (also noting that at least A/UX 3.0.1 still uses classic mode). Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-Id: <20211020134131.4392-8-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 7a8de089f4..83fde39298 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -111,6 +111,37 @@ struct GLUEState { #define GLUE_IRQ_NUBUS_9 0 +/* + * The GLUE logic on the Quadra 800 supports 2 different IRQ routing modes + * controlled from the VIA1 auxmode GPIO (port B bit 6) which are documented + * in NetBSD as follows: + * + * A/UX mode (Linux, NetBSD, auxmode GPIO low) + * + * Level 0: Spurious: ignored + * Level 1: Software + * Level 2: VIA2 (except ethernet, sound) + * Level 3: Ethernet + * Level 4: Serial (SCC) + * Level 5: Sound + * Level 6: VIA1 + * Level 7: NMIs: parity errors, RESET button, YANCC error + * + * Classic mode (default: used by MacOS, A/UX 3.0.1, auxmode GPIO high) + * + * Level 0: Spurious: ignored + * Level 1: VIA1 (clock, ADB) + * Level 2: VIA2 (NuBus, SCSI) + * Level 3: + * Level 4: Serial (SCC) + * Level 5: + * Level 6: + * Level 7: Non-maskable: parity errors, RESET button + * + * Note that despite references to A/UX mode in Linux and NetBSD, at least + * A/UX 3.0.1 still uses Classic mode. + */ + static void GLUE_set_irq(void *opaque, int irq, int level) { GLUEState *s = opaque; @@ -119,10 +150,25 @@ static void GLUE_set_irq(void *opaque, int irq, int level) if (s->auxmode) { /* Classic mode */ switch (irq) { + case GLUE_IRQ_IN_VIA1: + irq = 0; + break; + + case GLUE_IRQ_IN_VIA2: + irq = 1; + break; + case GLUE_IRQ_IN_SONIC: /* Route to VIA2 instead */ qemu_set_irq(s->irqs[GLUE_IRQ_NUBUS_9], level); return; + + case GLUE_IRQ_IN_ESCC: + irq = 3; + break; + + default: + g_assert_not_reached(); } } else { /* A/UX mode */ @@ -142,6 +188,9 @@ static void GLUE_set_irq(void *opaque, int irq, int level) case GLUE_IRQ_IN_ESCC: irq = 3; break; + + default: + g_assert_not_reached(); } } From 3ea74abe2ddfd0741d4aa75484c88b346b85fb26 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 14:41:31 +0100 Subject: [PATCH 0663/1334] q800: add NMI handler This allows the programmer's switch to be triggered via the monitor for debugging purposes. Since the CPU level 7 interrupt is level-triggered, use a timer to hold the NMI active for 100ms before releasing it again. Signed-off-by: Mark Cave-Ayland Reviewied-by: Laurent Vivier Message-Id: <20211020134131.4392-9-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 83fde39298..a081051a8d 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -28,6 +28,7 @@ #include "cpu.h" #include "hw/boards.h" #include "hw/or-irq.h" +#include "hw/nmi.h" #include "elf.h" #include "hw/loader.h" #include "ui/console.h" @@ -102,12 +103,14 @@ struct GLUEState { uint8_t ipr; uint8_t auxmode; qemu_irq irqs[1]; + QEMUTimer *nmi_release; }; #define GLUE_IRQ_IN_VIA1 0 #define GLUE_IRQ_IN_VIA2 1 #define GLUE_IRQ_IN_SONIC 2 #define GLUE_IRQ_IN_ESCC 3 +#define GLUE_IRQ_IN_NMI 4 #define GLUE_IRQ_NUBUS_9 0 @@ -167,6 +170,10 @@ static void GLUE_set_irq(void *opaque, int irq, int level) irq = 3; break; + case GLUE_IRQ_IN_NMI: + irq = 6; + break; + default: g_assert_not_reached(); } @@ -189,6 +196,10 @@ static void GLUE_set_irq(void *opaque, int irq, int level) irq = 3; break; + case GLUE_IRQ_IN_NMI: + irq = 6; + break; + default: g_assert_not_reached(); } @@ -216,12 +227,30 @@ static void glue_auxmode_set_irq(void *opaque, int irq, int level) s->auxmode = level; } +static void glue_nmi(NMIState *n, int cpu_index, Error **errp) +{ + GLUEState *s = GLUE(n); + + /* Hold NMI active for 100ms */ + GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1); + timer_mod(s->nmi_release, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100); +} + +static void glue_nmi_release(void *opaque) +{ + GLUEState *s = GLUE(opaque); + + GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0); +} + static void glue_reset(DeviceState *dev) { GLUEState *s = GLUE(dev); s->ipr = 0; s->auxmode = 0; + + timer_del(s->nmi_release); } static const VMStateDescription vmstate_glue = { @@ -231,6 +260,7 @@ static const VMStateDescription vmstate_glue = { .fields = (VMStateField[]) { VMSTATE_UINT8(ipr, GLUEState), VMSTATE_UINT8(auxmode, GLUEState), + VMSTATE_TIMER_PTR(nmi_release, GLUEState), VMSTATE_END_OF_LIST(), }, }; @@ -246,6 +276,13 @@ static Property glue_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void glue_finalize(Object *obj) +{ + GLUEState *s = GLUE(obj); + + timer_free(s->nmi_release); +} + static void glue_init(Object *obj) { DeviceState *dev = DEVICE(obj); @@ -255,15 +292,20 @@ static void glue_init(Object *obj) qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1); qdev_init_gpio_out(dev, s->irqs, 1); + + /* NMI release timer */ + s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s); } static void glue_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + NMIClass *nc = NMI_CLASS(klass); dc->vmsd = &vmstate_glue; dc->reset = glue_reset; device_class_set_props(dc, glue_properties); + nc->nmi_monitor_handler = glue_nmi; } static const TypeInfo glue_info = { @@ -271,7 +313,12 @@ static const TypeInfo glue_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(GLUEState), .instance_init = glue_init, + .instance_finalize = glue_finalize, .class_init = glue_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, }; static void main_cpu_reset(void *opaque) From a56c12fb760a57c1419df4a34e930160f1d8d428 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 20 Oct 2021 15:18:10 +0100 Subject: [PATCH 0664/1334] q800: drop 8-bit graphic_depth check for Apple 21 inch display The graphic_depth check is no longer required since commit df8abbbadf ("macfb: add common monitor modes supported by the MacOS toolbox ROM") which introduced code in macfb_common_realize() to only allow the resolutions/depths provided in macfb_mode_table to be specified for each display type. Signed-off-by: Mark Cave-Ayland Fixes: df8abbbadf ("macfb: add common monitor modes supported by the MacOS toolbox ROM") Message-Id: <20211020141810.7875-1-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/m68k/q800.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index a081051a8d..e4c7c9b88a 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -584,7 +584,7 @@ static void q800_init(MachineState *machine) qdev_prop_set_uint32(dev, "width", graphic_width); qdev_prop_set_uint32(dev, "height", graphic_height); qdev_prop_set_uint8(dev, "depth", graphic_depth); - if (graphic_width == 1152 && graphic_height == 870 && graphic_depth == 8) { + if (graphic_width == 1152 && graphic_height == 870) { qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_APPLE_21_COLOR); } else { qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_VGA); From 5384adef5d5ac27d57f684e3937592d2074ce8b9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 14 Oct 2021 13:56:41 +0200 Subject: [PATCH 0665/1334] tests/acpi: Get prepared for IORT E.b revision upgrade Ignore IORT till reference blob for E.b spec revision gets added. Signed-off-by: Eric Auger Reviewed-by: Michael S. Tsirkin Message-Id: <20211014115643.756977-2-eric.auger@redhat.com> Signed-off-by: Richard Henderson --- tests/qtest/bios-tables-test-allowed-diff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..9a5a923d6b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/virt/IORT", From 1c2cb7e0b3e6743b449a4ad28d70b77d2290e817 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 14 Oct 2021 13:56:42 +0200 Subject: [PATCH 0666/1334] hw/arm/virt-acpi-build: IORT upgrade up to revision E.b Upgrade the IORT table from B to E.b specification revision (ARM DEN 0049E.b). The SMMUv3 and root complex node have additional fields. Also unique IORT node identifiers are introduced: they are generated in sequential order. They are not cross-referenced though. Signed-off-by: Eric Auger Reviewed-by: Jean-Philippe Brucker Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-Id: <20211014115643.756977-3-eric.auger@redhat.com> Signed-off-by: Richard Henderson --- hw/arm/virt-acpi-build.c | 48 ++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 6cec97352b..9c4730f4ab 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -241,19 +241,20 @@ static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) #endif #define ID_MAPPING_ENTRY_SIZE 20 -#define SMMU_V3_ENTRY_SIZE 60 -#define ROOT_COMPLEX_ENTRY_SIZE 32 +#define SMMU_V3_ENTRY_SIZE 68 +#define ROOT_COMPLEX_ENTRY_SIZE 36 #define IORT_NODE_OFFSET 48 static void build_iort_id_mapping(GArray *table_data, uint32_t input_base, uint32_t id_count, uint32_t out_ref) { - /* Identity RID mapping covering the whole input RID range */ + /* Table 4 ID mapping format */ build_append_int_noprefix(table_data, input_base, 4); /* Input base */ build_append_int_noprefix(table_data, id_count, 4); /* Number of IDs */ build_append_int_noprefix(table_data, input_base, 4); /* Output base */ build_append_int_noprefix(table_data, out_ref, 4); /* Output Reference */ - build_append_int_noprefix(table_data, 0, 4); /* Flags */ + /* Flags */ + build_append_int_noprefix(table_data, 0 /* Single mapping (disabled) */, 4); } struct AcpiIortIdMapping { @@ -298,7 +299,7 @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b) /* * Input Output Remapping Table (IORT) * Conforms to "IO Remapping Table System Software on ARM Platforms", - * Document number: ARM DEN 0049B, October 2015 + * Document number: ARM DEN 0049E.b, Feb 2021 */ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) @@ -307,10 +308,11 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) const uint32_t iort_node_offset = IORT_NODE_OFFSET; size_t node_size, smmu_offset = 0; AcpiIortIdMapping *idmap; + uint32_t id = 0; GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); - AcpiTable table = { .sig = "IORT", .rev = 0, .oem_id = vms->oem_id, + AcpiTable table = { .sig = "IORT", .rev = 3, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; /* Table 2 The IORT */ acpi_table_begin(&table, table_data); @@ -358,12 +360,12 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, IORT_NODE_OFFSET, 4); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ - /* 3.1.1.3 ITS group node */ + /* Table 12 ITS Group Format */ build_append_int_noprefix(table_data, 0 /* ITS Group */, 1); /* Type */ node_size = 20 /* fixed header size */ + 4 /* 1 GIC ITS Identifier */; build_append_int_noprefix(table_data, node_size, 2); /* Length */ - build_append_int_noprefix(table_data, 0, 1); /* Revision */ - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 1, 1); /* Revision */ + build_append_int_noprefix(table_data, id++, 4); /* Identifier */ build_append_int_noprefix(table_data, 0, 4); /* Number of ID mappings */ build_append_int_noprefix(table_data, 0, 4); /* Reference to ID Array */ build_append_int_noprefix(table_data, 1, 4); /* Number of ITSs */ @@ -374,19 +376,19 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; smmu_offset = table_data->len - table.table_offset; - /* 3.1.1.2 SMMUv3 */ + /* Table 9 SMMUv3 Format */ build_append_int_noprefix(table_data, 4 /* SMMUv3 */, 1); /* Type */ node_size = SMMU_V3_ENTRY_SIZE + ID_MAPPING_ENTRY_SIZE; build_append_int_noprefix(table_data, node_size, 2); /* Length */ - build_append_int_noprefix(table_data, 0, 1); /* Revision */ - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 4, 1); /* Revision */ + build_append_int_noprefix(table_data, id++, 4); /* Identifier */ build_append_int_noprefix(table_data, 1, 4); /* Number of ID mappings */ /* Reference to ID Array */ build_append_int_noprefix(table_data, SMMU_V3_ENTRY_SIZE, 4); /* Base address */ build_append_int_noprefix(table_data, vms->memmap[VIRT_SMMU].base, 8); /* Flags */ - build_append_int_noprefix(table_data, 1 /* COHACC OverrideNote */, 4); + build_append_int_noprefix(table_data, 1 /* COHACC Override */, 4); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ build_append_int_noprefix(table_data, 0, 8); /* VATOS address */ /* Model */ @@ -395,35 +397,43 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, irq + 1, 4); /* PRI */ build_append_int_noprefix(table_data, irq + 3, 4); /* GERR */ build_append_int_noprefix(table_data, irq + 2, 4); /* Sync */ + build_append_int_noprefix(table_data, 0, 4); /* Proximity domain */ + /* DeviceID mapping index (ignored since interrupts are GSIV based) */ + build_append_int_noprefix(table_data, 0, 4); /* output IORT node is the ITS group node (the first node) */ build_iort_id_mapping(table_data, 0, 0xFFFF, IORT_NODE_OFFSET); } - /* Table 16 Root Complex Node */ + /* Table 17 Root Complex Node */ build_append_int_noprefix(table_data, 2 /* Root complex */, 1); /* Type */ node_size = ROOT_COMPLEX_ENTRY_SIZE + ID_MAPPING_ENTRY_SIZE * rc_mapping_count; build_append_int_noprefix(table_data, node_size, 2); /* Length */ - build_append_int_noprefix(table_data, 0, 1); /* Revision */ - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 3, 1); /* Revision */ + build_append_int_noprefix(table_data, id++, 4); /* Identifier */ /* Number of ID mappings */ build_append_int_noprefix(table_data, rc_mapping_count, 4); /* Reference to ID Array */ build_append_int_noprefix(table_data, ROOT_COMPLEX_ENTRY_SIZE, 4); - /* Table 13 Memory access properties */ + /* Table 14 Memory access properties */ /* CCA: Cache Coherent Attribute */ build_append_int_noprefix(table_data, 1 /* fully coherent */, 4); build_append_int_noprefix(table_data, 0, 1); /* AH: Note Allocation Hints */ build_append_int_noprefix(table_data, 0, 2); /* Reserved */ - /* MAF: Note Memory Access Flags */ - build_append_int_noprefix(table_data, 0x3 /* CCA = CPM = DCAS = 1 */, 1); + /* Table 15 Memory Access Flags */ + build_append_int_noprefix(table_data, 0x3 /* CCA = CPM = DACS = 1 */, 1); build_append_int_noprefix(table_data, 0, 4); /* ATS Attribute */ /* MCFG pci_segment */ build_append_int_noprefix(table_data, 0, 4); /* PCI Segment number */ + /* Memory address size limit */ + build_append_int_noprefix(table_data, 64, 1); + + build_append_int_noprefix(table_data, 0, 3); /* Reserved */ + /* Output Reference */ if (vms->iommu == VIRT_IOMMU_SMMUV3) { AcpiIortIdMapping *range; From 47432863ff1892870730e78630b5d0dbed7e72b2 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 14 Oct 2021 13:56:43 +0200 Subject: [PATCH 0667/1334] tests/acpi: Generate reference blob for IORT rev E.b Re-generate reference blobs with rebuild-expected-aml.sh. Differences reported by "make check V=1" are listed below (IORT.numamem). Differences for other variants are similar. /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20180629 (64-bit version) * Copyright (c) 2000 - 2018 Intel Corporation * - * Disassembly of tests/data/acpi/virt/IORT.numamem, Thu Oct 14 06:13:19 2021 + * Disassembly of /tmp/aml-K8L9A1, Thu Oct 14 06:13:19 2021 * * ACPI Data Table [IORT] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "IORT" [IO Remapping Table] -[004h 0004 4] Table Length : 0000007C -[008h 0008 1] Revision : 00 -[009h 0009 1] Checksum : 07 +[004h 0004 4] Table Length : 00000080 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : B3 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 4] Node Count : 00000002 [028h 0040 4] Node Offset : 00000030 [02Ch 0044 4] Reserved : 00000000 [030h 0048 1] Type : 00 [031h 0049 2] Length : 0018 -[033h 0051 1] Revision : 00 +[033h 0051 1] Revision : 01 [034h 0052 4] Reserved : 00000000 [038h 0056 4] Mapping Count : 00000000 [03Ch 0060 4] Mapping Offset : 00000000 [040h 0064 4] ItsCount : 00000001 [044h 0068 4] Identifiers : 00000000 [048h 0072 1] Type : 02 -[049h 0073 2] Length : 0034 -[04Bh 0075 1] Revision : 00 -[04Ch 0076 4] Reserved : 00000000 +[049h 0073 2] Length : 0038 +[04Bh 0075 1] Revision : 03 +[04Ch 0076 4] Reserved : 00000001 [050h 0080 4] Mapping Count : 00000001 -[054h 0084 4] Mapping Offset : 00000020 +[054h 0084 4] Mapping Offset : 00000024 [058h 0088 8] Memory Properties : [IORT Memory Access Properties] [058h 0088 4] Cache Coherency : 00000001 [05Ch 0092 1] Hints (decoded below) : 00 Transient : 0 Write Allocate : 0 Read Allocate : 0 Override : 0 [05Dh 0093 2] Reserved : 0000 [05Fh 0095 1] Memory Flags (decoded below) : 03 Coherency : 1 Device Attribute : 1 [060h 0096 4] ATS Attribute : 00000000 [064h 0100 4] PCI Segment Number : 00000000 -[068h 0104 1] Memory Size Limit : 00 +[068h 0104 1] Memory Size Limit : 40 [069h 0105 3] Reserved : 000000 -[068h 0104 4] Input base : 00000000 -[06Ch 0108 4] ID Count : 0000FFFF -[070h 0112 4] Output Base : 00000000 -[074h 0116 4] Output Reference : 00000030 -[078h 0120 4] Flags (decoded below) : 00000000 +[06Ch 0108 4] Input base : 00000000 +[070h 0112 4] ID Count : 0000FFFF +[074h 0116 4] Output Base : 00000000 +[078h 0120 4] Output Reference : 00000030 +[07Ch 0124 4] Flags (decoded below) : 00000000 Single Mapping : 0 -Raw Table Data: Length 124 (0x7C) +Raw Table Data: Length 128 (0x80) - 0000: 49 4F 52 54 7C 00 00 00 00 07 42 4F 43 48 53 20 // IORT|.....BOCHS + 0000: 49 4F 52 54 80 00 00 00 03 B3 42 4F 43 48 53 20 // IORT......BOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC 0020: 01 00 00 00 02 00 00 00 30 00 00 00 00 00 00 00 // ........0....... - 0030: 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // ................ - 0040: 01 00 00 00 00 00 00 00 02 34 00 00 00 00 00 00 // .........4...... - 0050: 01 00 00 00 20 00 00 00 01 00 00 00 00 00 00 03 // .... ........... - 0060: 00 00 00 00 00 00 00 00 00 00 00 00 FF FF 00 00 // ................ - 0070: 00 00 00 00 30 00 00 00 00 00 00 00 // ....0....... + 0030: 00 18 00 01 00 00 00 00 00 00 00 00 00 00 00 00 // ................ + 0040: 01 00 00 00 00 00 00 00 02 38 00 03 01 00 00 00 // .........8...... + 0050: 01 00 00 00 24 00 00 00 01 00 00 00 00 00 00 03 // ....$........... + 0060: 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 // ........@....... + 0070: FF FF 00 00 00 00 00 00 30 00 00 00 00 00 00 00 // ........0....... ** Signed-off-by: Eric Auger Reviewed-by: Michael S. Tsirkin Message-Id: <20211014115643.756977-4-eric.auger@redhat.com> Signed-off-by: Richard Henderson --- tests/data/acpi/virt/IORT | Bin 124 -> 128 bytes tests/data/acpi/virt/IORT.memhp | Bin 124 -> 128 bytes tests/data/acpi/virt/IORT.numamem | Bin 124 -> 128 bytes tests/data/acpi/virt/IORT.pxb | Bin 124 -> 128 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 5 files changed, 1 deletion(-) diff --git a/tests/data/acpi/virt/IORT b/tests/data/acpi/virt/IORT index 521acefe9ba66706c5607321a82d330586f3f280..7efd0ce8a6b3928efa7e1373f688ab4c5f50543b 100644 GIT binary patch literal 128 zcmebD4+?2uU|?Y0?Bwt45v<@85#X!<1dKp25F11@0kHuPgMkDCNC*yK93~3}W)K^M VRiHGGVg_O`aDdYP|3ers^8jQz3IPBB literal 124 zcmebD4+^Pa00MR=e`k+i1*eDrX9XZ&1PX!JAesq?4S*O7Bw!2(4Uz`|CKCt^;wu0# QRGb+i3L*dhhtM#y0PN=p0RR91 diff --git a/tests/data/acpi/virt/IORT.memhp b/tests/data/acpi/virt/IORT.memhp index 521acefe9ba66706c5607321a82d330586f3f280..7efd0ce8a6b3928efa7e1373f688ab4c5f50543b 100644 GIT binary patch literal 128 zcmebD4+?2uU|?Y0?Bwt45v<@85#X!<1dKp25F11@0kHuPgMkDCNC*yK93~3}W)K^M VRiHGGVg_O`aDdYP|3ers^8jQz3IPBB literal 124 zcmebD4+^Pa00MR=e`k+i1*eDrX9XZ&1PX!JAesq?4S*O7Bw!2(4Uz`|CKCt^;wu0# QRGb+i3L*dhhtM#y0PN=p0RR91 diff --git a/tests/data/acpi/virt/IORT.numamem b/tests/data/acpi/virt/IORT.numamem index 521acefe9ba66706c5607321a82d330586f3f280..7efd0ce8a6b3928efa7e1373f688ab4c5f50543b 100644 GIT binary patch literal 128 zcmebD4+?2uU|?Y0?Bwt45v<@85#X!<1dKp25F11@0kHuPgMkDCNC*yK93~3}W)K^M VRiHGGVg_O`aDdYP|3ers^8jQz3IPBB literal 124 zcmebD4+^Pa00MR=e`k+i1*eDrX9XZ&1PX!JAesq?4S*O7Bw!2(4Uz`|CKCt^;wu0# QRGb+i3L*dhhtM#y0PN=p0RR91 diff --git a/tests/data/acpi/virt/IORT.pxb b/tests/data/acpi/virt/IORT.pxb index 521acefe9ba66706c5607321a82d330586f3f280..7efd0ce8a6b3928efa7e1373f688ab4c5f50543b 100644 GIT binary patch literal 128 zcmebD4+?2uU|?Y0?Bwt45v<@85#X!<1dKp25F11@0kHuPgMkDCNC*yK93~3}W)K^M VRiHGGVg_O`aDdYP|3ers^8jQz3IPBB literal 124 zcmebD4+^Pa00MR=e`k+i1*eDrX9XZ&1PX!JAesq?4S*O7Bw!2(4Uz`|CKCt^;wu0# QRGb+i3L*dhhtM#y0PN=p0RR91 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 9a5a923d6b..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/virt/IORT", From 99abb72520cb27f2318908831abc6915e286158b Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 15 Oct 2021 20:42:46 +0800 Subject: [PATCH 0668/1334] hw/arm/virt: Don't create device-tree node for empty NUMA node The empty NUMA node, where no memory resides, are allowed. For example, the following command line specifies two empty NUMA nodes. With this, QEMU fails to boot because of the conflicting device-tree node names, as the following error message indicates. /home/gavin/sandbox/qemu.main/build/qemu-system-aarch64 \ -accel kvm -machine virt,gic-version=host \ -cpu host -smp 4,sockets=2,cores=2,threads=1 \ -m 1024M,slots=16,maxmem=64G \ -object memory-backend-ram,id=mem0,size=512M \ -object memory-backend-ram,id=mem1,size=512M \ -numa node,nodeid=0,cpus=0-1,memdev=mem0 \ -numa node,nodeid=1,cpus=2-3,memdev=mem1 \ -numa node,nodeid=2 \ -numa node,nodeid=3 : qemu-system-aarch64: FDT: Failed to create subnode /memory@80000000: FDT_ERR_EXISTS As specified by linux device-tree binding document, the device-tree nodes for these empty NUMA nodes shouldn't be generated. However, the corresponding NUMA node IDs should be included in the distance map. The memory hotplug through device-tree on ARM64 isn't existing so far and it's not necessary to require the user to provide a distance map. Furthermore, the default distance map Linux generates may even be sufficient. So this simply skips populating the device-tree nodes for these empty NUMA nodes to avoid the error, so that QEMU can be started successfully. Signed-off-by: Gavin Shan Reviewed-by: Andrew Jones Message-Id: <20211015124246.23073-1-gshan@redhat.com> Signed-off-by: Richard Henderson --- hw/arm/boot.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 57efb61ee4..74ad397b1f 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -599,10 +599,23 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, } g_strfreev(node_path); + /* + * We drop all the memory nodes which correspond to empty NUMA nodes + * from the device tree, because the Linux NUMA binding document + * states they should not be generated. Linux will get the NUMA node + * IDs of the empty NUMA nodes from the distance map if they are needed. + * This means QEMU users may be obliged to provide command lines which + * configure distance maps when the empty NUMA node IDs are needed and + * Linux's default distance map isn't sufficient. + */ if (ms->numa_state != NULL && ms->numa_state->num_nodes > 0) { mem_base = binfo->loader_start; for (i = 0; i < ms->numa_state->num_nodes; i++) { mem_len = ms->numa_state->nodes[i].node_mem; + if (!mem_len) { + continue; + } + rc = fdt_add_memory_node(fdt, acells, mem_base, scells, mem_len, i); if (rc < 0) { From b152229697c8e3ef2655d29488eadacde6b2b309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 18 Oct 2021 12:58:15 +0200 Subject: [PATCH 0669/1334] roms/edk2: Only init brotli submodule to build BaseTools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since EDK2 BaseTools only require the brotli submodule, we don't need to initialize other submodules to build it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Gerd Hoffmann Message-Id: <20211018105816.2663195-2-philmd@redhat.com> Signed-off-by: Richard Henderson --- roms/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/roms/Makefile b/roms/Makefile index eeb5970348..b967b53bb7 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -143,7 +143,8 @@ build-efi-roms: build-pxe-roms # efirom # edk2-basetools: - cd edk2/BaseTools && git submodule update --init --force + cd edk2/BaseTools && git submodule update --init --force \ + Source/C/BrotliCompress/brotli $(MAKE) -C edk2/BaseTools \ PYTHON_COMMAND=$${EDK2_PYTHON_COMMAND:-python3} \ EXTRA_OPTFLAGS='$(EDK2_BASETOOLS_OPTFLAGS)' \ From bd0da3a3d4f5e51856b5716508df9a4fdbbd416c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 18 Oct 2021 12:58:16 +0200 Subject: [PATCH 0670/1334] roms/edk2: Only initialize required submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The EDK2 firmware images built to test QEMU do not require the following submodules: - MdeModulePkg/Universal/RegularExpressionDxe/oniguruma - UnitTestFrameworkPkg/Library/CmockaLib/cmocka The only submodules required are: - ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 - BaseTools/Source/C/BrotliCompress/brotli - CryptoPkg/Library/OpensslLib/openssl - MdeModulePkg/Library/BrotliCustomDecompressLib/brotli Adapt the buildsys machinery to only initialize the required submodules. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Gerd Hoffmann Message-Id: <20211018105816.2663195-3-philmd@redhat.com> Signed-off-by: Richard Henderson --- .gitlab-ci.d/edk2.yml | 6 +++++- roms/Makefile.edk2 | 7 ++++++- scripts/make-release | 7 ++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/edk2.yml b/.gitlab-ci.d/edk2.yml index 62497ba47f..13d0f8b019 100644 --- a/.gitlab-ci.d/edk2.yml +++ b/.gitlab-ci.d/edk2.yml @@ -50,7 +50,11 @@ build-edk2: GIT_DEPTH: 3 script: # Clone the required submodules and build EDK2 - git submodule update --init roms/edk2 - - git -C roms/edk2 submodule update --init + - git -C roms/edk2 submodule update --init -- + ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 + BaseTools/Source/C/BrotliCompress/brotli + CryptoPkg/Library/OpensslLib/openssl + MdeModulePkg/Library/BrotliCustomDecompressLib/brotli - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) - echo "=== Using ${JOBS} simultaneous jobs ===" - make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2 diff --git a/roms/Makefile.edk2 b/roms/Makefile.edk2 index a8ed325575..fdae0b511f 100644 --- a/roms/Makefile.edk2 +++ b/roms/Makefile.edk2 @@ -51,7 +51,12 @@ all: $(foreach flashdev,$(flashdevs),../pc-bios/edk2-$(flashdev).fd.bz2) \ # make-release/tarball scripts. submodules: if test -d edk2/.git; then \ - cd edk2 && git submodule update --init --force; \ + cd edk2 && git submodule update --init --force -- \ + ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ + BaseTools/Source/C/BrotliCompress/brotli \ + CryptoPkg/Library/OpensslLib/openssl \ + MdeModulePkg/Library/BrotliCustomDecompressLib/brotli \ + ; \ fi # See notes on the ".NOTPARALLEL" target and the "+" indicator in diff --git a/scripts/make-release b/scripts/make-release index a2a8cda33c..05b14ecc95 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -27,7 +27,12 @@ git submodule update --init # don't necessarily have much control over how a submodule handles its # submodule dependencies, so we continue to handle these on a case-by-case # basis for now. -(cd roms/edk2 && git submodule update --init) +(cd roms/edk2 && \ + git submodule update --init -- \ + ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ + BaseTools/Source/C/BrotliCompress/brotli \ + CryptoPkg/Library/OpensslLib/openssl \ + MdeModulePkg/Library/BrotliCustomDecompressLib/brotli) popd tar --exclude=.git -cjf ${destination}.tar.bz2 ${destination} rm -rf ${destination} From b84722cf4455b44a98b5a527067001dee58ace10 Mon Sep 17 00:00:00 2001 From: Shuuichirou Ishii Date: Fri, 8 Oct 2021 15:36:04 +0900 Subject: [PATCH 0671/1334] hw/arm/sbsa-ref: Fixed cpu type error message typo. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Shuuichirou Ishii Reviewed-by: Alex Bennée Reviewed-by: Yanan Wang Message-Id: <20211008063604.670699-1-ishii.shuuichir@fujitsu.com> Signed-off-by: Richard Henderson --- hw/arm/sbsa-ref.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 509c5f09b4..358714bd3e 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -670,7 +670,7 @@ static void sbsa_ref_init(MachineState *machine) int n, sbsa_max_cpus; if (!cpu_type_valid(machine->cpu_type)) { - error_report("mach-virt: CPU type %s not supported", machine->cpu_type); + error_report("sbsa-ref: CPU type %s not supported", machine->cpu_type); exit(1); } From 8975eb891fb6df56442763acf2bdb7c03b0933bf Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Thu, 14 Oct 2021 21:43:25 +0200 Subject: [PATCH 0672/1334] hw/elf_ops.h: switch to ssize_t for elf loader return type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now, int was used as the return type for all the ELF loader related functions. The returned value is the sum of all loaded program headers "MemSize" fields. Because of the overflow check in elf_ops.h, trying to load an ELF bigger than INT_MAX will fail. Switch to ssize_t to remove this limitation. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Stefano Garzarella Message-Id: <20211014194325.19917-1-lmichel@kalray.eu> Signed-off-by: Richard Henderson --- hw/core/loader.c | 60 +++++++++++++++++++++++--------------------- include/hw/elf_ops.h | 27 ++++++++++---------- include/hw/loader.h | 58 +++++++++++++++++++++--------------------- 3 files changed, 74 insertions(+), 71 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index c623318b73..c7f97fdce8 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -326,7 +326,7 @@ static void *load_at(int fd, off_t offset, size_t size) #define SZ 64 #include "hw/elf_ops.h" -const char *load_elf_strerror(int error) +const char *load_elf_strerror(ssize_t error) { switch (error) { case 0: @@ -402,12 +402,12 @@ fail: } /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab) +ssize_t load_elf(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab) { return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, pentry, lowaddr, highaddr, pflags, big_endian, @@ -415,12 +415,13 @@ int load_elf(const char *filename, } /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf_as(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab, AddressSpace *as) +ssize_t load_elf_as(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as) { return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, pentry, lowaddr, highaddr, pflags, big_endian, @@ -428,13 +429,13 @@ int load_elf_as(const char *filename, } /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom) +ssize_t load_elf_ram(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, + int big_endian, int elf_machine, int clear_lsb, + int data_swab, AddressSpace *as, bool load_rom) { return load_elf_ram_sym(filename, elf_note_fn, translate_fn, translate_opaque, @@ -444,16 +445,17 @@ int load_elf_ram(const char *filename, } /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf_ram_sym(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, - int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) +ssize_t load_elf_ram_sym(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + uint32_t *pflags, int big_endian, int elf_machine, + int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { - int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; + int fd, data_order, target_data_order, must_swab; + ssize_t ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; fd = open(filename, O_RDONLY | O_BINARY); diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index 1c37cec4ae..995de8495c 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -312,25 +312,26 @@ static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, return nhdr; } -static int glue(load_elf, SZ)(const char *name, int fd, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, - int must_swab, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int elf_machine, - int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom, - symbol_fn_t sym_cb) +static ssize_t glue(load_elf, SZ)(const char *name, int fd, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, + int must_swab, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + uint32_t *pflags, int elf_machine, + int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, + symbol_fn_t sym_cb) { struct elfhdr ehdr; struct elf_phdr *phdr = NULL, *ph; - int size, i, total_size; + int size, i; + ssize_t total_size; elf_word mem_size, file_size, data_offset; uint64_t addr, low = (uint64_t)-1, high = 0; GMappedFile *mapped_file = NULL; uint8_t *data = NULL; - int ret = ELF_LOAD_FAILED; + ssize_t ret = ELF_LOAD_FAILED; if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) goto fail; @@ -482,7 +483,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, } } - if (mem_size > INT_MAX - total_size) { + if (mem_size > SSIZE_MAX - total_size) { ret = ELF_LOAD_TOO_BIG; goto fail; } diff --git a/include/hw/loader.h b/include/hw/loader.h index 81104cb02f..4fa485bd61 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -90,7 +90,7 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); #define ELF_LOAD_WRONG_ARCH -3 #define ELF_LOAD_WRONG_ENDIAN -4 #define ELF_LOAD_TOO_BIG -5 -const char *load_elf_strerror(int error); +const char *load_elf_strerror(ssize_t error); /** load_elf_ram_sym: * @filename: Path of ELF file @@ -128,48 +128,48 @@ const char *load_elf_strerror(int error); typedef void (*symbol_fn_t)(const char *st_name, int st_info, uint64_t st_value, uint64_t st_size); -int load_elf_ram_sym(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, - int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); +ssize_t load_elf_ram_sym(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + uint32_t *pflags, int big_endian, int elf_machine, + int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); /** load_elf_ram: * Same as load_elf_ram_sym(), but doesn't allow the caller to specify a * symbol callback function */ -int load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom); +ssize_t load_elf_ram(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, + int big_endian, int elf_machine, int clear_lsb, + int data_swab, AddressSpace *as, bool load_rom); /** load_elf_as: * Same as load_elf_ram(), but always loads the elf as ROM */ -int load_elf_as(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab, - AddressSpace *as); +ssize_t load_elf_as(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as); /** load_elf: * Same as load_elf_as(), but doesn't allow the caller to specify an * AddressSpace. */ -int load_elf(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab); +ssize_t load_elf(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab); /** load_elf_hdr: * @filename: Path of ELF file From 621f70d21027a914eda1446134193a24e7a662d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 4 Oct 2021 23:21:41 +0200 Subject: [PATCH 0673/1334] spapr/xive: Add source status helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and use them to set and test the ASSERTED bit of LSI sources. Signed-off-by: Cédric Le Goater Message-Id: <20211004212141.432954-1-clg@kaod.org> Signed-off-by: David Gibson --- hw/intc/spapr_xive.c | 2 +- hw/intc/spapr_xive_kvm.c | 10 +++------- hw/intc/xive.c | 8 ++++---- include/hw/ppc/xive.h | 24 ++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 89cfa018f5..4ec659b93e 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -185,7 +185,7 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", pq & XIVE_ESB_VAL_P ? 'P' : '-', pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ', + xive_source_is_asserted(xsrc, i) ? 'A' : ' ', xive_eas_is_masked(eas) ? "M" : " ", (int) xive_get_field64(EAS_END_DATA, eas->w)); diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index 6d4909d0a8..be94cff148 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -242,7 +242,7 @@ int kvmppc_xive_source_reset_one(XiveSource *xsrc, int srcno, Error **errp) if (xive_source_irq_is_lsi(xsrc, srcno)) { state |= KVM_XIVE_LEVEL_SENSITIVE; - if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + if (xive_source_is_asserted(xsrc, srcno)) { state |= KVM_XIVE_LEVEL_ASSERTED; } } @@ -321,7 +321,7 @@ uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset, if (xive_source_irq_is_lsi(xsrc, srcno) && offset == XIVE_ESB_LOAD_EOI) { xive_esb_read(xsrc, srcno, XIVE_ESB_SET_PQ_00); - if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + if (xive_source_is_asserted(xsrc, srcno)) { kvmppc_xive_esb_trigger(xsrc, srcno); } return 0; @@ -359,11 +359,7 @@ void kvmppc_xive_source_set_irq(void *opaque, int srcno, int val) return; } } else { - if (val) { - xsrc->status[srcno] |= XIVE_STATUS_ASSERTED; - } else { - xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED; - } + xive_source_set_asserted(xsrc, srcno, val); } kvmppc_xive_esb_trigger(xsrc, srcno); diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 6c82326ec7..190194d27f 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -875,7 +875,7 @@ static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno) { uint8_t old_pq = xive_source_esb_get(xsrc, srcno); - xsrc->status[srcno] |= XIVE_STATUS_ASSERTED; + xive_source_set_asserted(xsrc, srcno, true); switch (old_pq) { case XIVE_ESB_RESET: @@ -923,7 +923,7 @@ static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno) * notification */ if (xive_source_irq_is_lsi(xsrc, srcno) && - xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + xive_source_is_asserted(xsrc, srcno)) { ret = xive_source_lsi_trigger(xsrc, srcno); } @@ -1104,7 +1104,7 @@ void xive_source_set_irq(void *opaque, int srcno, int val) if (val) { notify = xive_source_lsi_trigger(xsrc, srcno); } else { - xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED; + xive_source_set_asserted(xsrc, srcno, false); } } else { if (val) { @@ -1133,7 +1133,7 @@ void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon) xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", pq & XIVE_ESB_VAL_P ? 'P' : '-', pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' '); + xive_source_is_asserted(xsrc, i) ? 'A' : ' '); } } diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 252c58a1d6..b8ab0bf749 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -286,6 +286,30 @@ uint8_t xive_esb_set(uint8_t *pq, uint8_t value); uint8_t xive_source_esb_get(XiveSource *xsrc, uint32_t srcno); uint8_t xive_source_esb_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq); +/* + * Source status helpers + */ +static inline void xive_source_set_status(XiveSource *xsrc, uint32_t srcno, + uint8_t status, bool enable) +{ + if (enable) { + xsrc->status[srcno] |= status; + } else { + xsrc->status[srcno] &= ~status; + } +} + +static inline void xive_source_set_asserted(XiveSource *xsrc, uint32_t srcno, + bool enable) +{ + xive_source_set_status(xsrc, srcno, XIVE_STATUS_ASSERTED, enable); +} + +static inline bool xive_source_is_asserted(XiveSource *xsrc, uint32_t srcno) +{ + return xsrc->status[srcno] & XIVE_STATUS_ASSERTED; +} + void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon); From 6f4912a4160f157217730b0affdcb6c92c24ca76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 16:17:10 +0200 Subject: [PATCH 0674/1334] target/ppc: Use tcg_constant_i32() in gen_setb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using TCG temporaries for the -1 and 8 constant values. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211003141711.3673181-2-f4bug@amsat.org> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index c3c6cb9589..0258c1be16 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -5068,19 +5068,15 @@ static void gen_mtspr(DisasContext *ctx) static void gen_setb(DisasContext *ctx) { TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t8 = tcg_temp_new_i32(); - TCGv_i32 tm1 = tcg_temp_new_i32(); + TCGv_i32 t8 = tcg_constant_i32(8); + TCGv_i32 tm1 = tcg_constant_i32(-1); int crf = crfS(ctx->opcode); tcg_gen_setcondi_i32(TCG_COND_GEU, t0, cpu_crf[crf], 4); - tcg_gen_movi_i32(t8, 8); - tcg_gen_movi_i32(tm1, -1); tcg_gen_movcond_i32(TCG_COND_GEU, t0, cpu_crf[crf], t8, tm1, t0); tcg_gen_ext_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); tcg_temp_free_i32(t0); - tcg_temp_free_i32(t8); - tcg_temp_free_i32(tm1); } #endif From 491b3cca3653bf36db67f91be0e3db64682bef91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Oct 2021 16:17:11 +0200 Subject: [PATCH 0675/1334] target/ppc: Use tcg_constant_i64() in gen_brh() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mask of the Byte-Reverse Halfword opcode is a read-only constant. We can avoid using a TCG temporary by moving the mask to the constant pool. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211003141711.3673181-3-f4bug@amsat.org> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 0258c1be16..98f304302e 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7569,18 +7569,16 @@ static void gen_brw(DisasContext *ctx) /* brh */ static void gen_brh(DisasContext *ctx) { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 mask = tcg_constant_i64(0x00ff00ff00ff00ffull); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_movi_i64(t0, 0x00ff00ff00ff00ffull); tcg_gen_shri_i64(t1, cpu_gpr[rS(ctx->opcode)], 8); - tcg_gen_and_i64(t2, t1, t0); - tcg_gen_and_i64(t1, cpu_gpr[rS(ctx->opcode)], t0); + tcg_gen_and_i64(t2, t1, mask); + tcg_gen_and_i64(t1, cpu_gpr[rS(ctx->opcode)], mask); tcg_gen_shli_i64(t1, t1, 8); tcg_gen_or_i64(cpu_gpr[rA(ctx->opcode)], t1, t2); - tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); } From a8dcb8da8a95edde3d6d8bb6a0502d50ed632557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 5 Oct 2021 07:33:24 +0200 Subject: [PATCH 0676/1334] target/ppc: Fix the test raising the decrementer exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 4d9b8ef9b5ab ("target/ppc: Fix 64-bit decrementer") introduced new int64t variables and broke the test triggering the decrementer exception. Revert partially the change to evaluate both clause of the if statement. Reported-by: Coverity CID 1464061 Fixes: 4d9b8ef9b5ab ("target/ppc: Fix 64-bit decrementer") Suggested-by: Peter Maydell Signed-off-by: Cédric Le Goater Message-Id: <20211005053324.441132-1-clg@kaod.org> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/ppc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index f5d012f860..a724b0bb5e 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -848,7 +848,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers * an edge interrupt, so raise it here too. */ - if ((signed_value < 3) || + if ((value < 3) || ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) || ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0 && signed_decr >= 0)) { From 3c706d02522fb031e79823fd14b121878dccbcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Oct 2021 19:08:01 +0200 Subject: [PATCH 0677/1334] hw/ppc/spapr_softmmu: Reduce include list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 962104f0448 ("hw/ppc: moved hcalls that depend on softmmu") introduced a lot of unnecessary #include directives. Remove them. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211006170801.178023-1-philmd@redhat.com> Signed-off-by: David Gibson --- hw/ppc/spapr_softmmu.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/hw/ppc/spapr_softmmu.c b/hw/ppc/spapr_softmmu.c index 6c6b86dd3c..f8924270ef 100644 --- a/hw/ppc/spapr_softmmu.c +++ b/hw/ppc/spapr_softmmu.c @@ -1,25 +1,10 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "qapi/error.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "qemu/error-report.h" #include "cpu.h" -#include "exec/exec-all.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_cpu_core.h" #include "mmu-hash64.h" -#include "cpu-models.h" -#include "trace.h" -#include "kvm_ppc.h" -#include "hw/ppc/fdt.h" -#include "hw/ppc/spapr_ovec.h" #include "mmu-book3s-v3.h" -#include "hw/mem/memory-device.h" static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) { From 644c68696e8335f80d4a9295db0445505e24d8e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 6 Oct 2021 23:05:46 +0200 Subject: [PATCH 0678/1334] spapr/xive: Use xive_esb_rw() to trigger interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xive_esb_rw() is the common routine used for memory accesses on ESB page. Use it for triggers also. Signed-off-by: Cédric Le Goater Message-Id: <20211006210546.641102-1-clg@kaod.org> Signed-off-by: David Gibson --- hw/intc/spapr_xive_kvm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index be94cff148..61fe7bd2d3 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -301,9 +301,7 @@ static uint8_t xive_esb_read(XiveSource *xsrc, int srcno, uint32_t offset) static void kvmppc_xive_esb_trigger(XiveSource *xsrc, int srcno) { - uint64_t *addr = xsrc->esb_mmap + xive_source_esb_page(xsrc, srcno); - - *addr = 0x0; + xive_esb_rw(xsrc, srcno, 0, 0, true); } uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset, From 5ae3d2e8ba37def4b3ca38f220200bf5721317e0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 6 Oct 2021 09:11:40 +0200 Subject: [PATCH 0679/1334] hw/ppc: Fix iothread locking in the 405 code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using u-boot as firmware with the taihu board, QEMU aborts with this assertion: ERROR:../accel/tcg/tcg-accel-ops.c:79:tcg_handle_interrupt: assertion failed: (qemu_mutex_iothread_locked()) Running QEMU with "-d in_asm" shows that the crash happens when writing to SPR 0x3f2, so we are missing to lock the iothread in the code path here. Signed-off-by: Thomas Huth Message-Id: <20211006071140.565952-1-thuth@redhat.com> Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/ppc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index a724b0bb5e..e8127599c9 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -336,6 +336,8 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) { PowerPCCPU *cpu = env_archcpu(env); + qemu_mutex_lock_iothread(); + switch ((val >> 28) & 0x3) { case 0x0: /* No action */ @@ -353,6 +355,8 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) ppc40x_system_reset(cpu); break; } + + qemu_mutex_unlock_iothread(); } /* PowerPC 40x internal IRQ controller */ From 252fcf36bba483493365e91c2f98569de29a43fd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 11 Oct 2021 14:59:30 +0200 Subject: [PATCH 0680/1334] tests/acceptance: Add tests for the ppc405 boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the U-Boot firmware, we can check that at least the serial console of the ppc405 boards is still usable. Signed-off-by: Thomas Huth Message-Id: <20211011125930.750217-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé [dwg: Added an extra tag at Philippe's suggestion] Signed-off-by: David Gibson --- tests/acceptance/ppc_405.py | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/acceptance/ppc_405.py diff --git a/tests/acceptance/ppc_405.py b/tests/acceptance/ppc_405.py new file mode 100644 index 0000000000..c534d5d32f --- /dev/null +++ b/tests/acceptance/ppc_405.py @@ -0,0 +1,42 @@ +# Test that the U-Boot firmware boots on ppc 405 machines and check the console +# +# Copyright (c) 2021 Red Hat, Inc. +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command_and_wait_for_pattern + +class Ppc405Machine(Test): + + timeout = 90 + + def do_test_ppc405(self): + uboot_url = ('https://gitlab.com/huth/u-boot/-/raw/' + 'taihu-2021-10-09/u-boot-taihu.bin') + uboot_hash = ('3208940e908a5edc7c03eab072c60f0dcfadc2ab'); + file_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) + self.vm.set_console(console_index=1) + self.vm.add_args('-bios', file_path) + self.vm.launch() + wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board') + exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') + + def test_ppc_taihu(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:taihu + :avocado: tags=cpu:405ep + """ + self.do_test_ppc405() + + def test_ppc_ref405ep(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:ref405ep + :avocado: tags=cpu:405ep + """ + self.do_test_ppc405() From 66c6b40aba13807506f20c7522f4930c9ffc76ce Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 14 Oct 2021 19:32:31 -0300 Subject: [PATCH 0681/1334] linux-user/ppc: Fix XER access in save/restore_user_regs We should use cpu_read_xer/cpu_write_xer to save/restore the complete register since some of its bits are in other fields of CPUPPCState. A test is added to prevent future regressions. Fixes: da91a00f191f ("target-ppc: Split out SO, OV, CA fields from XER") Signed-off-by: Matheus Ferst Message-Id: <20211014223234.127012-2-matheus.ferst@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- linux-user/ppc/signal.c | 9 +++-- tests/tcg/ppc64/Makefile.target | 2 + tests/tcg/ppc64le/Makefile.target | 2 + tests/tcg/ppc64le/signal_save_restore_xer.c | 42 +++++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 tests/tcg/ppc64le/signal_save_restore_xer.c diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index c37744c8fc..90a0369632 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -242,7 +242,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) __put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); + __put_user(cpu_read_xer(env), &frame->mc_gregs[TARGET_PT_XER]); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { ccr |= env->crf[i] << (32 - ((i + 1) * 4)); @@ -315,6 +315,7 @@ static void restore_user_regs(CPUPPCState *env, { target_ulong save_r2 = 0; target_ulong msr; + target_ulong xer; target_ulong ccr; int i; @@ -330,9 +331,11 @@ static void restore_user_regs(CPUPPCState *env, __get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); __get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); __get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); - __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); + __get_user(xer, &frame->mc_gregs[TARGET_PT_XER]); + cpu_write_xer(env, xer); + + __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; } diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index a6a4ddaeca..6ab7934fdf 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -23,4 +23,6 @@ run-plugin-byte_reverse-with-%: $(call skip-test, "RUN of byte_reverse ($*)", "not built") endif +PPC64_TESTS += signal_save_restore_xer + TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/ppc64le/Makefile.target b/tests/tcg/ppc64le/Makefile.target index c0c14ffbad..5e65b1590d 100644 --- a/tests/tcg/ppc64le/Makefile.target +++ b/tests/tcg/ppc64le/Makefile.target @@ -22,4 +22,6 @@ run-plugin-byte_reverse-with-%: $(call skip-test, "RUN of byte_reverse ($*)", "not built") endif +PPC64LE_TESTS += signal_save_restore_xer + TESTS += $(PPC64LE_TESTS) diff --git a/tests/tcg/ppc64le/signal_save_restore_xer.c b/tests/tcg/ppc64le/signal_save_restore_xer.c new file mode 100644 index 0000000000..e4f8a07dd7 --- /dev/null +++ b/tests/tcg/ppc64le/signal_save_restore_xer.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +#define XER_SO (1 << 31) +#define XER_OV (1 << 30) +#define XER_CA (1 << 29) +#define XER_OV32 (1 << 19) +#define XER_CA32 (1 << 18) + +uint64_t saved; + +void sigill_handler(int sig, siginfo_t *si, void *ucontext) +{ + ucontext_t *uc = ucontext; + uc->uc_mcontext.regs->nip += 4; + saved = uc->uc_mcontext.regs->xer; + uc->uc_mcontext.regs->xer |= XER_OV | XER_OV32; +} + +int main(void) +{ + uint64_t initial = XER_CA | XER_CA32, restored; + struct sigaction sa = { + .sa_sigaction = sigill_handler, + .sa_flags = SA_SIGINFO + }; + + sigaction(SIGILL, &sa, NULL); + + asm("mtspr 1, %1\n\t" + ".long 0x0\n\t" + "mfspr %0, 1\n\t" + : "=r" (restored) + : "r" (initial)); + + assert(saved == initial); + assert(restored == (XER_OV | XER_OV32 | XER_CA | XER_CA32)); + + return 0; +} From 7974dc5900f7c128232782b0b39ccd40001bdb08 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 14 Oct 2021 19:32:32 -0300 Subject: [PATCH 0682/1334] target/ppc: Fix XER access in gdbstub The value of XER is split in multiple fields of CPUPPCState, like env->xer and env->so. To get/set the whole register from gdb, we should use cpu_read_xer/cpu_write_xer. Fixes: da91a00f191f ("target-ppc: Split out SO, OV, CA fields from XER") Signed-off-by: Matheus Ferst Message-Id: <20211014223234.127012-3-matheus.ferst@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/gdbstub.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 1808a150e4..105c2f7dd1 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -159,7 +159,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) gdb_get_regl(buf, env->ctr); break; case 69: - gdb_get_reg32(buf, env->xer); + gdb_get_reg32(buf, cpu_read_xer(env)); break; case 70: gdb_get_reg32(buf, env->fpscr); @@ -217,7 +217,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) gdb_get_reg64(buf, env->ctr); break; case 69 + 32: - gdb_get_reg32(buf, env->xer); + gdb_get_reg32(buf, cpu_read_xer(env)); break; case 70 + 32: gdb_get_reg64(buf, env->fpscr); @@ -269,7 +269,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldtul_p(mem_buf); break; case 69: - env->xer = ldl_p(mem_buf); + cpu_write_xer(env, ldl_p(mem_buf)); break; case 70: /* fpscr */ @@ -319,7 +319,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldq_p(mem_buf); break; case 69 + 32: - env->xer = ldl_p(mem_buf); + cpu_write_xer(env, ldl_p(mem_buf)); break; case 70 + 32: /* fpscr */ From 10de0521889d36633450e35b22f6a45ef856226d Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 14 Oct 2021 19:32:33 -0300 Subject: [PATCH 0683/1334] linux-user: Fix XER access in ppc version of elf_core_copy_regs env->xer doesn't hold some bits of XER, like OV and CA. To write the complete register in the core dump we should read XER value with cpu_read_xer. Reported-by: Lucas Mateus Castro (alqotel) Fixes: da91a00f191f ("target-ppc: Split out SO, OV, CA fields from XER") Signed-off-by: Matheus Ferst Message-Id: <20211014223234.127012-4-matheus.ferst@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- linux-user/elfload.c | 2 +- target/ppc/cpu.c | 2 +- target/ppc/cpu.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2404d482ba..eb32f3e2cb 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -901,7 +901,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en (*regs)[33] = tswapreg(env->msr); (*regs)[35] = tswapreg(env->ctr); (*regs)[36] = tswapreg(env->lr); - (*regs)[37] = tswapreg(env->xer); + (*regs)[37] = tswapreg(cpu_read_xer(env)); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { ccr |= env->crf[i] << (32 - ((i + 1) * 4)); diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index 7ad9bd6044..f933d9f2bd 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -27,7 +27,7 @@ #include "helper_regs.h" #include "sysemu/tcg.h" -target_ulong cpu_read_xer(CPUPPCState *env) +target_ulong cpu_read_xer(const CPUPPCState *env) { if (is_isa300(env)) { return env->xer | (env->so << XER_SO) | diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index baa4e7c34d..c6fc0043a9 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2412,7 +2412,7 @@ enum { /*****************************************************************************/ #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) -target_ulong cpu_read_xer(CPUPPCState *env); +target_ulong cpu_read_xer(const CPUPPCState *env); void cpu_write_xer(CPUPPCState *env, target_ulong xer); /* From 3938cacdb2367c203fd796af3f8c70cdb70c5007 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 14 Oct 2021 19:32:34 -0300 Subject: [PATCH 0684/1334] target/ppc: Fix XER access in monitor We can't read env->xer directly, as it does not contain some bits of XER. Instead, we should have a callback that uses cpu_read_xer to read the complete register. Fixes: da91a00f191f ("target-ppc: Split out SO, OV, CA fields from XER") Signed-off-by: Matheus Ferst Message-Id: <20211014223234.127012-5-matheus.ferst@eldorado.org.br> Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/monitor.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c index a475108b2d..0b805ef6e9 100644 --- a/target/ppc/monitor.c +++ b/target/ppc/monitor.c @@ -44,6 +44,13 @@ static target_long monitor_get_ccr(Monitor *mon, const struct MonitorDef *md, return u; } +static target_long monitor_get_xer(Monitor *mon, const struct MonitorDef *md, + int val) +{ + CPUArchState *env = mon_get_cpu_env(mon); + return cpu_read_xer(env); +} + static target_long monitor_get_decr(Monitor *mon, const struct MonitorDef *md, int val) { @@ -85,7 +92,7 @@ const MonitorDef monitor_defs[] = { { "decr", 0, &monitor_get_decr, }, { "ccr|cr", 0, &monitor_get_ccr, }, /* Machine state register */ - { "xer", offsetof(CPUPPCState, xer) }, + { "xer", 0, &monitor_get_xer }, { "msr", offsetof(CPUPPCState, msr) }, { "tbu", 0, &monitor_get_tbu, }, { "tbl", 0, &monitor_get_tbl, }, From 239fec2497907f2adf7e6b9fdda4138e81bac619 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 14 Oct 2021 21:50:19 +0200 Subject: [PATCH 0685/1334] ppc/pegasos2: Restrict memory to 2 gigabytes The CHRP spec this board confirms to only allows 2 GiB of system memory below 4 GiB as the high 2 GiB is allocated to IO and system resources. To avoid problems with memory overlapping these areas restrict RAM to 2 GiB similar to mac_newworld. Signed-off-by: BALATON Zoltan Message-Id: <54f58229a69c9c1cca21bcecad700b3d7052edd5.1634241019.git.balaton@eik.bme.hu> Signed-off-by: David Gibson --- hw/ppc/pegasos2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index b8ce859f1a..474cfdeabf 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -117,6 +117,10 @@ static void pegasos2_init(MachineState *machine) qemu_register_reset(pegasos2_cpu_reset, pm->cpu); /* RAM */ + if (machine->ram_size > 2 * GiB) { + error_report("RAM size more than 2 GiB is not supported"); + exit(1); + } memory_region_add_subregion(get_system_memory(), 0, machine->ram); /* allocate and load firmware */ From 99173b679a346d6a92dbb685adecf5c419288c0c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 14 Oct 2021 21:50:19 +0200 Subject: [PATCH 0686/1334] ppc/pegasos2: Warn when using VOF but no kernel is specified Issue a warning when using VOF (which is the default) but no -kernel option given to let users know that it will likely fail as the guest has nothing to run. It is not a hard error because it may still be useful to start the machine without further options for testing or inspecting it from monitor without actually booting it. Signed-off-by: BALATON Zoltan Message-Id: Signed-off-by: David Gibson --- hw/ppc/pegasos2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 474cfdeabf..a1dd1f6752 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -194,7 +194,10 @@ static void pegasos2_init(MachineState *machine) if (!pm->vof) { warn_report("Option -kernel may be ineffective with -bios."); } + } else if (pm->vof) { + warn_report("Using Virtual OpenFirmware but no -kernel option."); } + if (!pm->vof && machine->kernel_cmdline && machine->kernel_cmdline[0]) { warn_report("Option -append may be ineffective with -bios."); } From 94cd1ffbe1a8b7f08df76e14d9226804cc21b56c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 14 Oct 2021 21:50:19 +0200 Subject: [PATCH 0687/1334] ppc/pegasos2: Implement get-time-of-day RTAS function with VOF This is needed for Linux to access RTC time. Signed-off-by: BALATON Zoltan Message-Id: <6233eb07c680d6c74427e11b9641958f98d53378.1634241019.git.balaton@eik.bme.hu> Signed-off-by: David Gibson --- hw/ppc/pegasos2.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index a1dd1f6752..a9e3625f56 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -31,6 +31,8 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "exec/address-spaces.h" +#include "qom/qom-qobject.h" +#include "qapi/qmp/qdict.h" #include "trace.h" #include "qemu/datadir.h" #include "sysemu/device_tree.h" @@ -369,6 +371,29 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, return H_PARAMETER; } switch (token) { + case RTAS_GET_TIME_OF_DAY: + { + QObject *qo = object_property_get_qobject(qdev_get_machine(), + "rtc-time", &error_fatal); + QDict *qd = qobject_to(QDict, qo); + + if (nargs != 0 || nrets != 8 || !qd) { + stl_be_phys(as, rets, -1); + qobject_unref(qo); + return H_PARAMETER; + } + + stl_be_phys(as, rets, 0); + stl_be_phys(as, rets + 4, qdict_get_int(qd, "tm_year") + 1900); + stl_be_phys(as, rets + 8, qdict_get_int(qd, "tm_mon") + 1); + stl_be_phys(as, rets + 12, qdict_get_int(qd, "tm_mday")); + stl_be_phys(as, rets + 16, qdict_get_int(qd, "tm_hour")); + stl_be_phys(as, rets + 20, qdict_get_int(qd, "tm_min")); + stl_be_phys(as, rets + 24, qdict_get_int(qd, "tm_sec")); + stl_be_phys(as, rets + 28, 0); + qobject_unref(qo); + return H_SUCCESS; + } case RTAS_READ_PCI_CONFIG: { uint32_t addr, len, val; From bd20cde50bb1a3b47fcba02432edc6141d9fb1d0 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 14 Oct 2021 21:50:19 +0200 Subject: [PATCH 0688/1334] ppc/pegasos2: Access MV64361 registers via their memory region Instead of relying on the mapped address of the MV64361 registers access them via their memory region. This is not a problem at reset time when these registers are mapped at the default address but the guest could change this later and then the RTAS calls accessing PCI config registers could fail. None of the guests actually do this so this only avoids a theoretical problem not seen in practice. Signed-off-by: BALATON Zoltan Message-Id: Signed-off-by: David Gibson --- hw/pci-host/mv64361.c | 1 + hw/ppc/pegasos2.c | 113 ++++++++++++++++++++---------------------- 2 files changed, 54 insertions(+), 60 deletions(-) diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index 92b0f5d047..00b3ff7d90 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -869,6 +869,7 @@ static void mv64361_realize(DeviceState *dev, Error **errp) s->base_addr_enable = 0x1fffff; memory_region_init_io(&s->regs, OBJECT(s), &mv64361_ops, s, TYPE_MV64361, 0x10000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->regs); for (i = 0; i < 2; i++) { g_autofree char *name = g_strdup_printf("pcihost%d", i); object_initialize_child(OBJECT(dev), name, &s->pci[i], diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index a9e3625f56..a861bf16b8 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -205,56 +205,49 @@ static void pegasos2_init(MachineState *machine) } } -static uint32_t pegasos2_pci_config_read(AddressSpace *as, int bus, +static uint32_t pegasos2_mv_reg_read(Pegasos2MachineState *pm, + uint32_t addr, uint32_t len) +{ + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + uint64_t val = 0xffffffffULL; + memory_region_dispatch_read(r, addr, &val, size_memop(len) | MO_LE, + MEMTXATTRS_UNSPECIFIED); + return val; +} + +static void pegasos2_mv_reg_write(Pegasos2MachineState *pm, uint32_t addr, + uint32_t len, uint32_t val) +{ + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + memory_region_dispatch_write(r, addr, val, size_memop(len) | MO_LE, + MEMTXATTRS_UNSPECIFIED); +} + +static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus, uint32_t addr, uint32_t len) { - hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); - uint32_t val = 0xffffffff; + hwaddr pcicfg = bus ? 0xc78 : 0xcf8; + uint64_t val = 0xffffffffULL; - stl_le_phys(as, pcicfg, addr | BIT(31)); - switch (len) { - case 4: - val = ldl_le_phys(as, pcicfg + 4); - break; - case 2: - val = lduw_le_phys(as, pcicfg + 4); - break; - case 1: - val = ldub_phys(as, pcicfg + 4); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); - break; + if (len <= 4) { + pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); + val = pegasos2_mv_reg_read(pm, pcicfg + 4, len); } return val; } -static void pegasos2_pci_config_write(AddressSpace *as, int bus, uint32_t addr, - uint32_t len, uint32_t val) +static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus, + uint32_t addr, uint32_t len, uint32_t val) { - hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); + hwaddr pcicfg = bus ? 0xc78 : 0xcf8; - stl_le_phys(as, pcicfg, addr | BIT(31)); - switch (len) { - case 4: - stl_le_phys(as, pcicfg + 4, val); - break; - case 2: - stw_le_phys(as, pcicfg + 4, val); - break; - case 1: - stb_phys(as, pcicfg + 4, val); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); - break; - } + pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); + pegasos2_mv_reg_write(pm, pcicfg + 4, len, val); } static void pegasos2_machine_reset(MachineState *machine) { Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); - AddressSpace *as = CPU(pm->cpu)->as; void *fdt; uint64_t d[2]; int sz; @@ -265,51 +258,51 @@ static void pegasos2_machine_reset(MachineState *machine) } /* Otherwise, set up devices that board firmware would normally do */ - stl_le_phys(as, 0xf1000000, 0x28020ff); - stl_le_phys(as, 0xf1000278, 0xa31fc); - stl_le_phys(as, 0xf100f300, 0x11ff0400); - stl_le_phys(as, 0xf100f10c, 0x80000000); - stl_le_phys(as, 0xf100001c, 0x8000000); - pegasos2_pci_config_write(as, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | + pegasos2_mv_reg_write(pm, 0, 4, 0x28020ff); + pegasos2_mv_reg_write(pm, 0x278, 4, 0xa31fc); + pegasos2_mv_reg_write(pm, 0xf300, 4, 0x11ff0400); + pegasos2_mv_reg_write(pm, 0xf10c, 4, 0x80000000); + pegasos2_mv_reg_write(pm, 0x1c, 4, 0x8000000); + pegasos2_pci_config_write(pm, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pegasos2_pci_config_write(as, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | + pegasos2_pci_config_write(pm, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | 0x50, 1, 0x2); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_INTERRUPT_LINE, 2, 0x109); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_CLASS_PROG, 1, 0xf); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | 0x40, 1, 0xb); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | 0x50, 4, 0x17171717); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_COMMAND, 2, 0x87); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 2) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 2) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 3) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 3) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x48, 4, 0xf00); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x40, 4, 0x558020); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x90, 4, 0xd00); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 5) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 5) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 6) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 6) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); /* Device tree and VOF set up */ @@ -404,7 +397,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, } addr = ldl_be_phys(as, args); len = ldl_be_phys(as, args + 4); - val = pegasos2_pci_config_read(as, !(addr >> 24), + val = pegasos2_pci_config_read(pm, !(addr >> 24), addr & 0x0fffffff, len); stl_be_phys(as, rets, 0); stl_be_phys(as, rets + 4, val); @@ -421,7 +414,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, addr = ldl_be_phys(as, args); len = ldl_be_phys(as, args + 4); val = ldl_be_phys(as, args + 8); - pegasos2_pci_config_write(as, !(addr >> 24), + pegasos2_pci_config_write(pm, !(addr >> 24), addr & 0x0fffffff, len, val); stl_be_phys(as, rets, 0); return H_SUCCESS; From d200ea14b7ec5b9f0eaf1eee6e8fc47c359ee40d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 14 Oct 2021 21:50:19 +0200 Subject: [PATCH 0689/1334] ppc/pegasos2: Add constants for PCI config addresses Define a constant for PCI config addresses to make it clearer what these numbers are. Signed-off-by: BALATON Zoltan Message-Id: <9bd8e84d02d91693b71082a1fadeb86e6bce3025.1634241019.git.balaton@eik.bme.hu> Signed-off-by: David Gibson --- hw/ppc/pegasos2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index a861bf16b8..39e96d323f 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -54,11 +54,13 @@ #define BUS_FREQ_HZ 133333333 +#define PCI0_CFG_ADDR 0xcf8 #define PCI0_MEM_BASE 0xc0000000 #define PCI0_MEM_SIZE 0x20000000 #define PCI0_IO_BASE 0xf8000000 #define PCI0_IO_SIZE 0x10000 +#define PCI1_CFG_ADDR 0xc78 #define PCI1_MEM_BASE 0x80000000 #define PCI1_MEM_SIZE 0x40000000 #define PCI1_IO_BASE 0xfe000000 @@ -226,7 +228,7 @@ static void pegasos2_mv_reg_write(Pegasos2MachineState *pm, uint32_t addr, static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus, uint32_t addr, uint32_t len) { - hwaddr pcicfg = bus ? 0xc78 : 0xcf8; + hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; uint64_t val = 0xffffffffULL; if (len <= 4) { @@ -239,7 +241,7 @@ static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus, static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus, uint32_t addr, uint32_t len, uint32_t val) { - hwaddr pcicfg = bus ? 0xc78 : 0xcf8; + hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); pegasos2_mv_reg_write(pm, pcicfg + 4, len, val); From 284c0486e7872243458b956b9a91bc757b59a44c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 14 Oct 2021 21:50:19 +0200 Subject: [PATCH 0690/1334] ppc/pegasos2: Implement power-off RTAS function with VOF This only helps Linux guests as only that seems to use it. Signed-off-by: BALATON Zoltan Message-Id: <1c1e030f2bbc86e950b3310fb5922facdc21ef86.1634241019.git.balaton@eik.bme.hu> Signed-off-by: David Gibson --- hw/ppc/pegasos2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 39e96d323f..e427ac2fe0 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -22,6 +22,7 @@ #include "hw/i2c/smbus_eeprom.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" +#include "sysemu/runstate.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" @@ -429,6 +430,16 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, qemu_log_mask(LOG_UNIMP, "%c", ldl_be_phys(as, args)); stl_be_phys(as, rets, 0); return H_SUCCESS; + case RTAS_POWER_OFF: + { + if (nargs != 2 || nrets != 1) { + stl_be_phys(as, rets, -1); + return H_PARAMETER; + } + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + stl_be_phys(as, rets, 0); + return H_SUCCESS; + } default: qemu_log_mask(LOG_UNIMP, "Unknown RTAS token %u (args=%u, rets=%u)\n", token, nargs, nrets); From 5ff1dfdf66f99c5208187cc2716a3a974f22b7c7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 15 Oct 2021 11:00:08 +0200 Subject: [PATCH 0691/1334] tests/acceptance: Add a test for the bamboo ppc board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel and initrd from the "Aboriginal Linux" project can be used to run some tests on the bamboo ppc machine. Signed-off-by: Thomas Huth Message-Id: <20211015090008.1299609-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- MAINTAINERS | 1 + tests/acceptance/ppc_bamboo.py | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/acceptance/ppc_bamboo.py diff --git a/MAINTAINERS b/MAINTAINERS index 9e9f489a41..4e77d03651 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1245,6 +1245,7 @@ Bamboo L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c +F: tests/acceptance/ppc_bamboo.py e500 L: qemu-ppc@nongnu.org diff --git a/tests/acceptance/ppc_bamboo.py b/tests/acceptance/ppc_bamboo.py new file mode 100644 index 0000000000..dd33bf66f3 --- /dev/null +++ b/tests/acceptance/ppc_bamboo.py @@ -0,0 +1,39 @@ +# Test that Linux kernel boots on the ppc bamboo board and check the console +# +# Copyright (c) 2021 Red Hat +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command_and_wait_for_pattern + +class BambooMachine(Test): + + timeout = 90 + + def test_ppc_bamboo(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:bamboo + :avocado: tags=cpu:440epb + :avocado: tags=device:rtl8139 + """ + tar_url = ('http://landley.net/aboriginal/downloads/binaries/' + 'system-image-powerpc-440fp.tar.gz') + tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9' + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + archive.extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + + '/system-image-powerpc-440fp/linux', + '-initrd', self.workdir + + '/system-image-powerpc-440fp/rootfs.cpio.gz', + '-nic', 'user,model=rtl8139,restrict=on') + self.vm.launch() + wait_for_console_pattern(self, 'Type exit when done') + exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2', + '10.0.2.2 is alive!') + exec_command_and_wait_for_pattern(self, 'halt', 'System Halted') From 6fa5726be6a52b246335cb86a3c118cdfd40c677 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Fri, 15 Oct 2021 15:19:40 -0300 Subject: [PATCH 0692/1334] target/ppc: Filter mtmsr[d] input before setting MSR PowerISA says that mtmsr[d] "does not alter MSR[HV], MSR[S], MSR[ME], or MSR[LE]", but the current code only filters the GPR-provided value if L=1. This behavior caused some problems in FreeBSD, and a build option was added to work around the issue [1], but it seems that the bug was not reported in launchpad/gitlab. This patch address the issue in qemu, so the option on FreeBSD should no longer be required. [1] https://cgit.freebsd.org/src/commit/?id=4efb1ca7d2a44cfb33d7f9e18bd92f8d68dcfee0 Signed-off-by: Matheus Ferst Message-Id: <20211015181940.197982-1-matheus.ferst@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/cpu.h | 1 + target/ppc/translate.c | 73 +++++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c6fc0043a9..cc1911bc75 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -314,6 +314,7 @@ typedef struct ppc_v3_pate_t { #define MSR_AP 23 /* Access privilege state on 602 hflags */ #define MSR_VSX 23 /* Vector Scalar Extension (ISA 2.06 and later) x hflags */ #define MSR_SA 22 /* Supervisor access mode on 602 hflags */ +#define MSR_S 22 /* Secure state */ #define MSR_KEY 19 /* key bit on 603e */ #define MSR_POW 18 /* Power management */ #define MSR_TGPR 17 /* TGPR usage on 602/603 x */ diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 98f304302e..d0d400cd8c 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -4934,32 +4934,40 @@ static void gen_mtmsrd(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) + TCGv t0, t1; + target_ulong mask; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_icount_io_start(ctx); + if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], - (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(t1, cpu_msr, - ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); - tcg_gen_or_tl(t1, t1, t0); - - gen_helper_store_msr(cpu_env, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - + mask = (1ULL << MSR_RI) | (1ULL << MSR_EE); } else { + /* mtmsrd does not alter HV, S, ME, or LE */ + mask = ~((1ULL << MSR_LE) | (1ULL << MSR_ME) | (1ULL << MSR_S) | + (1ULL << MSR_HV)); /* * XXX: we need to update nip before the store if we enter * power saving mode, we will exit the loop directly from * ppc_store_msr */ gen_update_nip(ctx, ctx->base.pc_next); - gen_helper_store_msr(cpu_env, cpu_gpr[rS(ctx->opcode)]); } + + tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], mask); + tcg_gen_andi_tl(t1, cpu_msr, ~mask); + tcg_gen_or_tl(t0, t0, t1); + + gen_helper_store_msr(cpu_env, t0); + /* Must stop the translation as machine state (may have) changed */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; + + tcg_temp_free(t0); + tcg_temp_free(t1); #endif /* !defined(CONFIG_USER_ONLY) */ } #endif /* defined(TARGET_PPC64) */ @@ -4969,23 +4977,19 @@ static void gen_mtmsr(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) + TCGv t0, t1; + target_ulong mask = 0xFFFFFFFF; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_icount_io_start(ctx); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], - (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(t1, cpu_msr, - ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); - tcg_gen_or_tl(t1, t1, t0); - - gen_helper_store_msr(cpu_env, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - + mask &= (1ULL << MSR_RI) | (1ULL << MSR_EE); } else { - TCGv msr = tcg_temp_new(); + /* mtmsr does not alter S, ME, or LE */ + mask &= ~((1ULL << MSR_LE) | (1ULL << MSR_ME) | (1ULL << MSR_S)); /* * XXX: we need to update nip before the store if we enter @@ -4993,16 +4997,19 @@ static void gen_mtmsr(DisasContext *ctx) * ppc_store_msr */ gen_update_nip(ctx, ctx->base.pc_next); -#if defined(TARGET_PPC64) - tcg_gen_deposit_tl(msr, cpu_msr, cpu_gpr[rS(ctx->opcode)], 0, 32); -#else - tcg_gen_mov_tl(msr, cpu_gpr[rS(ctx->opcode)]); -#endif - gen_helper_store_msr(cpu_env, msr); - tcg_temp_free(msr); } + + tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], mask); + tcg_gen_andi_tl(t1, cpu_msr, ~mask); + tcg_gen_or_tl(t0, t0, t1); + + gen_helper_store_msr(cpu_env, t0); + /* Must stop the translation as machine state (may have) changed */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; + + tcg_temp_free(t0); + tcg_temp_free(t1); #endif } From f7460df27162d1643f74677d53fad4328142c6a9 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Sun, 17 Oct 2021 22:01:19 -0300 Subject: [PATCH 0693/1334] target/ppc: add MMCR0 PMCC bits to hflags We're going to add PMU support for TCG PPC64 chips, based on IBM POWER8+ emulation and following PowerISA v3.1. This requires several PMU related registers to be exposed to userspace (problem state). PowerISA v3.1 dictates that the PMCC bits of the MMCR0 register controls the level of access of the PMU registers to problem state. This patch start things off by exposing both PMCC bits to hflags, allowing us to access them via DisasContext in the read/write callbacks that we're going to add next. Signed-off-by: Daniel Henrique Barboza Message-Id: <20211018010133.315842-2-danielhb413@gmail.com> Signed-off-by: David Gibson --- target/ppc/cpu.h | 6 ++++++ target/ppc/helper_regs.c | 6 ++++++ target/ppc/translate.c | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index cc1911bc75..24d1f2cf97 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -343,6 +343,10 @@ typedef struct ppc_v3_pate_t { #define MSR_RI 1 /* Recoverable interrupt 1 */ #define MSR_LE 0 /* Little-endian mode 1 hflags */ +/* PMU bits */ +#define MMCR0_PMCC0 PPC_BIT(44) /* PMC Control bit 0 */ +#define MMCR0_PMCC1 PPC_BIT(45) /* PMC Control bit 1 */ + /* LPCR bits */ #define LPCR_VPM0 PPC_BIT(0) #define LPCR_VPM1 PPC_BIT(1) @@ -608,6 +612,8 @@ enum { HFLAGS_SE = 10, /* MSR_SE -- from elsewhere on embedded ppc */ HFLAGS_FP = 13, /* MSR_FP */ HFLAGS_PR = 14, /* MSR_PR */ + HFLAGS_PMCC0 = 15, /* MMCR0 PMCC bit 0 */ + HFLAGS_PMCC1 = 16, /* MMCR0 PMCC bit 1 */ HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */ HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */ diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 1bfb480ecf..99562edd57 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -109,6 +109,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (env->spr[SPR_LPCR] & LPCR_HR) { hflags |= 1 << HFLAGS_HR; } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { + hflags |= 1 << HFLAGS_PMCC0; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { + hflags |= 1 << HFLAGS_PMCC1; + } #ifndef CONFIG_USER_ONLY if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { diff --git a/target/ppc/translate.c b/target/ppc/translate.c index d0d400cd8c..a4c5ef3701 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -175,6 +175,8 @@ struct DisasContext { bool tm_enabled; bool gtse; bool hr; + bool mmcr0_pmcc0; + bool mmcr0_pmcc1; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint32_t flags; @@ -8552,6 +8554,8 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->tm_enabled = (hflags >> HFLAGS_TM) & 1; ctx->gtse = (hflags >> HFLAGS_GTSE) & 1; ctx->hr = (hflags >> HFLAGS_HR) & 1; + ctx->mmcr0_pmcc0 = (hflags >> HFLAGS_PMCC0) & 1; + ctx->mmcr0_pmcc1 = (hflags >> HFLAGS_PMCC1) & 1; ctx->singlestep_enabled = 0; if ((hflags >> HFLAGS_SE) & 1) { From 565cb1096733dae6d388244e03d60d680f6eca84 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Sun, 17 Oct 2021 22:01:20 -0300 Subject: [PATCH 0694/1334] target/ppc: add user read/write functions for MMCR0 Userspace need access to PMU SPRs to be able to operate the PMU. One of such SPRs is MMCR0. MMCR0, as defined by PowerISA v3.1, is classified as a 'group A' PMU register. This class of registers has common read/write rules that are governed by MMCR0 PMCC bits. MMCR0 is also not fully exposed to problem state: only MMCR0_FC, MMCR0_PMAO and MMCR0_PMAE bits are readable/writable in this case. This patch exposes MMCR0 to userspace by doing the following: - two new callbacks, spr_read_MMCR0_ureg() and spr_write_MMCR0_ureg(), are added to be used as problem state read/write callbacks of UMMCR0. Both callbacks filters the amount of bits userspace is able to read/write by using a MMCR0_UREG_MASK; - problem state access control is done by the spr_groupA_read_allowed() and spr_groupA_write_allowed() helpers. These helpers will read the current PMCC bits from DisasContext and check whether the read/write MMCR0 operation is valid or noti; - to avoid putting exclusive PMU logic into the already loaded translate.c file, let's create a new 'power8-pmu-regs.c.inc' file that will hold all the spr_read/spr_write functions of PMU registers. The 'power8' name of this new file intends to hint about the proven support of the PMU logic to be added. The code has been tested with the IBM POWER chip family, POWER8 being the oldest version tested. This doesn't mean that the PMU logic will break with any other PPC64 chip that implements Book3s, but rather that we can't assert that it works properly with any Book3s compliant chip. CC: Gustavo Romero Signed-off-by: Gustavo Romero Signed-off-by: Daniel Henrique Barboza Message-Id: <20211018010133.315842-3-danielhb413@gmail.com> Signed-off-by: David Gibson --- target/ppc/cpu.h | 7 ++ target/ppc/cpu_init.c | 2 +- target/ppc/power8-pmu-regs.c.inc | 116 +++++++++++++++++++++++++++++++ target/ppc/spr_tcg.h | 2 + target/ppc/translate.c | 2 + 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 target/ppc/power8-pmu-regs.c.inc diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 24d1f2cf97..0bd008f4b8 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -344,8 +344,15 @@ typedef struct ppc_v3_pate_t { #define MSR_LE 0 /* Little-endian mode 1 hflags */ /* PMU bits */ +#define MMCR0_FC PPC_BIT(32) /* Freeze Counters */ +#define MMCR0_PMAO PPC_BIT(56) /* Perf Monitor Alert Ocurred */ +#define MMCR0_PMAE PPC_BIT(37) /* Perf Monitor Alert Enable */ +#define MMCR0_EBE PPC_BIT(43) /* Perf Monitor EBB Enable */ +#define MMCR0_FCECE PPC_BIT(38) /* FC on Enabled Cond or Event */ #define MMCR0_PMCC0 PPC_BIT(44) /* PMC Control bit 0 */ #define MMCR0_PMCC1 PPC_BIT(45) /* PMC Control bit 1 */ +/* MMCR0 userspace r/w mask */ +#define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) /* LPCR bits */ #define LPCR_VPM0 PPC_BIT(0) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6aad01d1d3..375bdca1e1 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6867,7 +6867,7 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env) static void register_book3s_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_POWER_UMMCR0, "UMMCR0", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_MMCR0_ureg, &spr_write_MMCR0_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UMMCR1, "UMMCR1", diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc new file mode 100644 index 0000000000..37c812dd4d --- /dev/null +++ b/target/ppc/power8-pmu-regs.c.inc @@ -0,0 +1,116 @@ +/* + * PMU register read/write functions for TCG IBM POWER chips + * + * Copyright IBM Corp. 2021 + * + * Authors: + * Daniel Henrique Barboza + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + +/* + * Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the + * PMCs) has problem state read access. + * + * Read acccess is granted for all PMCC values but 0b01, where a + * Facility Unavailable Interrupt will occur. + */ +static bool spr_groupA_read_allowed(DisasContext *ctx) +{ + if (!ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return false; + } + + return true; +} + +/* + * Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the + * PMCs) has problem state write access. + * + * Write acccess is granted for PMCC values 0b10 and 0b11. Userspace + * writing with PMCC 0b00 will generate a Hypervisor Emulation + * Assistance Interrupt. Userspace writing with PMCC 0b01 will + * generate a Facility Unavailable Interrupt. + */ +static bool spr_groupA_write_allowed(DisasContext *ctx) +{ + if (ctx->mmcr0_pmcc0) { + return true; + } + + if (ctx->mmcr0_pmcc1) { + /* PMCC = 0b01 */ + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + } else { + /* PMCC = 0b00 */ + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + + return false; +} + +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) +{ + TCGv t0; + + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + t0 = tcg_temp_new(); + + /* + * Filter out all bits but FC, PMAO, and PMAE, according + * to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0, + * fourth paragraph. + */ + gen_load_spr(t0, SPR_POWER_MMCR0); + tcg_gen_andi_tl(t0, t0, MMCR0_UREG_MASK); + tcg_gen_mov_tl(cpu_gpr[gprn], t0); + + tcg_temp_free(t0); +} + +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0, t1; + + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + /* + * Filter out all bits but FC, PMAO, and PMAE, according + * to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0, + * fourth paragraph. + */ + tcg_gen_andi_tl(t0, cpu_gpr[gprn], MMCR0_UREG_MASK); + gen_load_spr(t1, SPR_POWER_MMCR0); + tcg_gen_andi_tl(t1, t1, ~(MMCR0_UREG_MASK)); + /* Keep all other bits intact */ + tcg_gen_or_tl(t1, t1, t0); + gen_store_spr(SPR_POWER_MMCR0, t1); + + tcg_temp_free(t0); + tcg_temp_free(t1); +} +#else +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} +#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ diff --git a/target/ppc/spr_tcg.h b/target/ppc/spr_tcg.h index 0be5f347d5..b28b095097 100644 --- a/target/ppc/spr_tcg.h +++ b/target/ppc/spr_tcg.h @@ -32,6 +32,7 @@ void spr_write_lr(DisasContext *ctx, int sprn, int gprn); void spr_read_ctr(DisasContext *ctx, int gprn, int sprn); void spr_write_ctr(DisasContext *ctx, int sprn, int gprn); void spr_read_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_tbl(DisasContext *ctx, int gprn, int sprn); void spr_read_tbu(DisasContext *ctx, int gprn, int sprn); void spr_read_atbl(DisasContext *ctx, int gprn, int sprn); @@ -40,6 +41,7 @@ void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn); void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn); void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn); void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn); +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn); #ifndef CONFIG_USER_ONLY void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index a4c5ef3701..518337bcb7 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7486,6 +7486,8 @@ static int times_4(DisasContext *ctx, int x) #include "decode-insn32.c.inc" #include "decode-insn64.c.inc" +#include "power8-pmu-regs.c.inc" + #include "translate/fixedpoint-impl.c.inc" #include "translate/fp-impl.c.inc" From 7b3ecf16c81c16eb3cf171b0bd63c08f1a5dd942 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Sun, 17 Oct 2021 22:01:21 -0300 Subject: [PATCH 0695/1334] target/ppc: add user read/write functions for MMCR2 Similar to the previous patch, let's add problem state read/write access to the MMCR2 SPR, which is also a group A PMU SPR that needs to be filtered to be read/written by userspace. Signed-off-by: Daniel Henrique Barboza Message-Id: <20211018010133.315842-4-danielhb413@gmail.com> Signed-off-by: David Gibson --- target/ppc/cpu.h | 9 +++ target/ppc/cpu_init.c | 2 +- target/ppc/power8-pmu-regs.c.inc | 98 ++++++++++++++++++++++++++++---- target/ppc/spr_tcg.h | 2 + 4 files changed, 99 insertions(+), 12 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 0bd008f4b8..0472ec9154 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -353,6 +353,15 @@ typedef struct ppc_v3_pate_t { #define MMCR0_PMCC1 PPC_BIT(45) /* PMC Control bit 1 */ /* MMCR0 userspace r/w mask */ #define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) +/* MMCR2 userspace r/w mask */ +#define MMCR2_FC1P0 PPC_BIT(1) /* MMCR2 FCnP0 for PMC1 */ +#define MMCR2_FC2P0 PPC_BIT(10) /* MMCR2 FCnP0 for PMC2 */ +#define MMCR2_FC3P0 PPC_BIT(19) /* MMCR2 FCnP0 for PMC3 */ +#define MMCR2_FC4P0 PPC_BIT(28) /* MMCR2 FCnP0 for PMC4 */ +#define MMCR2_FC5P0 PPC_BIT(37) /* MMCR2 FCnP0 for PMC5 */ +#define MMCR2_FC6P0 PPC_BIT(46) /* MMCR2 FCnP0 for PMC6 */ +#define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \ + MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0) /* LPCR bits */ #define LPCR_VPM0 PPC_BIT(0) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 375bdca1e1..ad88e54950 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6975,7 +6975,7 @@ static void register_power8_pmu_sup_sprs(CPUPPCState *env) static void register_power8_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_POWER_UMMCR2, "UMMCR2", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_MMCR2_ureg, &spr_write_MMCR2_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_USIER, "USIER", diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index 37c812dd4d..fb95175183 100644 --- a/target/ppc/power8-pmu-regs.c.inc +++ b/target/ppc/power8-pmu-regs.c.inc @@ -55,6 +55,33 @@ static bool spr_groupA_write_allowed(DisasContext *ctx) return false; } +/* + * Helper function to avoid code repetition between MMCR0 and + * MMCR2 problem state write functions. + * + * 'ret' must be tcg_temp_freed() by the caller. + */ +static TCGv masked_gprn_for_spr_write(int gprn, int sprn, + uint64_t spr_mask) +{ + TCGv ret = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); + + /* 'ret' starts with all mask bits cleared */ + gen_load_spr(ret, sprn); + tcg_gen_andi_tl(ret, ret, ~(spr_mask)); + + /* Apply the mask into 'gprn' in a temp var */ + tcg_gen_andi_tl(t0, cpu_gpr[gprn], spr_mask); + + /* Add the masked gprn bits into 'ret' */ + tcg_gen_or_tl(ret, ret, t0); + + tcg_temp_free(t0); + + return ret; +} + void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) { TCGv t0; @@ -79,29 +106,68 @@ void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) { - TCGv t0, t1; + TCGv masked_gprn; if (!spr_groupA_write_allowed(ctx)) { return; } - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - /* * Filter out all bits but FC, PMAO, and PMAE, according * to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0, * fourth paragraph. */ - tcg_gen_andi_tl(t0, cpu_gpr[gprn], MMCR0_UREG_MASK); - gen_load_spr(t1, SPR_POWER_MMCR0); - tcg_gen_andi_tl(t1, t1, ~(MMCR0_UREG_MASK)); - /* Keep all other bits intact */ - tcg_gen_or_tl(t1, t1, t0); - gen_store_spr(SPR_POWER_MMCR0, t1); + masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR0, + MMCR0_UREG_MASK); + gen_store_spr(SPR_POWER_MMCR0, masked_gprn); + + tcg_temp_free(masked_gprn); +} + +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn) +{ + TCGv t0; + + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + t0 = tcg_temp_new(); + + /* + * On read, filter out all bits that are not FCnP0 bits. + * When MMCR0[PMCC] is set to 0b10 or 0b11, providing + * problem state programs read/write access to MMCR2, + * only the FCnP0 bits can be accessed. All other bits are + * not changed when mtspr is executed in problem state, and + * all other bits return 0s when mfspr is executed in problem + * state, according to ISA v3.1, section 10.4.6 Monitor Mode + * Control Register 2, p. 1316, third paragraph. + */ + gen_load_spr(t0, SPR_POWER_MMCR2); + tcg_gen_andi_tl(t0, t0, MMCR2_UREG_MASK); + tcg_gen_mov_tl(cpu_gpr[gprn], t0); tcg_temp_free(t0); - tcg_temp_free(t1); +} + +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) +{ + TCGv masked_gprn; + + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + /* + * Filter the bits that can be written using MMCR2_UREG_MASK, + * similar to what is done in spr_write_MMCR0_ureg(). + */ + masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR2, + MMCR2_UREG_MASK); + gen_store_spr(SPR_POWER_MMCR2, masked_gprn); + + tcg_temp_free(masked_gprn); } #else void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) @@ -113,4 +179,14 @@ void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) { spr_noaccess(ctx, gprn, sprn); } + +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ diff --git a/target/ppc/spr_tcg.h b/target/ppc/spr_tcg.h index b28b095097..cb7f40eedf 100644 --- a/target/ppc/spr_tcg.h +++ b/target/ppc/spr_tcg.h @@ -33,6 +33,7 @@ void spr_read_ctr(DisasContext *ctx, int gprn, int sprn); void spr_write_ctr(DisasContext *ctx, int sprn, int gprn); void spr_read_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_tbl(DisasContext *ctx, int gprn, int sprn); void spr_read_tbu(DisasContext *ctx, int gprn, int sprn); void spr_read_atbl(DisasContext *ctx, int gprn, int sprn); @@ -42,6 +43,7 @@ void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn); void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn); void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn); #ifndef CONFIG_USER_ONLY void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); From cedf706956e7440653b18ac2c2a9452b8d710577 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Sun, 17 Oct 2021 22:01:22 -0300 Subject: [PATCH 0696/1334] target/ppc: adding user read/write functions for PMCs Problem state needs to be able to read and write the PMU counters, otherwise it won't be aware of any sampling result that the PMU produces after a Perf run. This patch does that in a similar fashion as already done in the previous patches. PMCs 5 and 6 have a special condition, aside from the constraints that are common with PMCs 1-4, where they are not part of the PMU if MMCR0_PMCC is 0b11. Signed-off-by: Daniel Henrique Barboza Message-Id: <20211018010133.315842-5-danielhb413@gmail.com> Signed-off-by: David Gibson --- target/ppc/cpu_init.c | 12 +++--- target/ppc/power8-pmu-regs.c.inc | 70 ++++++++++++++++++++++++++++++++ target/ppc/spr_tcg.h | 4 ++ 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index ad88e54950..65545ba9ca 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6879,27 +6879,27 @@ static void register_book3s_pmu_user_sprs(CPUPPCState *env) &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC1, "UPMC1", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC2, "UPMC2", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC3, "UPMC3", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC4, "UPMC4", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC5, "UPMC5", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC56_ureg, &spr_write_PMC56_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC6, "UPMC6", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC56_ureg, &spr_write_PMC56_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_USIAR, "USIAR", diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index fb95175183..7391851238 100644 --- a/target/ppc/power8-pmu-regs.c.inc +++ b/target/ppc/power8-pmu-regs.c.inc @@ -169,6 +169,56 @@ void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) tcg_temp_free(masked_gprn); } + +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn) +{ + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) +{ + /* + * If PMCC = 0b11, PMC5 and PMC6 aren't included in the Performance + * Monitor, and a read attempt results in a Facility Unavailable + * Interrupt. + */ + if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return; + } + + /* The remaining steps are similar to PMCs 1-4 userspace read */ + spr_read_PMC14_ureg(ctx, gprn, sprn); +} + +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn) +{ + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + spr_write_ureg(ctx, sprn, gprn); +} + +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn) +{ + /* + * If PMCC = 0b11, PMC5 and PMC6 aren't included in the Performance + * Monitor, and a write attempt results in a Facility Unavailable + * Interrupt. + */ + if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return; + } + + /* The remaining steps are similar to PMCs 1-4 userspace write */ + spr_write_PMC14_ureg(ctx, sprn, gprn); +} #else void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) { @@ -189,4 +239,24 @@ void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) { spr_noaccess(ctx, gprn, sprn); } + +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} + +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ diff --git a/target/ppc/spr_tcg.h b/target/ppc/spr_tcg.h index cb7f40eedf..520f1ef233 100644 --- a/target/ppc/spr_tcg.h +++ b/target/ppc/spr_tcg.h @@ -34,6 +34,8 @@ void spr_write_ctr(DisasContext *ctx, int sprn, int gprn); void spr_read_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_tbl(DisasContext *ctx, int gprn, int sprn); void spr_read_tbu(DisasContext *ctx, int gprn, int sprn); void spr_read_atbl(DisasContext *ctx, int gprn, int sprn); @@ -44,6 +46,8 @@ void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn); void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn); #ifndef CONFIG_USER_ONLY void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); From 6f9e8515c106650fbba7222c8f66234c8546c025 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Oct 2021 11:18:17 +0200 Subject: [PATCH 0697/1334] hw/ppc/ppc4xx_pci: Fix ppc4xx_pci_map_irq() for recent Linux kernels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent Linux kernels are accessing the PCI device in slot 0 that represents the PCI host bridge. This causes ppc4xx_pci_map_irq() to return -1 which causes an assert() later: hw/pci/pci.c:262: pci_bus_change_irq_level: Assertion `irq_num >= 0' failed. Thus we should allocate an IRQ line for the device in slot 0, too. To avoid changes to the outside of ppc4xx_pci.c, we map it to the internal IRQ number 4 which will then happily be ignored since ppc440_bamboo.c does not wire it up. With these changes it is now possible again to use recent Linux kernels for the bamboo board. Signed-off-by: Thomas Huth Message-Id: <20211019091817.469003-1-thuth@redhat.com> Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/ppc4xx_pci.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index 8147ba6f94..304a29349c 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/ppc/ppc4xx_pci.c @@ -48,12 +48,14 @@ OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST_BRIDGE) #define PPC4xx_PCI_NR_PMMS 3 #define PPC4xx_PCI_NR_PTMS 2 +#define PPC4xx_PCI_NUM_DEVS 5 + struct PPC4xxPCIState { PCIHostState parent_obj; struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; - qemu_irq irq[PCI_NUM_PINS]; + qemu_irq irq[PPC4xx_PCI_NUM_DEVS]; MemoryRegion container; MemoryRegion iomem; @@ -246,7 +248,7 @@ static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) trace_ppc4xx_pci_map_irq(pci_dev->devfn, irq_num, slot); - return slot - 1; + return slot > 0 ? slot - 1 : PPC4xx_PCI_NUM_DEVS - 1; } static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) @@ -254,7 +256,7 @@ static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) qemu_irq *pci_irqs = opaque; trace_ppc4xx_pci_set_irq(irq_num); - assert(irq_num >= 0); + assert(irq_num >= 0 && irq_num < PPC4xx_PCI_NUM_DEVS); qemu_set_irq(pci_irqs[irq_num], level); } From 3cc322f437b027d95aa96dd140b2b4fa9a5b6dfb Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 19 Oct 2021 10:00:35 +0200 Subject: [PATCH 0698/1334] tests/acpi: Add void table for virt/DBG2 bios-tables-test Add placeholders for DBG2 reference table for virt tests and ignore till reference blob is added. Signed-off-by: Eric Auger Acked-by: Igor Mammedov Acked-by: Michael S. Tsirkin Message-Id: <20211019080037.930641-2-eric.auger@redhat.com> Signed-off-by: Richard Henderson --- tests/data/acpi/virt/DBG2 | 0 tests/qtest/bios-tables-test-allowed-diff.h | 1 + 2 files changed, 1 insertion(+) create mode 100644 tests/data/acpi/virt/DBG2 diff --git a/tests/data/acpi/virt/DBG2 b/tests/data/acpi/virt/DBG2 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..1910d154c2 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/virt/DBG2", From f0dc9a5d8d556a42a3e614d041effc8854eac71e Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 19 Oct 2021 10:00:36 +0200 Subject: [PATCH 0699/1334] hw/arm/virt_acpi_build: Generate DBG2 table ARM SBBR specification mandates DBG2 table (Debug Port Table 2) since v1.0 (ARM DEN0044F 8.3.1.7 DBG2). The DBG2 table allows to describe one or more debug ports. Generate an DBG2 table featuring a single debug port, the PL011. The DBG2 specification can be found at "Microsoft Debug Port Table 2 (DBG2)" https://docs.microsoft.com/en-us/windows-hardware/drivers/bringup/acpi-debug-port-table?redirectedfrom=MSDN Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Reviewed-by: Igor Mammedov Acked-by: Michael S. Tsirkin Message-Id: <20211019080037.930641-3-eric.auger@redhat.com> Signed-off-by: Richard Henderson --- hw/arm/virt-acpi-build.c | 63 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 9c4730f4ab..d3bb4cba3e 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -626,6 +626,64 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_table_end(linker, &table); } +/* Debug Port Table 2 (DBG2) */ +static void +build_dbg2(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) +{ + AcpiTable table = { .sig = "DBG2", .rev = 0, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; + int dbg2devicelength; + const char name[] = "COM0"; + const int namespace_length = sizeof(name); + + acpi_table_begin(&table, table_data); + + dbg2devicelength = 22 + /* BaseAddressRegister[] offset */ + 12 + /* BaseAddressRegister[] */ + 4 + /* AddressSize[] */ + namespace_length /* NamespaceString[] */; + + /* OffsetDbgDeviceInfo */ + build_append_int_noprefix(table_data, 44, 4); + /* NumberDbgDeviceInfo */ + build_append_int_noprefix(table_data, 1, 4); + + /* Table 2. Debug Device Information structure format */ + build_append_int_noprefix(table_data, 0, 1); /* Revision */ + build_append_int_noprefix(table_data, dbg2devicelength, 2); /* Length */ + /* NumberofGenericAddressRegisters */ + build_append_int_noprefix(table_data, 1, 1); + /* NameSpaceStringLength */ + build_append_int_noprefix(table_data, namespace_length, 2); + build_append_int_noprefix(table_data, 38, 2); /* NameSpaceStringOffset */ + build_append_int_noprefix(table_data, 0, 2); /* OemDataLength */ + /* OemDataOffset (0 means no OEM data) */ + build_append_int_noprefix(table_data, 0, 2); + + /* Port Type */ + build_append_int_noprefix(table_data, 0x8000 /* Serial */, 2); + /* Port Subtype */ + build_append_int_noprefix(table_data, 0x3 /* ARM PL011 UART */, 2); + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + /* BaseAddressRegisterOffset */ + build_append_int_noprefix(table_data, 22, 2); + /* AddressSizeOffset */ + build_append_int_noprefix(table_data, 34, 2); + + /* BaseAddressRegister[] */ + build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 8, 0, 1, + vms->memmap[VIRT_UART].base); + + /* AddressSize[] */ + build_append_int_noprefix(table_data, + vms->memmap[VIRT_UART].size, 4); + + /* NamespaceString[] */ + g_array_append_vals(table_data, name, namespace_length); + + acpi_table_end(linker, &table); +}; + /* * ACPI spec, Revision 5.1 Errata A * 5.2.12 Multiple APIC Description Table (MADT) @@ -885,7 +943,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) dsdt = tables_blob->len; build_dsdt(tables_blob, tables->linker, vms); - /* FADT MADT GTDT MCFG SPCR pointed to by RSDT */ + /* FADT MADT GTDT MCFG SPCR DBG2 pointed to by RSDT */ acpi_add_table(table_offsets, tables_blob); build_fadt_rev5(tables_blob, tables->linker, vms, dsdt); @@ -908,6 +966,9 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_spcr(tables_blob, tables->linker, vms); + acpi_add_table(table_offsets, tables_blob); + build_dbg2(tables_blob, tables->linker, vms); + if (vms->ras) { build_ghes_error_table(tables->hardware_errors, tables->linker); acpi_add_table(table_offsets, tables_blob); From 98f5c60fbda773ba5620941adf1cb96b3b2ff758 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 19 Oct 2021 10:00:37 +0200 Subject: [PATCH 0700/1334] bios-tables-test: Generate reference table for virt/DBG2 Add the DBG2 table generated with tests/data/acpi/rebuild-expected-aml.sh Signed-off-by: Eric Auger Acked-by: Michael S. Tsirkin Message-Id: <20211019080037.930641-4-eric.auger@redhat.com> Signed-off-by: Richard Henderson --- tests/data/acpi/virt/DBG2 | Bin 0 -> 87 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/virt/DBG2 b/tests/data/acpi/virt/DBG2 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..86e6314f7b0235ef8ed3e0221e09f996c41f5e98 100644 GIT binary patch literal 87 zcmZ>9ayJTR0D|*Q{>~o33QiFL&I&-l2owUbL9`AKgJ=eA21Zr}H4uw|p@A7lh%qQJ TFmQk+Il-a=3=Gcxz6J~c3~mVl literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 1910d154c2..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/virt/DBG2", From 31511b6fe0251806f425b5a671c6d211e030162d Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 20 Oct 2021 22:21:18 +0800 Subject: [PATCH 0701/1334] hw/arm/virt: Only describe cpu topology since virt-6.2 On existing older machine types, without cpu topology described in ACPI or DT, the guest will populate one by default. With the topology described, it will read the information and set up its topology as instructed, but that may not be the same as what was getting used by default. It's possible that an user application has a dependency on the default topology and if the default one gets changed it will probably behave differently. Based on above consideration we'd better only describe topology information to the guest on 6.2 and later machine types. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Message-Id: <20211020142125.7516-2-wangyanan55@huawei.com> Signed-off-by: Richard Henderson --- hw/arm/virt.c | 1 + include/hw/arm/virt.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4160d49688..15e8d8cf4a 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2816,6 +2816,7 @@ static void virt_machine_6_1_options(MachineClass *mc) virt_machine_6_2_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); mc->smp_props.prefer_sockets = true; + vmc->no_cpu_topology = true; /* qemu ITS was introduced with 6.2 */ vmc->no_tcg_its = true; diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index b461b8d261..dc6b66ffc8 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -125,11 +125,13 @@ struct VirtMachineClass { bool claim_edge_triggered_timers; bool smbios_old_sys_ver; bool no_highmem_ecam; - bool no_ged; /* Machines < 4.2 has no support for ACPI GED device */ + bool no_ged; /* Machines < 4.2 have no support for ACPI GED device */ bool kvm_no_adjvtime; bool no_kvm_steal_time; bool acpi_expose_flash; bool no_secure_gpio; + /* Machines < 6.2 have no support for describing cpu topology to guest */ + bool no_cpu_topology; }; struct VirtMachineState { From b863f0b75852dfd62b3f31b08eeddd3b03694fc2 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 20 Oct 2021 22:21:19 +0800 Subject: [PATCH 0702/1334] device_tree: Add qemu_fdt_add_path qemu_fdt_add_path() works like qemu_fdt_add_subnode(), except it also adds all missing subnodes from the given path. We'll use it in a coming patch where we will add cpu-map to the device tree. And we also tweak an error message of qemu_fdt_add_subnode(). Co-developed-by: Andrew Jones Signed-off-by: Yanan Wang Reviewed-by: David Gibson Reviewed-by: Andrew Jones Cc: David Gibson Cc: Alistair Francis Message-Id: <20211020142125.7516-3-wangyanan55@huawei.com> Signed-off-by: Richard Henderson --- include/sysemu/device_tree.h | 1 + softmmu/device_tree.c | 44 ++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index 8a2fe55622..ef060a9759 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -121,6 +121,7 @@ uint32_t qemu_fdt_get_phandle(void *fdt, const char *path); uint32_t qemu_fdt_alloc_phandle(void *fdt); int qemu_fdt_nop_node(void *fdt, const char *node_path); int qemu_fdt_add_subnode(void *fdt, const char *name); +int qemu_fdt_add_path(void *fdt, const char *path); #define qemu_fdt_setprop_cells(fdt, node_path, property, ...) \ do { \ diff --git a/softmmu/device_tree.c b/softmmu/device_tree.c index b621f63fba..3965c834ca 100644 --- a/softmmu/device_tree.c +++ b/softmmu/device_tree.c @@ -540,8 +540,8 @@ int qemu_fdt_add_subnode(void *fdt, const char *name) retval = fdt_add_subnode(fdt, parent, basename); if (retval < 0) { - error_report("FDT: Failed to create subnode %s: %s", name, - fdt_strerror(retval)); + error_report("%s: Failed to create subnode %s: %s", + __func__, name, fdt_strerror(retval)); exit(1); } @@ -549,6 +549,46 @@ int qemu_fdt_add_subnode(void *fdt, const char *name) return retval; } +/* + * qemu_fdt_add_path: Like qemu_fdt_add_subnode(), but will add + * all missing subnodes from the given path. + */ +int qemu_fdt_add_path(void *fdt, const char *path) +{ + const char *name; + const char *p = path; + int namelen, retval; + int parent = 0; + + if (path[0] != '/') { + return -1; + } + + while (p) { + name = p + 1; + p = strchr(name, '/'); + namelen = p != NULL ? p - name : strlen(name); + + retval = fdt_subnode_offset_namelen(fdt, parent, name, namelen); + if (retval < 0 && retval != -FDT_ERR_NOTFOUND) { + error_report("%s: Unexpected error in finding subnode %.*s: %s", + __func__, namelen, name, fdt_strerror(retval)); + exit(1); + } else if (retval == -FDT_ERR_NOTFOUND) { + retval = fdt_add_subnode_namelen(fdt, parent, name, namelen); + if (retval < 0) { + error_report("%s: Failed to create subnode %.*s: %s", + __func__, namelen, name, fdt_strerror(retval)); + exit(1); + } + } + + parent = retval; + } + + return retval; +} + void qemu_fdt_dumpdtb(void *fdt, int size) { const char *dumpdtb = current_machine->dumpdtb; From 72b0527ff6ab09502e0b320050eb086fc4d61d26 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 20 Oct 2021 22:21:20 +0800 Subject: [PATCH 0703/1334] hw/arm/virt: Add cpu-map to device tree Support device tree CPU topology descriptions. In accordance with the Devicetree Specification, the Linux Doc "arm/cpus.yaml" requires that cpus and cpu nodes in the DT are present. And we have already met the requirement by generating /cpus/cpu@* nodes for members within ms->smp.cpus. Accordingly, we should also create subnodes in cpu-map for the present cpus, each of which relates to an unique cpu node. The Linux Doc "cpu/cpu-topology.txt" states that the hierarchy of CPUs in a SMP system is defined through four entities and they are socket/cluster/core/thread. It is also required that a socket node's child nodes must be one or more cluster nodes. Given that currently we are only provided with information of socket/core/thread, we assume there is one cluster child node in each socket node when creating cpu-map. Co-developed-by: Yanan Wang Signed-off-by: Andrew Jones Signed-off-by: Yanan Wang Message-Id: <20211020142125.7516-4-wangyanan55@huawei.com> Signed-off-by: Richard Henderson --- hw/arm/virt.c | 70 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 15e8d8cf4a..ca433adb5b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -351,20 +351,21 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) int cpu; int addr_cells = 1; const MachineState *ms = MACHINE(vms); + const VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); int smp_cpus = ms->smp.cpus; /* - * From Documentation/devicetree/bindings/arm/cpus.txt - * On ARM v8 64-bit systems value should be set to 2, - * that corresponds to the MPIDR_EL1 register size. - * If MPIDR_EL1[63:32] value is equal to 0 on all CPUs - * in the system, #address-cells can be set to 1, since - * MPIDR_EL1[63:32] bits are not used for CPUs - * identification. + * See Linux Documentation/devicetree/bindings/arm/cpus.yaml + * On ARM v8 64-bit systems value should be set to 2, + * that corresponds to the MPIDR_EL1 register size. + * If MPIDR_EL1[63:32] value is equal to 0 on all CPUs + * in the system, #address-cells can be set to 1, since + * MPIDR_EL1[63:32] bits are not used for CPUs + * identification. * - * Here we actually don't know whether our system is 32- or 64-bit one. - * The simplest way to go is to examine affinity IDs of all our CPUs. If - * at least one of them has Aff3 populated, we set #address-cells to 2. + * Here we actually don't know whether our system is 32- or 64-bit one. + * The simplest way to go is to examine affinity IDs of all our CPUs. If + * at least one of them has Aff3 populated, we set #address-cells to 2. */ for (cpu = 0; cpu < smp_cpus; cpu++) { ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); @@ -407,8 +408,57 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) ms->possible_cpus->cpus[cs->cpu_index].props.node_id); } + if (!vmc->no_cpu_topology) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + } + g_free(nodename); } + + if (!vmc->no_cpu_topology) { + /* + * Add vCPU topology description through fdt node cpu-map. + * + * See Linux Documentation/devicetree/bindings/cpu/cpu-topology.txt + * In a SMP system, the hierarchy of CPUs can be defined through + * four entities that are used to describe the layout of CPUs in + * the system: socket/cluster/core/thread. + * + * A socket node represents the boundary of system physical package + * and its child nodes must be one or more cluster nodes. A system + * can contain several layers of clustering within a single physical + * package and cluster nodes can be contained in parent cluster nodes. + * + * Given that cluster is not yet supported in the vCPU topology, + * we currently generate one cluster node within each socket node + * by default. + */ + qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); + + for (cpu = smp_cpus - 1; cpu >= 0; cpu--) { + char *cpu_path = g_strdup_printf("/cpus/cpu@%d", cpu); + char *map_path; + + if (ms->smp.threads > 1) { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/cluster0/core%d/thread%d", + cpu / (ms->smp.cores * ms->smp.threads), + (cpu / ms->smp.threads) % ms->smp.cores, + cpu % ms->smp.threads); + } else { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/cluster0/core%d", + cpu / ms->smp.cores, + cpu % ms->smp.cores); + } + qemu_fdt_add_path(ms->fdt, map_path); + qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", cpu_path); + + g_free(map_path); + g_free(cpu_path); + } + } } static void fdt_add_its_gic_node(VirtMachineState *vms) From 9de36ef87270b669219b973362a948347ab382f4 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 20 Oct 2021 22:21:21 +0800 Subject: [PATCH 0704/1334] hw/acpi/aml-build: Add Processor hierarchy node structure Add a generic API to build Processor hierarchy node structure (Type 0), which is strictly consistent with descriptions in ACPI 6.3: 5.2.29.1. This function will be used to build ACPI PPTT table for cpu topology. Co-developed-by: Ying Fang Co-developed-by: Henglong Fan Co-developed-by: Yanan Wang Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Michael S. Tsirkin Reviewed-by: Eric Auger Message-Id: <20211020142125.7516-5-wangyanan55@huawei.com> Signed-off-by: Richard Henderson --- hw/acpi/aml-build.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 76af0ebaf9..5195324585 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1964,6 +1964,36 @@ void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, acpi_table_end(linker, &table); } +/* + * ACPI spec, Revision 6.3 + * 5.2.29.1 Processor hierarchy node structure (Type 0) + */ +static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, + uint32_t parent, uint32_t id, + uint32_t *priv_rsrc, + uint32_t priv_num) +{ + int i; + + build_append_byte(tbl, 0); /* Type 0 - processor */ + build_append_byte(tbl, 20 + priv_num * 4); /* Length */ + build_append_int_noprefix(tbl, 0, 2); /* Reserved */ + build_append_int_noprefix(tbl, flags, 4); /* Flags */ + build_append_int_noprefix(tbl, parent, 4); /* Parent */ + build_append_int_noprefix(tbl, id, 4); /* ACPI Processor ID */ + + /* Number of private resources */ + build_append_int_noprefix(tbl, priv_num, 4); + + /* Private resources[N] */ + if (priv_num > 0) { + assert(priv_rsrc); + for (i = 0; i < priv_num; i++) { + build_append_int_noprefix(tbl, priv_rsrc[i], 4); + } + } +} + /* build rev1/rev3/rev5.1 FADT */ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, const char *oem_id, const char *oem_table_id) From 099f2df2e6b0ec1b0f1d4e518f730594a66661a8 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 20 Oct 2021 22:21:22 +0800 Subject: [PATCH 0705/1334] hw/acpi/aml-build: Add PPTT table Add the Processor Properties Topology Table (PPTT) used to describe CPU topology information to ACPI guests. Note, a DT-boot Linux guest with a non-flat CPU topology will see socket and core IDs being sequential integers starting from zero, which is different from ACPI-boot Linux guest, e.g. with -smp 4,sockets=2,cores=2,threads=1 a DT boot produces: cpu: 0 package_id: 0 core_id: 0 cpu: 1 package_id: 0 core_id: 1 cpu: 2 package_id: 1 core_id: 0 cpu: 3 package_id: 1 core_id: 1 an ACPI boot produces: cpu: 0 package_id: 36 core_id: 0 cpu: 1 package_id: 36 core_id: 1 cpu: 2 package_id: 96 core_id: 2 cpu: 3 package_id: 96 core_id: 3 This is due to several reasons: 1) DT cpu nodes do not have an equivalent field to what the PPTT ACPI Processor ID must be, i.e. something equal to the MADT CPU UID or equal to the UID of an ACPI processor container. In both ACPI cases those are platform dependant IDs assigned by the vendor. 2) While QEMU is the vendor for a guest, if the topology specifies SMT (> 1 thread), then, with ACPI, it is impossible to assign a core-id the same value as a package-id, thus it is not possible to have package-id=0 and core-id=0. This is because package and core containers must be in the same ACPI namespace and therefore must have unique UIDs. 3) ACPI processor containers are not mandatorily required for PPTT tables to be used and, due to the limitations of which IDs are selected described above in (2), they are not helpful for QEMU, so we don't build them with this patch. In the absence of them, Linux assigns its own unique IDs. The maintainers have chosen not to use counters from zero, but rather ACPI table offsets, which explains why the numbers are so much larger than with DT. 4) When there is no SMT (threads=1) the core IDs for ACPI boot guests match the logical CPU IDs, because these IDs must be equal to the MADT CPU UID (as no processor containers are present), and QEMU uses the logical CPU ID for these MADT IDs. So in summary, with QEMU as the vendor for the guests, we simply use sequential integers starting from zero for the non-leaf nodes but with ID-valid flag unset, so that guest will ignore them and use table offsets as unique container IDs. And we use logical CPU IDs for the leaf nodes with the ID-valid flag set, which will be consistent with MADT. Currently the implementation of PPTT generation complies with ACPI specification 5.2.29 (Revision 6.3). The 6.3 spec can be found at: https://uefi.org/sites/default/files/resources/ACPI_6_3_May16.pdf Reviewed-by: Eric Auger Co-developed-by: Yanan Wang Signed-off-by: Andrew Jones Signed-off-by: Yanan Wang Reviewed-by: Michael S. Tsirkin Message-Id: <20211020142125.7516-6-wangyanan55@huawei.com> Signed-off-by: Richard Henderson --- hw/acpi/aml-build.c | 59 +++++++++++++++++++++++++++++++++++++ include/hw/acpi/aml-build.h | 3 ++ 2 files changed, 62 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 5195324585..b3b3310df3 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1994,6 +1994,65 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, } } +/* + * ACPI spec, Revision 6.3 + * 5.2.29 Processor Properties Topology Table (PPTT) + */ +void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, + const char *oem_id, const char *oem_table_id) +{ + int pptt_start = table_data->len; + int uid = 0; + int socket; + AcpiTable table = { .sig = "PPTT", .rev = 2, + .oem_id = oem_id, .oem_table_id = oem_table_id }; + + acpi_table_begin(&table, table_data); + + for (socket = 0; socket < ms->smp.sockets; socket++) { + uint32_t socket_offset = table_data->len - pptt_start; + int core; + + build_processor_hierarchy_node( + table_data, + /* + * Physical package - represents the boundary + * of a physical package + */ + (1 << 0), + 0, socket, NULL, 0); + + for (core = 0; core < ms->smp.cores; core++) { + uint32_t core_offset = table_data->len - pptt_start; + int thread; + + if (ms->smp.threads > 1) { + build_processor_hierarchy_node( + table_data, + (0 << 0), /* not a physical package */ + socket_offset, core, NULL, 0); + + for (thread = 0; thread < ms->smp.threads; thread++) { + build_processor_hierarchy_node( + table_data, + (1 << 1) | /* ACPI Processor ID valid */ + (1 << 2) | /* Processor is a Thread */ + (1 << 3), /* Node is a Leaf */ + core_offset, uid++, NULL, 0); + } + } else { + build_processor_hierarchy_node( + table_data, + (1 << 1) | /* ACPI Processor ID valid */ + (1 << 3), /* Node is a Leaf */ + socket_offset, uid++, NULL, 0); + } + } + } + + acpi_table_end(linker, &table); +} + /* build rev1/rev3/rev5.1 FADT */ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, const char *oem_id, const char *oem_table_id) diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 3cf6f2c1b9..8346003a22 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -489,6 +489,9 @@ void build_srat_memory(GArray *table_data, uint64_t base, void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, const char *oem_id, const char *oem_table_id); +void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, + const char *oem_id, const char *oem_table_id); + void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, const char *oem_id, const char *oem_table_id); From 4ebd52b92a560094f5127fa9478044084636cbdf Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 20 Oct 2021 22:21:23 +0800 Subject: [PATCH 0706/1334] tests/data/acpi/virt: Add an empty expected file for PPTT Add a generic empty binary file for the new introduced PPTT table under tests/data/acpi/virt, and list it as files to be changed in tests/qtest/bios-tables-test-allowed-diff.h Signed-off-by: Yanan Wang Reviewed-by: Eric Auger Message-Id: <20211020142125.7516-7-wangyanan55@huawei.com> Signed-off-by: Richard Henderson --- tests/data/acpi/virt/PPTT | 0 tests/qtest/bios-tables-test-allowed-diff.h | 1 + 2 files changed, 1 insertion(+) create mode 100644 tests/data/acpi/virt/PPTT diff --git a/tests/data/acpi/virt/PPTT b/tests/data/acpi/virt/PPTT new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..cb143a55a6 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/virt/PPTT", From 70d23ed534efa038bb10af9f1c537d4eeb0db137 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 20 Oct 2021 22:21:24 +0800 Subject: [PATCH 0707/1334] hw/arm/virt-acpi-build: Generate PPTT table Generate the Processor Properties Topology Table (PPTT) for ARM virt machines supporting it (>= 6.2). Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Eric Auger Message-Id: <20211020142125.7516-8-wangyanan55@huawei.com> Signed-off-by: Richard Henderson --- hw/arm/virt-acpi-build.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index d3bb4cba3e..674f902652 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -943,13 +943,19 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) dsdt = tables_blob->len; build_dsdt(tables_blob, tables->linker, vms); - /* FADT MADT GTDT MCFG SPCR DBG2 pointed to by RSDT */ + /* FADT MADT PPTT GTDT MCFG SPCR DBG2 pointed to by RSDT */ acpi_add_table(table_offsets, tables_blob); build_fadt_rev5(tables_blob, tables->linker, vms, dsdt); acpi_add_table(table_offsets, tables_blob); build_madt(tables_blob, tables->linker, vms); + if (!vmc->no_cpu_topology) { + acpi_add_table(table_offsets, tables_blob); + build_pptt(tables_blob, tables->linker, ms, + vms->oem_id, vms->oem_table_id); + } + acpi_add_table(table_offsets, tables_blob); build_gtdt(tables_blob, tables->linker, vms); From f801789ff00f457044dcd91323316dbde42578d1 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Wed, 20 Oct 2021 22:21:25 +0800 Subject: [PATCH 0708/1334] tests/data/acpi/virt: Update the empty expected file for PPTT Run ./tests/data/acpi/rebuild-expected-aml.sh from build directory to update PPTT binary. Also empty bios-tables-test-allowed-diff.h. Disassembled output of the updated new file: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20180810 (64-bit version) * Copyright (c) 2000 - 2018 Intel Corporation * * Disassembly of tests/data/acpi/virt/PPTT, Fri Oct 8 10:12:32 2021 * * ACPI Data Table [PPTT] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "PPTT" [Processor Properties Topology Table] [004h 0004 4] Table Length : 0000004C [008h 0008 1] Revision : 02 [009h 0009 1] Checksum : A8 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 1] Subtable Type : 00 [Processor Hierarchy Node] [025h 0037 1] Length : 14 [026h 0038 2] Reserved : 0000 [028h 0040 4] Flags (decoded below) : 00000001 Physical package : 1 ACPI Processor ID valid : 0 [02Ch 0044 4] Parent : 00000000 [030h 0048 4] ACPI Processor ID : 00000000 [034h 0052 4] Private Resource Number : 00000000 [038h 0056 1] Subtable Type : 00 [Processor Hierarchy Node] [039h 0057 1] Length : 14 [03Ah 0058 2] Reserved : 0000 [03Ch 0060 4] Flags (decoded below) : 0000000A Physical package : 0 ACPI Processor ID valid : 1 [040h 0064 4] Parent : 00000024 [044h 0068 4] ACPI Processor ID : 00000000 [048h 0072 4] Private Resource Number : 00000000 Raw Table Data: Length 76 (0x4C) 0000: 50 50 54 54 4C 00 00 00 02 A8 42 4F 43 48 53 20 // PPTTL.....BOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC 0020: 01 00 00 00 00 14 00 00 01 00 00 00 00 00 00 00 // ................ 0030: 00 00 00 00 00 00 00 00 00 14 00 00 0A 00 00 00 // ................ 0040: 24 00 00 00 00 00 00 00 00 00 00 00 // $........... Reviewed-by: Eric Auger Signed-off-by: Yanan Wang Message-Id: <20211020142125.7516-9-wangyanan55@huawei.com> Signed-off-by: Richard Henderson --- tests/data/acpi/virt/PPTT | Bin 0 -> 76 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/virt/PPTT b/tests/data/acpi/virt/PPTT index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7a1258ecf123555b24462c98ccbb76b4ac1d0c2b 100644 GIT binary patch literal 76 zcmWFt2nq3FU|?Wc;pFe^5v<@85#X!<1dKp25F11@h%hjKX%HI*fMQ%gwhD|7qyeJ> B2LS*8 literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index cb143a55a6..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/virt/PPTT", From c672f19f328922eff4963b0b61fbdcfa661e1c06 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Thu, 7 Oct 2021 16:17:41 +0800 Subject: [PATCH 0709/1334] target/riscv: Pass the same value to oprsz and maxsz for vmv.v.v oprsz and maxsz are passed with the same value in commit: eee2d61e202. However, vmv.v.v was missed in that commit and should pass the same value as well in its tcg_gen_gvec_2_ptr() call. Signed-off-by: Frank Chang Reviewed-by: Richard Henderson Message-id: 20211007081803.1705656-1-frank.chang@sifive.com Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 081a5ca34d..45be491ed4 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1619,7 +1619,8 @@ static bool trans_vmv_v_v(DisasContext *s, arg_vmv_v_v *a) tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs1), - cpu_env, 0, s->vlen / 8, data, fns[s->sew]); + cpu_env, s->vlen / 8, s->vlen / 8, data, + fns[s->sew]); gen_set_label(over); } return true; From e573a7f325e4d66d1005f7bb80d51ce95f307951 Mon Sep 17 00:00:00 2001 From: Travis Geiselbrecht Date: Fri, 8 Oct 2021 22:50:19 -0700 Subject: [PATCH 0710/1334] target/riscv: line up all of the registers in the info register dump Ensure the columns for all of the register names and values line up. No functional change, just a minor tweak to the output. Signed-off-by: Travis Geiselbrecht Reviewed-by: Alistair Francis Message-id: 20211009055019.545153-1-travisg@gmail.com Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1d69d1887e..660f9ce131 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -258,7 +258,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) } if (riscv_has_ext(env, RVH)) { qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hstatus ", env->hstatus); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsstatus ", + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsstatus", (target_ulong)env->vsstatus); } qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ", env->mip); @@ -289,8 +289,8 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval ", env->mtval); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "stval ", env->stval); if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "htval ", env->htval); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval2 ", env->mtval2); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "htval ", env->htval); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval2 ", env->mtval2); } qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mscratch", env->mscratch); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "sscratch", env->sscratch); @@ -298,7 +298,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) #endif for (i = 0; i < 32; i++) { - qemu_fprintf(f, " %s " TARGET_FMT_lx, + qemu_fprintf(f, " %-8s " TARGET_FMT_lx, riscv_int_regnames[i], env->gpr[i]); if ((i & 3) == 3) { qemu_fprintf(f, "\n"); @@ -306,7 +306,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) } if (flags & CPU_DUMP_FPU) { for (i = 0; i < 32; i++) { - qemu_fprintf(f, " %s %016" PRIx64, + qemu_fprintf(f, " %-8s %016" PRIx64, riscv_fpr_regnames[i], env->fpr[i]); if ((i & 3) == 3) { qemu_fprintf(f, "\n"); From 54c17609372bb119575f3bb7309ea57fc1a58d1d Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 13 Oct 2021 20:41:25 +0200 Subject: [PATCH 0711/1334] target/riscv: Fix orc.b implementation The earlier implementation fell into a corner case for bytes that were 0x01, giving a wrong result (but not affecting our application test cases for strings, as an ASCII value 0x01 is rare in those...). This changes the algorithm to: 1. Mask out the high-bit of each bytes (so that each byte is <= 127). 2. Add 127 to each byte (i.e. if the low 7 bits are not 0, this will overflow into the highest bit of each byte). 3. Bitwise-or the original value back in (to cover those cases where the source byte was exactly 128) to saturate the high-bit. 4. Shift-and-mask (implemented as a mask-and-shift) to extract the MSB of each byte into its LSB. 5. Multiply with 0xff to fan out the LSB to all bits of each byte. Fixes: d7a4fcb034 ("target/riscv: Add orc.b instruction for Zbb, removing gorc/gorci") Signed-off-by: Philipp Tomsich Reported-by: Vincent Palatin Tested-by: Vincent Palatin Reviewed-by: Richard Henderson Message-id: 20211013184125.2010897-1-philipp.tomsich@vrull.eu Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 185c3e9a60..3095624f32 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -249,13 +249,16 @@ static bool trans_rev8_64(DisasContext *ctx, arg_rev8_64 *a) static void gen_orc_b(TCGv ret, TCGv source1) { TCGv tmp = tcg_temp_new(); - TCGv ones = tcg_constant_tl(dup_const_tl(MO_8, 0x01)); + TCGv low7 = tcg_constant_tl(dup_const_tl(MO_8, 0x7f)); - /* Set lsb in each byte if the byte was zero. */ - tcg_gen_sub_tl(tmp, source1, ones); - tcg_gen_andc_tl(tmp, tmp, source1); + /* Set msb in each byte if the byte was non-zero. */ + tcg_gen_and_tl(tmp, source1, low7); + tcg_gen_add_tl(tmp, tmp, low7); + tcg_gen_or_tl(tmp, tmp, source1); + + /* Extract the msb to the lsb in each byte */ + tcg_gen_andc_tl(tmp, tmp, low7); tcg_gen_shri_tl(tmp, tmp, 7); - tcg_gen_andc_tl(tmp, ones, tmp); /* Replicate the lsb of each byte across the byte. */ tcg_gen_muli_tl(ret, tmp, 0xff); From 03fd0c5fe98f5617076527e9783d030294b64d6d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 16 Oct 2021 11:09:08 +0800 Subject: [PATCH 0712/1334] hw/riscv: virt: Use machine->ram as the system memory If default main_mem is used to be registered as the system memory, other memory cannot be initialized. Therefore, the system memory should be initialized to the machine->ram, which consists of the default main_mem and other possible memory required by applications, such as shared hugepage memory in DPDK. Also, the mc->defaul_ram_id should be set to the default main_mem, such as "riscv_virt_board.ram" for the virt machine. Signed-off-by: Mingwang Li Signed-off-by: Yifei Jiang Reviewed-by: Alistair Francis Message-id: 20211016030908.40480-1-limingwang@huawei.com Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index ec0cb69b8c..b3b431c847 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -771,7 +771,6 @@ static void virt_machine_init(MachineState *machine) const MemMapEntry *memmap = virt_memmap; RISCVVirtState *s = RISCV_VIRT_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *main_mem = g_new(MemoryRegion, 1); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); char *plic_hart_config, *soc_name; target_ulong start_addr = memmap[VIRT_DRAM].base; @@ -890,10 +889,8 @@ static void virt_machine_init(MachineState *machine) } /* register system main memory (actual RAM) */ - memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram", - machine->ram_size, &error_fatal); memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base, - main_mem); + machine->ram); /* create device tree */ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, @@ -1032,6 +1029,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id; mc->numa_mem_supported = true; + mc->default_ram_id = "riscv_virt_board.ram"; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); From 61d56494884b0d4bbf78d0561258b3548dea3390 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Fri, 15 Oct 2021 15:45:02 +0800 Subject: [PATCH 0713/1334] target/riscv: fix TB_FLAGS bits overlapping bug for rvv/rvh TB_FLAGS mem_idx bits was extended from 2 bits to 3 bits in commit: c445593, but other TB_FLAGS bits for rvv and rvh were not shift as well so these bits may overlap with each other when rvv is enabled. Signed-off-by: Frank Chang Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20211015074627.3957162-2-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 14 +++++++------- target/riscv/translate.c | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 9e55b2f5b1..ce42c83bda 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -378,7 +378,6 @@ void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, target_ulong riscv_cpu_get_fflags(CPURISCVState *env); void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); -#define TB_FLAGS_MMU_MASK 7 #define TB_FLAGS_PRIV_MMU_MASK 3 #define TB_FLAGS_PRIV_HYP_ACCESS_MASK (1 << 2) #define TB_FLAGS_MSTATUS_FS MSTATUS_FS @@ -387,13 +386,14 @@ typedef CPURISCVState CPUArchState; typedef RISCVCPU ArchCPU; #include "exec/cpu-all.h" -FIELD(TB_FLAGS, VL_EQ_VLMAX, 2, 1) -FIELD(TB_FLAGS, LMUL, 3, 2) -FIELD(TB_FLAGS, SEW, 5, 3) -FIELD(TB_FLAGS, VILL, 8, 1) +FIELD(TB_FLAGS, MEM_IDX, 0, 3) +FIELD(TB_FLAGS, VL_EQ_VLMAX, 3, 1) +FIELD(TB_FLAGS, LMUL, 4, 2) +FIELD(TB_FLAGS, SEW, 6, 3) +FIELD(TB_FLAGS, VILL, 9, 1) /* Is a Hypervisor instruction load/store allowed? */ -FIELD(TB_FLAGS, HLSX, 9, 1) -FIELD(TB_FLAGS, MSTATUS_HS_FS, 10, 2) +FIELD(TB_FLAGS, HLSX, 10, 1) +FIELD(TB_FLAGS, MSTATUS_HS_FS, 11, 2) bool riscv_cpu_is_32bit(CPURISCVState *env); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 6d7fbca1fa..62214e97fa 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -501,7 +501,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) uint32_t tb_flags = ctx->base.tb->flags; ctx->pc_succ_insn = ctx->base.pc_first; - ctx->mem_idx = tb_flags & TB_FLAGS_MMU_MASK; + ctx->mem_idx = FIELD_EX32(tb_flags, TB_FLAGS, MEM_IDX); ctx->mstatus_fs = tb_flags & TB_FLAGS_MSTATUS_FS; ctx->priv_ver = env->priv_ver; #if !defined(CONFIG_USER_ONLY) From 31dbcff713aa297033a008b5188fbb37abf85f36 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 18 Oct 2021 14:32:00 +1000 Subject: [PATCH 0714/1334] target/riscv: Remove some unused macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 1a9540d1f1a ("target/riscv: Drop support for ISA spec version 1.09.1") these definitions are unused, remove them. Signed-off-by: Alistair Francis Reviewed-by: Frank Chang Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Message-id: f4d8a7a035f39c0a35d44c1e371c5c99cc2fa15a.1634531504.git.alistair.francis@wdc.com --- target/riscv/cpu_bits.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 999187a9ee..3aa2512d13 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -427,14 +427,6 @@ #define SATP64_ASID 0x0FFFF00000000000ULL #define SATP64_PPN 0x00000FFFFFFFFFFFULL -/* VM modes (mstatus.vm) privileged ISA 1.9.1 */ -#define VM_1_09_MBARE 0 -#define VM_1_09_MBB 1 -#define VM_1_09_MBBID 2 -#define VM_1_09_SV32 8 -#define VM_1_09_SV39 9 -#define VM_1_09_SV48 10 - /* VM modes (satp.mode) privileged ISA 1.10 */ #define VM_1_10_MBARE 0 #define VM_1_10_SV32 1 From 9d3d60b704f48217cf9b38b883cc15c40f76f286 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 18 Oct 2021 14:32:15 +1000 Subject: [PATCH 0715/1334] target/riscv: Organise the CPU properties Organise the CPU properties so that standard extensions come first then followed by experimental extensions. Signed-off-by: Alistair Francis Reviewed-by: Frank Chang Reviewed-by: Bin Meng Message-id: b6598570f60c5ee7f402be56d837bb44b289cc4d.1634531504.git.alistair.francis@wdc.com --- target/riscv/cpu.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 660f9ce131..11bb59ac6d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -581,6 +581,7 @@ static void riscv_cpu_init(Object *obj) } static Property riscv_cpu_properties[] = { + /* Defaults for standard extensions */ DEFINE_PROP_BOOL("i", RISCVCPU, cfg.ext_i, true), DEFINE_PROP_BOOL("e", RISCVCPU, cfg.ext_e, false), DEFINE_PROP_BOOL("g", RISCVCPU, cfg.ext_g, true), @@ -591,22 +592,24 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("c", RISCVCPU, cfg.ext_c, true), DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true), DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), - /* This is experimental so mark with 'x-' */ + DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), + DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), + DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), + DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), + DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), + + DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), + + /* These are experimental so mark with 'x-' */ DEFINE_PROP_BOOL("x-zba", RISCVCPU, cfg.ext_zba, false), DEFINE_PROP_BOOL("x-zbb", RISCVCPU, cfg.ext_zbb, false), DEFINE_PROP_BOOL("x-zbc", RISCVCPU, cfg.ext_zbc, false), DEFINE_PROP_BOOL("x-zbs", RISCVCPU, cfg.ext_zbs, false), DEFINE_PROP_BOOL("x-h", RISCVCPU, cfg.ext_h, false), DEFINE_PROP_BOOL("x-v", RISCVCPU, cfg.ext_v, false), - DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), - DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), - DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), - DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec), DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), - DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), - DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), /* ePMP 0.9.3 */ DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false), From 53677acf25afa8e529d7f81a6ae9a03d15c72713 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:16:55 -0700 Subject: [PATCH 0716/1334] target/riscv: Move cpu_get_tb_cpu_state out of line Move the function to cpu_helper.c, as it is large and growing. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-2-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 47 ++------------------------------------- target/riscv/cpu_helper.c | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ce42c83bda..f543364d51 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -413,51 +413,8 @@ static inline uint32_t vext_get_vlmax(RISCVCPU *cpu, target_ulong vtype) return cpu->cfg.vlen >> (sew + 3 - lmul); } -static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *pflags) -{ - uint32_t flags = 0; - - *pc = env->pc; - *cs_base = 0; - - if (riscv_has_ext(env, RVV)) { - uint32_t vlmax = vext_get_vlmax(env_archcpu(env), env->vtype); - bool vl_eq_vlmax = (env->vstart == 0) && (vlmax == env->vl); - flags = FIELD_DP32(flags, TB_FLAGS, VILL, - FIELD_EX64(env->vtype, VTYPE, VILL)); - flags = FIELD_DP32(flags, TB_FLAGS, SEW, - FIELD_EX64(env->vtype, VTYPE, VSEW)); - flags = FIELD_DP32(flags, TB_FLAGS, LMUL, - FIELD_EX64(env->vtype, VTYPE, VLMUL)); - flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); - } else { - flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); - } - -#ifdef CONFIG_USER_ONLY - flags |= TB_FLAGS_MSTATUS_FS; -#else - flags |= cpu_mmu_index(env, 0); - if (riscv_cpu_fp_enabled(env)) { - flags |= env->mstatus & MSTATUS_FS; - } - - if (riscv_has_ext(env, RVH)) { - if (env->priv == PRV_M || - (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || - (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && - get_field(env->hstatus, HSTATUS_HU))) { - flags = FIELD_DP32(flags, TB_FLAGS, HLSX, 1); - } - - flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_FS, - get_field(env->mstatus_hs, MSTATUS_FS)); - } -#endif - - *pflags = flags; -} +void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *pflags); RISCVException riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index d41d5cd27c..14d1d3cb72 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -35,6 +35,52 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) #endif } +void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *pflags) +{ + uint32_t flags = 0; + + *pc = env->pc; + *cs_base = 0; + + if (riscv_has_ext(env, RVV)) { + uint32_t vlmax = vext_get_vlmax(env_archcpu(env), env->vtype); + bool vl_eq_vlmax = (env->vstart == 0) && (vlmax == env->vl); + flags = FIELD_DP32(flags, TB_FLAGS, VILL, + FIELD_EX64(env->vtype, VTYPE, VILL)); + flags = FIELD_DP32(flags, TB_FLAGS, SEW, + FIELD_EX64(env->vtype, VTYPE, VSEW)); + flags = FIELD_DP32(flags, TB_FLAGS, LMUL, + FIELD_EX64(env->vtype, VTYPE, VLMUL)); + flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); + } else { + flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); + } + +#ifdef CONFIG_USER_ONLY + flags |= TB_FLAGS_MSTATUS_FS; +#else + flags |= cpu_mmu_index(env, 0); + if (riscv_cpu_fp_enabled(env)) { + flags |= env->mstatus & MSTATUS_FS; + } + + if (riscv_has_ext(env, RVH)) { + if (env->priv == PRV_M || + (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || + (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && + get_field(env->hstatus, HSTATUS_HU))) { + flags = FIELD_DP32(flags, TB_FLAGS, HLSX, 1); + } + + flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_FS, + get_field(env->mstatus_hs, MSTATUS_FS)); + } +#endif + + *pflags = flags; +} + #ifndef CONFIG_USER_ONLY static int riscv_cpu_local_irq_pending(CPURISCVState *env) { From 99bc874fb3a0709c36ae4e594a1262ce1660e698 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:16:56 -0700 Subject: [PATCH 0717/1334] target/riscv: Create RISCVMXL enumeration Move the MXL_RV* defines to enumerators. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-3-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 3aa2512d13..cffcd3a5df 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -364,9 +364,11 @@ #define MISA32_MXL 0xC0000000 #define MISA64_MXL 0xC000000000000000ULL -#define MXL_RV32 1 -#define MXL_RV64 2 -#define MXL_RV128 3 +typedef enum { + MXL_RV32 = 1, + MXL_RV64 = 2, + MXL_RV128 = 3, +} RISCVMXL; /* sstatus CSR bits */ #define SSTATUS_UIE 0x00000001 From e91a7227cb802ea62ffa14707ebc2f588b01213d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:16:57 -0700 Subject: [PATCH 0718/1334] target/riscv: Split misa.mxl and misa.ext The hw representation of misa.mxl is at the high bits of the misa csr. Representing this in the same way inside QEMU results in overly complex code trying to check that field. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-4-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- linux-user/elfload.c | 2 +- linux-user/riscv/cpu_loop.c | 2 +- target/riscv/cpu.c | 78 +++++++++++++++++++++---------------- target/riscv/cpu.h | 15 +++---- target/riscv/csr.c | 44 ++++++++++++++------- target/riscv/gdbstub.c | 8 ++-- target/riscv/machine.c | 10 +++-- target/riscv/translate.c | 10 +++-- 8 files changed, 100 insertions(+), 69 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index eb32f3e2cb..f9b8261692 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1448,7 +1448,7 @@ static uint32_t get_elf_hwcap(void) uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A') | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C'); - return cpu->env.misa & mask; + return cpu->env.misa_ext & mask; #undef MISA_BIT } diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 9859a366e4..e5bb6d908a 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -133,7 +133,7 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) env->gpr[xSP] = regs->sp; env->elf_flags = info->elf_flags; - if ((env->misa & RVE) && !(env->elf_flags & EF_RISCV_RVE)) { + if ((env->misa_ext & RVE) && !(env->elf_flags & EF_RISCV_RVE)) { error_report("Incompatible ELF: RVE cpu requires RVE ABI binary"); exit(EXIT_FAILURE); } diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 11bb59ac6d..f18bb4b21e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -110,16 +110,13 @@ const char *riscv_cpu_get_trap_name(target_ulong cause, bool async) bool riscv_cpu_is_32bit(CPURISCVState *env) { - if (env->misa & RV64) { - return false; - } - - return true; + return env->misa_mxl == MXL_RV32; } -static void set_misa(CPURISCVState *env, target_ulong misa) +static void set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext) { - env->misa_mask = env->misa = misa; + env->misa_mxl_max = env->misa_mxl = mxl; + env->misa_ext_mask = env->misa_ext = ext; } static void set_priv_version(CPURISCVState *env, int priv_ver) @@ -148,9 +145,9 @@ static void riscv_any_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; #if defined(TARGET_RISCV32) - set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVU); + set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVU); #elif defined(TARGET_RISCV64) - set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVU); + set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU); #endif set_priv_version(env, PRIV_VERSION_1_11_0); } @@ -160,20 +157,20 @@ static void rv64_base_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; /* We set this in the realise function */ - set_misa(env, RV64); + set_misa(env, MXL_RV64, 0); } static void rv64_sifive_u_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); } static void rv64_sifive_e_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU); + set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); qdev_prop_set_bit(DEVICE(obj), "mmu", false); } @@ -182,20 +179,20 @@ static void rv32_base_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; /* We set this in the realise function */ - set_misa(env, RV32); + set_misa(env, MXL_RV32, 0); } static void rv32_sifive_u_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); } static void rv32_sifive_e_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU); + set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); qdev_prop_set_bit(DEVICE(obj), "mmu", false); } @@ -203,7 +200,7 @@ static void rv32_sifive_e_cpu_init(Object *obj) static void rv32_ibex_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV32 | RVI | RVM | RVC | RVU); + set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); qdev_prop_set_bit(DEVICE(obj), "mmu", false); qdev_prop_set_bit(DEVICE(obj), "x-epmp", true); @@ -212,7 +209,7 @@ static void rv32_ibex_cpu_init(Object *obj) static void rv32_imafcu_nommu_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVC | RVU); + set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); qdev_prop_set_bit(DEVICE(obj), "mmu", false); @@ -360,6 +357,7 @@ static void riscv_cpu_reset(DeviceState *dev) mcc->parent_reset(dev); #ifndef CONFIG_USER_ONLY + env->misa_mxl = env->misa_mxl_max; env->priv = PRV_M; env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); env->mcause = 0; @@ -388,7 +386,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); int priv_version = 0; - target_ulong target_misa = env->misa; Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -434,8 +431,23 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) set_resetvec(env, cpu->cfg.resetvec); - /* If only XLEN is set for misa, then set misa from properties */ - if (env->misa == RV32 || env->misa == RV64) { + /* Validate that MISA_MXL is set properly. */ + switch (env->misa_mxl_max) { +#ifdef TARGET_RISCV64 + case MXL_RV64: + break; +#endif + case MXL_RV32: + break; + default: + g_assert_not_reached(); + } + assert(env->misa_mxl_max == env->misa_mxl); + + /* If only MISA_EXT is unset for misa, then set it from properties */ + if (env->misa_ext == 0) { + uint32_t ext = 0; + /* Do some ISA extension error checking */ if (cpu->cfg.ext_i && cpu->cfg.ext_e) { error_setg(errp, @@ -462,38 +474,38 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) /* Set the ISA extensions, checks should have happened above */ if (cpu->cfg.ext_i) { - target_misa |= RVI; + ext |= RVI; } if (cpu->cfg.ext_e) { - target_misa |= RVE; + ext |= RVE; } if (cpu->cfg.ext_m) { - target_misa |= RVM; + ext |= RVM; } if (cpu->cfg.ext_a) { - target_misa |= RVA; + ext |= RVA; } if (cpu->cfg.ext_f) { - target_misa |= RVF; + ext |= RVF; } if (cpu->cfg.ext_d) { - target_misa |= RVD; + ext |= RVD; } if (cpu->cfg.ext_c) { - target_misa |= RVC; + ext |= RVC; } if (cpu->cfg.ext_s) { - target_misa |= RVS; + ext |= RVS; } if (cpu->cfg.ext_u) { - target_misa |= RVU; + ext |= RVU; } if (cpu->cfg.ext_h) { - target_misa |= RVH; + ext |= RVH; } if (cpu->cfg.ext_v) { int vext_version = VEXT_VERSION_0_07_1; - target_misa |= RVV; + ext |= RVV; if (!is_power_of_2(cpu->cfg.vlen)) { error_setg(errp, "Vector extension VLEN must be power of 2"); @@ -532,7 +544,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) set_vext_version(env, vext_version); } - set_misa(env, target_misa); + set_misa(env, env->misa_mxl, ext); } riscv_cpu_register_gdb_regs_for_features(cs); @@ -708,7 +720,7 @@ char *riscv_isa_string(RISCVCPU *cpu) char *isa_str = g_new(char, maxlen); char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS); for (i = 0; i < sizeof(riscv_exts); i++) { - if (cpu->env.misa & RV(riscv_exts[i])) { + if (cpu->env.misa_ext & RV(riscv_exts[i])) { *p++ = qemu_tolower(riscv_exts[i]); } } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f543364d51..90d11e3c2a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -25,6 +25,7 @@ #include "exec/cpu-defs.h" #include "fpu/softfloat-types.h" #include "qom/object.h" +#include "cpu_bits.h" #define TCG_GUEST_DEFAULT_MO 0 @@ -51,9 +52,6 @@ # define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE64 #endif -#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2)) -#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2)) - #define RV(x) ((target_ulong)1 << (x - 'A')) #define RVI RV('I') @@ -133,8 +131,12 @@ struct CPURISCVState { target_ulong priv_ver; target_ulong bext_ver; target_ulong vext_ver; - target_ulong misa; - target_ulong misa_mask; + + /* RISCVMXL, but uint32_t for vmstate migration */ + uint32_t misa_mxl; /* current mxl */ + uint32_t misa_mxl_max; /* max mxl for this cpu */ + uint32_t misa_ext; /* current extensions */ + uint32_t misa_ext_mask; /* max ext for this cpu */ uint32_t features; @@ -313,7 +315,7 @@ struct RISCVCPU { static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) { - return (env->misa & ext) != 0; + return (env->misa_ext & ext) != 0; } static inline bool riscv_feature(CPURISCVState *env, int feature) @@ -322,7 +324,6 @@ static inline bool riscv_feature(CPURISCVState *env, int feature) } #include "cpu_user.h" -#include "cpu_bits.h" extern const char * const riscv_int_regnames[]; extern const char * const riscv_fpr_regnames[]; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 23fbbd3216..d0c86a300d 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -39,7 +39,7 @@ static RISCVException fs(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) /* loose check condition for fcsr in vector extension */ - if ((csrno == CSR_FCSR) && (env->misa & RVV)) { + if ((csrno == CSR_FCSR) && (env->misa_ext & RVV)) { return RISCV_EXCP_NONE; } if (!env->debugger && !riscv_cpu_fp_enabled(env)) { @@ -51,7 +51,7 @@ static RISCVException fs(CPURISCVState *env, int csrno) static RISCVException vs(CPURISCVState *env, int csrno) { - if (env->misa & RVV) { + if (env->misa_ext & RVV) { return RISCV_EXCP_NONE; } return RISCV_EXCP_ILLEGAL_INST; @@ -557,7 +557,22 @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno, static RISCVException read_misa(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->misa; + target_ulong misa; + + switch (env->misa_mxl) { + case MXL_RV32: + misa = (target_ulong)MXL_RV32 << 30; + break; +#ifdef TARGET_RISCV64 + case MXL_RV64: + misa = (target_ulong)MXL_RV64 << 62; + break; +#endif + default: + g_assert_not_reached(); + } + + *val = misa | env->misa_ext; return RISCV_EXCP_NONE; } @@ -583,8 +598,13 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } + /* + * misa.MXL writes are not supported by QEMU. + * Drop writes to those bits. + */ + /* Mask extensions that are not supported by this hart */ - val &= env->misa_mask; + val &= env->misa_ext_mask; /* Mask extensions that are not supported by QEMU */ val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU); @@ -601,20 +621,14 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, val &= ~RVC; } - /* misa.MXL writes are not supported by QEMU */ - if (riscv_cpu_is_32bit(env)) { - val = (env->misa & MISA32_MXL) | (val & ~MISA32_MXL); - } else { - val = (env->misa & MISA64_MXL) | (val & ~MISA64_MXL); + /* If nothing changed, do nothing. */ + if (val == env->misa_ext) { + return RISCV_EXCP_NONE; } /* flush translation cache */ - if (val != env->misa) { - tb_flush(env_cpu(env)); - } - - env->misa = val; - + tb_flush(env_cpu(env)); + env->misa_ext = val; return RISCV_EXCP_NONE; } diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index a7a9c0b1fe..5257df0217 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -54,10 +54,10 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) static int riscv_gdb_get_fpu(CPURISCVState *env, GByteArray *buf, int n) { if (n < 32) { - if (env->misa & RVD) { + if (env->misa_ext & RVD) { return gdb_get_reg64(buf, env->fpr[n]); } - if (env->misa & RVF) { + if (env->misa_ext & RVF) { return gdb_get_reg32(buf, env->fpr[n]); } /* there is hole between ft11 and fflags in fpu.xml */ @@ -191,10 +191,10 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - if (env->misa & RVD) { + if (env->misa_ext & RVD) { gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, 36, "riscv-64bit-fpu.xml", 0); - } else if (env->misa & RVF) { + } else if (env->misa_ext & RVF) { gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, 36, "riscv-32bit-fpu.xml", 0); } diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 16a08302da..f64b2a96c1 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -140,8 +140,8 @@ static const VMStateDescription vmstate_hyper = { const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32), VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32), @@ -153,8 +153,10 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.guest_phys_fault_addr, RISCVCPU), VMSTATE_UINTTL(env.priv_ver, RISCVCPU), VMSTATE_UINTTL(env.vext_ver, RISCVCPU), - VMSTATE_UINTTL(env.misa, RISCVCPU), - VMSTATE_UINTTL(env.misa_mask, RISCVCPU), + VMSTATE_UINT32(env.misa_mxl, RISCVCPU), + VMSTATE_UINT32(env.misa_ext, RISCVCPU), + VMSTATE_UINT32(env.misa_mxl_max, RISCVCPU), + VMSTATE_UINT32(env.misa_ext_mask, RISCVCPU), VMSTATE_UINT32(env.features, RISCVCPU), VMSTATE_UINTTL(env.priv, RISCVCPU), VMSTATE_UINTTL(env.virt, RISCVCPU), diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 62214e97fa..0aa2122529 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -55,7 +55,8 @@ typedef struct DisasContext { /* pc_succ_insn points to the instruction following base.pc_next */ target_ulong pc_succ_insn; target_ulong priv_ver; - target_ulong misa; + RISCVMXL xl; + uint32_t misa_ext; uint32_t opcode; uint32_t mstatus_fs; uint32_t mstatus_hs_fs; @@ -86,7 +87,7 @@ typedef struct DisasContext { static inline bool has_ext(DisasContext *ctx, uint32_t ext) { - return ctx->misa & ext; + return ctx->misa_ext & ext; } #ifdef TARGET_RISCV32 @@ -96,7 +97,7 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) #else static inline bool is_32bit(DisasContext *ctx) { - return (ctx->misa & RV32) == RV32; + return ctx->xl == MXL_RV32; } #endif @@ -513,7 +514,8 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #else ctx->virt_enabled = false; #endif - ctx->misa = env->misa; + ctx->xl = env->misa_mxl; + ctx->misa_ext = env->misa_ext; ctx->frm = -1; /* unknown rounding mode */ ctx->ext_ifencei = cpu->cfg.ext_ifencei; ctx->vlen = cpu->cfg.vlen; From db23e5d981ab22da0bfe1150f4828d08484b1fba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:16:58 -0700 Subject: [PATCH 0719/1334] target/riscv: Replace riscv_cpu_is_32bit with riscv_cpu_mxl Shortly, the set of supported XL will not be just 32 and 64, and representing that properly using the enumeration will be imperative. Two places, booting and gdb, intentionally use misa_mxl_max to emphasize the use of the reset value of misa.mxl, and not the current cpu state. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-5-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- hw/riscv/boot.c | 2 +- semihosting/arm-compat-semi.c | 2 +- target/riscv/cpu.c | 24 ++++++++++++++---------- target/riscv/cpu.h | 9 ++++++++- target/riscv/cpu_helper.c | 12 ++++++------ target/riscv/csr.c | 24 ++++++++++++------------ target/riscv/gdbstub.c | 2 +- target/riscv/monitor.c | 4 ++-- 8 files changed, 45 insertions(+), 34 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 993bf89064..d1ffc7b56c 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -35,7 +35,7 @@ bool riscv_is_32bit(RISCVHartArrayState *harts) { - return riscv_cpu_is_32bit(&harts->harts[0].env); + return harts->harts[0].env.misa_mxl_max == MXL_RV32; } target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 01badea99c..37963becae 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -775,7 +775,7 @@ static inline bool is_64bit_semihosting(CPUArchState *env) #if defined(TARGET_ARM) return is_a64(env); #elif defined(TARGET_RISCV) - return !riscv_cpu_is_32bit(env); + return riscv_cpu_mxl(env) != MXL_RV32; #else #error un-handled architecture #endif diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index f18bb4b21e..3d144af1ab 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -108,11 +108,6 @@ const char *riscv_cpu_get_trap_name(target_ulong cause, bool async) } } -bool riscv_cpu_is_32bit(CPURISCVState *env) -{ - return env->misa_mxl == MXL_RV32; -} - static void set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext) { env->misa_mxl_max = env->misa_mxl = mxl; @@ -249,7 +244,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) #ifndef CONFIG_USER_ONLY qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", (target_ulong)env->mstatus); - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatush ", (target_ulong)(env->mstatus >> 32)); } @@ -372,10 +367,16 @@ static void riscv_cpu_reset(DeviceState *dev) static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) { RISCVCPU *cpu = RISCV_CPU(s); - if (riscv_cpu_is_32bit(&cpu->env)) { + + switch (riscv_cpu_mxl(&cpu->env)) { + case MXL_RV32: info->print_insn = print_insn_riscv32; - } else { + break; + case MXL_RV64: info->print_insn = print_insn_riscv64; + break; + default: + g_assert_not_reached(); } } @@ -634,10 +635,13 @@ static gchar *riscv_gdb_arch_name(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - if (riscv_cpu_is_32bit(env)) { + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: return g_strdup("riscv:rv32"); - } else { + case MXL_RV64: return g_strdup("riscv:rv64"); + default: + g_assert_not_reached(); } } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 90d11e3c2a..e23157b05c 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -396,7 +396,14 @@ FIELD(TB_FLAGS, VILL, 9, 1) FIELD(TB_FLAGS, HLSX, 10, 1) FIELD(TB_FLAGS, MSTATUS_HS_FS, 11, 2) -bool riscv_cpu_is_32bit(CPURISCVState *env); +#ifdef TARGET_RISCV32 +#define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) +#else +static inline RISCVMXL riscv_cpu_mxl(CPURISCVState *env) +{ + return env->misa_mxl; +} +#endif /* * A simplification for VLMAX diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 14d1d3cb72..403f54171d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -152,7 +152,7 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env) void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) { - uint64_t sd = riscv_cpu_is_32bit(env) ? MSTATUS32_SD : MSTATUS64_SD; + uint64_t sd = riscv_cpu_mxl(env) == MXL_RV32 ? MSTATUS32_SD : MSTATUS64_SD; uint64_t mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS | MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE | MSTATUS64_UXL | sd; @@ -447,7 +447,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, if (first_stage == true) { if (use_background) { - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { base = (hwaddr)get_field(env->vsatp, SATP32_PPN) << PGSHIFT; vm = get_field(env->vsatp, SATP32_MODE); } else { @@ -455,7 +455,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, vm = get_field(env->vsatp, SATP64_MODE); } } else { - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { base = (hwaddr)get_field(env->satp, SATP32_PPN) << PGSHIFT; vm = get_field(env->satp, SATP32_MODE); } else { @@ -465,7 +465,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } widened = 0; } else { - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { base = (hwaddr)get_field(env->hgatp, SATP32_PPN) << PGSHIFT; vm = get_field(env->hgatp, SATP32_MODE); } else { @@ -558,7 +558,7 @@ restart: } target_ulong pte; - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { pte = address_space_ldl(cs->as, pte_addr, attrs, &res); } else { pte = address_space_ldq(cs->as, pte_addr, attrs, &res); @@ -678,7 +678,7 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, int page_fault_exceptions, vm; uint64_t stap_mode; - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { stap_mode = SATP32_MODE; } else { stap_mode = SATP64_MODE; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index d0c86a300d..9c0753bc8b 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -95,7 +95,7 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; } - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { switch (csrno) { case CSR_CYCLEH: if (!get_field(env->hcounteren, COUNTEREN_CY) && @@ -130,7 +130,7 @@ static RISCVException ctr(CPURISCVState *env, int csrno) static RISCVException ctr32(CPURISCVState *env, int csrno) { - if (!riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) != MXL_RV32) { return RISCV_EXCP_ILLEGAL_INST; } @@ -145,7 +145,7 @@ static RISCVException any(CPURISCVState *env, int csrno) static RISCVException any32(CPURISCVState *env, int csrno) { - if (!riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) != MXL_RV32) { return RISCV_EXCP_ILLEGAL_INST; } @@ -180,7 +180,7 @@ static RISCVException hmode(CPURISCVState *env, int csrno) static RISCVException hmode32(CPURISCVState *env, int csrno) { - if (!riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) != MXL_RV32) { if (riscv_cpu_virt_enabled(env)) { return RISCV_EXCP_ILLEGAL_INST; } else { @@ -486,7 +486,7 @@ static RISCVException read_mstatus(CPURISCVState *env, int csrno, static int validate_vm(CPURISCVState *env, target_ulong vm) { - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { return valid_vm_1_10_32[vm & 0xf]; } else { return valid_vm_1_10_64[vm & 0xf]; @@ -510,7 +510,7 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR | MSTATUS_TW; - if (!riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) != MXL_RV32) { /* * RV32: MPV and GVA are not in mstatus. The current plan is to * add them to mstatush. For now, we just don't support it. @@ -522,7 +522,7 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | ((mstatus & MSTATUS_XS) == MSTATUS_XS); - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { mstatus = set_field(mstatus, MSTATUS32_SD, dirty); } else { mstatus = set_field(mstatus, MSTATUS64_SD, dirty); @@ -795,7 +795,7 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno, { target_ulong mask = (sstatus_v1_10_mask); - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { mask |= SSTATUS32_SD; } else { mask |= SSTATUS64_SD; @@ -1006,7 +1006,7 @@ static RISCVException write_satp(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { vm = validate_vm(env, get_field(val, SATP32_MODE)); mask = (val ^ env->satp) & (SATP32_MODE | SATP32_ASID | SATP32_PPN); asid = (val ^ env->satp) & SATP32_ASID; @@ -1034,7 +1034,7 @@ static RISCVException read_hstatus(CPURISCVState *env, int csrno, target_ulong *val) { *val = env->hstatus; - if (!riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) != MXL_RV32) { /* We only support 64-bit VSXL */ *val = set_field(*val, HSTATUS_VSXL, 2); } @@ -1047,7 +1047,7 @@ static RISCVException write_hstatus(CPURISCVState *env, int csrno, target_ulong val) { env->hstatus = val; - if (!riscv_cpu_is_32bit(env) && get_field(val, HSTATUS_VSXL) != 2) { + if (riscv_cpu_mxl(env) != MXL_RV32 && get_field(val, HSTATUS_VSXL) != 2) { qemu_log_mask(LOG_UNIMP, "QEMU does not support mixed HSXLEN options."); } if (get_field(val, HSTATUS_VSBE) != 0) { @@ -1215,7 +1215,7 @@ static RISCVException write_htimedelta(CPURISCVState *env, int csrno, return RISCV_EXCP_ILLEGAL_INST; } - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { env->htimedelta = deposit64(env->htimedelta, 0, 32, (uint64_t)val); } else { env->htimedelta = val; diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 5257df0217..23429179e2 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -161,7 +161,7 @@ static int riscv_gen_dynamic_csr_xml(CPUState *cs, int base_reg) CPURISCVState *env = &cpu->env; GString *s = g_string_new(NULL); riscv_csr_predicate_fn predicate; - int bitsize = riscv_cpu_is_32bit(env) ? 32 : 64; + int bitsize = 16 << env->misa_mxl_max; int i; g_string_printf(s, ""); diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c index f7e6ea72b3..7efb4b62c1 100644 --- a/target/riscv/monitor.c +++ b/target/riscv/monitor.c @@ -150,7 +150,7 @@ static void mem_info_svxx(Monitor *mon, CPUArchState *env) target_ulong last_size; int last_attr; - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { base = (hwaddr)get_field(env->satp, SATP32_PPN) << PGSHIFT; vm = get_field(env->satp, SATP32_MODE); } else { @@ -220,7 +220,7 @@ void hmp_info_mem(Monitor *mon, const QDict *qdict) return; } - if (riscv_cpu_is_32bit(env)) { + if (riscv_cpu_mxl(env) == MXL_RV32) { if (!(env->satp & SATP32_MODE)) { monitor_printf(mon, "No translation or protection\n"); return; From 92371bd9033e5a50a7541d96ff8ad067930a4f93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:16:59 -0700 Subject: [PATCH 0720/1334] target/riscv: Add MXL/SXL/UXL to TB_FLAGS Begin adding support for switching XLEN at runtime. Extract the effective XLEN from MISA and MSTATUS and store for use during translation. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-6-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 8 ++++++++ target/riscv/cpu.h | 2 ++ target/riscv/cpu_helper.c | 33 +++++++++++++++++++++++++++++++++ target/riscv/csr.c | 3 +++ target/riscv/translate.c | 2 +- 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 3d144af1ab..dd9eccd68e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -355,6 +355,14 @@ static void riscv_cpu_reset(DeviceState *dev) env->misa_mxl = env->misa_mxl_max; env->priv = PRV_M; env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); + if (env->misa_mxl > MXL_RV32) { + /* + * The reset status of SXL/UXL is undefined, but mstatus is WARL + * and we must ensure that the value after init is valid for read. + */ + env->mstatus = set_field(env->mstatus, MSTATUS64_SXL, env->misa_mxl); + env->mstatus = set_field(env->mstatus, MSTATUS64_UXL, env->misa_mxl); + } env->mcause = 0; env->pc = env->resetvec; env->two_stage_lookup = false; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index e23157b05c..a33dc30be8 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -395,6 +395,8 @@ FIELD(TB_FLAGS, VILL, 9, 1) /* Is a Hypervisor instruction load/store allowed? */ FIELD(TB_FLAGS, HLSX, 10, 1) FIELD(TB_FLAGS, MSTATUS_HS_FS, 11, 2) +/* The combination of MXL/SXL/UXL that applies to the current cpu mode. */ +FIELD(TB_FLAGS, XL, 13, 2) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 403f54171d..429afd1f48 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -35,6 +35,37 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) #endif } +static RISCVMXL cpu_get_xl(CPURISCVState *env) +{ +#if defined(TARGET_RISCV32) + return MXL_RV32; +#elif defined(CONFIG_USER_ONLY) + return MXL_RV64; +#else + RISCVMXL xl = riscv_cpu_mxl(env); + + /* + * When emulating a 32-bit-only cpu, use RV32. + * When emulating a 64-bit cpu, and MXL has been reduced to RV32, + * MSTATUSH doesn't have UXL/SXL, therefore XLEN cannot be widened + * back to RV64 for lower privs. + */ + if (xl != MXL_RV32) { + switch (env->priv) { + case PRV_M: + break; + case PRV_U: + xl = get_field(env->mstatus, MSTATUS64_UXL); + break; + default: /* PRV_S | PRV_H */ + xl = get_field(env->mstatus, MSTATUS64_SXL); + break; + } + } + return xl; +#endif +} + void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *pflags) { @@ -78,6 +109,8 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, } #endif + flags = FIELD_DP32(flags, TB_FLAGS, XL, cpu_get_xl(env)); + *pflags = flags; } diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 9c0753bc8b..c4a479ddd2 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -526,6 +526,9 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, mstatus = set_field(mstatus, MSTATUS32_SD, dirty); } else { mstatus = set_field(mstatus, MSTATUS64_SD, dirty); + /* SXL and UXL fields are for now read only */ + mstatus = set_field(mstatus, MSTATUS64_SXL, MXL_RV64); + mstatus = set_field(mstatus, MSTATUS64_UXL, MXL_RV64); } env->mstatus = mstatus; diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 0aa2122529..77cad9bc45 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -514,7 +514,6 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #else ctx->virt_enabled = false; #endif - ctx->xl = env->misa_mxl; ctx->misa_ext = env->misa_ext; ctx->frm = -1; /* unknown rounding mode */ ctx->ext_ifencei = cpu->cfg.ext_ifencei; @@ -526,6 +525,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->lmul = FIELD_EX32(tb_flags, TB_FLAGS, LMUL); ctx->mlen = 1 << (ctx->sew + 3 - ctx->lmul); ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); + ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); ctx->cs = cs; ctx->w = false; ctx->ntemp = 0; From fbb48032e46976cfc94a90a4233a2060fdc36a4e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:00 -0700 Subject: [PATCH 0721/1334] target/riscv: Use REQUIRE_64BIT in amo_check64 Use the same REQUIRE_64BIT check that we use elsewhere, rather than open-coding the use of is_32bit. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-7-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 45be491ed4..233131bae1 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -743,7 +743,8 @@ static bool amo_check(DisasContext *s, arg_rwdvm* a) static bool amo_check64(DisasContext *s, arg_rwdvm* a) { - return !is_32bit(s) && amo_check(s, a); + REQUIRE_64BIT(s); + return amo_check(s, a); } GEN_VEXT_TRANS(vamoswapw_v, 0, rwdvm, amo_op, amo_check) From 4e97d459a0f2b92815c2c2c6eb96b75e2235b42e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:01 -0700 Subject: [PATCH 0722/1334] target/riscv: Properly check SEW in amo_op We're currently assuming SEW <= 3, and the "else" from the SEW == 3 must be less. Use a switch and explicitly bound both SEW and SEQ for all cases. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-8-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 233131bae1..17ee3babef 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -704,18 +704,20 @@ static bool amo_op(DisasContext *s, arg_rwdvm *a, uint8_t seq) gen_helper_exit_atomic(cpu_env); s->base.is_jmp = DISAS_NORETURN; return true; - } else { - if (s->sew == 3) { - if (!is_32bit(s)) { - fn = fnsd[seq]; - } else { - /* Check done in amo_check(). */ - g_assert_not_reached(); - } - } else { - assert(seq < ARRAY_SIZE(fnsw)); - fn = fnsw[seq]; - } + } + + switch (s->sew) { + case 0 ... 2: + assert(seq < ARRAY_SIZE(fnsw)); + fn = fnsw[seq]; + break; + case 3: + /* XLEN check done in amo_check(). */ + assert(seq < ARRAY_SIZE(fnsd)); + fn = fnsd[seq]; + break; + default: + g_assert_not_reached(); } data = FIELD_DP32(data, VDATA, MLEN, s->mlen); From 905b9fcde1fb84d718d95369c5d886bc81bbdd8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:02 -0700 Subject: [PATCH 0723/1334] target/riscv: Replace is_32bit with get_xl/get_xlen In preparation for RV128, replace a simple predicate with a more versatile test. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-9-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/translate.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 77cad9bc45..d0ba54091e 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -91,16 +91,19 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) } #ifdef TARGET_RISCV32 -# define is_32bit(ctx) true +#define get_xl(ctx) MXL_RV32 #elif defined(CONFIG_USER_ONLY) -# define is_32bit(ctx) false +#define get_xl(ctx) MXL_RV64 #else -static inline bool is_32bit(DisasContext *ctx) -{ - return ctx->xl == MXL_RV32; -} +#define get_xl(ctx) ((ctx)->xl) #endif +/* The word size for this machine mode. */ +static inline int __attribute__((unused)) get_xlen(DisasContext *ctx) +{ + return 16 << get_xl(ctx); +} + /* The word size for this operation. */ static inline int oper_len(DisasContext *ctx) { @@ -257,7 +260,7 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) static void mark_fs_dirty(DisasContext *ctx) { TCGv tmp; - target_ulong sd = is_32bit(ctx) ? MSTATUS32_SD : MSTATUS64_SD; + target_ulong sd = get_xl(ctx) == MXL_RV32 ? MSTATUS32_SD : MSTATUS64_SD; if (ctx->mstatus_fs != MSTATUS_FS) { /* Remember the state change for the rest of the TB. */ @@ -316,16 +319,16 @@ EX_SH(12) } \ } while (0) -#define REQUIRE_32BIT(ctx) do { \ - if (!is_32bit(ctx)) { \ - return false; \ - } \ +#define REQUIRE_32BIT(ctx) do { \ + if (get_xl(ctx) != MXL_RV32) { \ + return false; \ + } \ } while (0) -#define REQUIRE_64BIT(ctx) do { \ - if (is_32bit(ctx)) { \ - return false; \ - } \ +#define REQUIRE_64BIT(ctx) do { \ + if (get_xl(ctx) < MXL_RV64) { \ + return false; \ + } \ } while (0) static int ex_rvc_register(DisasContext *ctx, int reg) From 7667cafd5a0d173d1853b4d8414d4a98df1374dc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Oct 2021 20:17:03 -0700 Subject: [PATCH 0724/1334] target/riscv: Replace DisasContext.w with DisasContext.ol In preparation for RV128, consider more than just "w" for operand size modification. This will be used for the "d" insns from RV128 as well. Rename oper_len to get_olen to better match get_xlen. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-id: 20211020031709.359469-10-richard.henderson@linaro.org Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 8 +-- target/riscv/insn_trans/trans_rvi.c.inc | 18 +++---- target/riscv/insn_trans/trans_rvm.c.inc | 10 ++-- target/riscv/translate.c | 71 ++++++++++++++++--------- 4 files changed, 63 insertions(+), 44 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 3095624f32..9ef8ab94ad 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -344,7 +344,7 @@ static bool trans_cpopw(DisasContext *ctx, arg_cpopw *a) { REQUIRE_64BIT(ctx); REQUIRE_ZBB(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_unary(ctx, a, EXT_ZERO, tcg_gen_ctpop_tl); } @@ -370,7 +370,7 @@ static bool trans_rorw(DisasContext *ctx, arg_rorw *a) { REQUIRE_64BIT(ctx); REQUIRE_ZBB(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift(ctx, a, EXT_NONE, gen_rorw); } @@ -378,7 +378,7 @@ static bool trans_roriw(DisasContext *ctx, arg_roriw *a) { REQUIRE_64BIT(ctx); REQUIRE_ZBB(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_rorw); } @@ -404,7 +404,7 @@ static bool trans_rolw(DisasContext *ctx, arg_rolw *a) { REQUIRE_64BIT(ctx); REQUIRE_ZBB(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift(ctx, a, EXT_NONE, gen_rolw); } diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index a6a57c94bb..9cf0383cfb 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -331,14 +331,14 @@ static bool trans_and(DisasContext *ctx, arg_and *a) static bool trans_addiw(DisasContext *ctx, arg_addiw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_arith_imm_fn(ctx, a, EXT_NONE, tcg_gen_addi_tl); } static bool trans_slliw(DisasContext *ctx, arg_slliw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift_imm_fn(ctx, a, EXT_NONE, tcg_gen_shli_tl); } @@ -350,7 +350,7 @@ static void gen_srliw(TCGv dst, TCGv src, target_long shamt) static bool trans_srliw(DisasContext *ctx, arg_srliw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_srliw); } @@ -362,42 +362,42 @@ static void gen_sraiw(TCGv dst, TCGv src, target_long shamt) static bool trans_sraiw(DisasContext *ctx, arg_sraiw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_sraiw); } static bool trans_addw(DisasContext *ctx, arg_addw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_arith(ctx, a, EXT_NONE, tcg_gen_add_tl); } static bool trans_subw(DisasContext *ctx, arg_subw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_arith(ctx, a, EXT_NONE, tcg_gen_sub_tl); } static bool trans_sllw(DisasContext *ctx, arg_sllw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift(ctx, a, EXT_NONE, tcg_gen_shl_tl); } static bool trans_srlw(DisasContext *ctx, arg_srlw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift(ctx, a, EXT_ZERO, tcg_gen_shr_tl); } static bool trans_sraw(DisasContext *ctx, arg_sraw *a) { REQUIRE_64BIT(ctx); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_shift(ctx, a, EXT_SIGN, tcg_gen_sar_tl); } diff --git a/target/riscv/insn_trans/trans_rvm.c.inc b/target/riscv/insn_trans/trans_rvm.c.inc index b89a85ad3a..9a1fe3c799 100644 --- a/target/riscv/insn_trans/trans_rvm.c.inc +++ b/target/riscv/insn_trans/trans_rvm.c.inc @@ -214,7 +214,7 @@ static bool trans_mulw(DisasContext *ctx, arg_mulw *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVM); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl); } @@ -222,7 +222,7 @@ static bool trans_divw(DisasContext *ctx, arg_divw *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVM); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_arith(ctx, a, EXT_SIGN, gen_div); } @@ -230,7 +230,7 @@ static bool trans_divuw(DisasContext *ctx, arg_divuw *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVM); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_arith(ctx, a, EXT_ZERO, gen_divu); } @@ -238,7 +238,7 @@ static bool trans_remw(DisasContext *ctx, arg_remw *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVM); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_arith(ctx, a, EXT_SIGN, gen_rem); } @@ -246,6 +246,6 @@ static bool trans_remuw(DisasContext *ctx, arg_remuw *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVM); - ctx->w = true; + ctx->ol = MXL_RV32; return gen_arith(ctx, a, EXT_ZERO, gen_remu); } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index d0ba54091e..afd59ef690 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -67,7 +67,7 @@ typedef struct DisasContext { to any system register, which includes CSR_FRM, so we do not have to reset this known value. */ int frm; - bool w; + RISCVMXL ol; bool virt_enabled; bool ext_ifencei; bool hlsx; @@ -104,12 +104,17 @@ static inline int __attribute__((unused)) get_xlen(DisasContext *ctx) return 16 << get_xl(ctx); } -/* The word size for this operation. */ -static inline int oper_len(DisasContext *ctx) -{ - return ctx->w ? 32 : TARGET_LONG_BITS; -} +/* The operation length, as opposed to the xlen. */ +#ifdef TARGET_RISCV32 +#define get_ol(ctx) MXL_RV32 +#else +#define get_ol(ctx) ((ctx)->ol) +#endif +static inline int get_olen(DisasContext *ctx) +{ + return 16 << get_ol(ctx); +} /* * RISC-V requires NaN-boxing of narrower width floating point values. @@ -197,24 +202,34 @@ static TCGv get_gpr(DisasContext *ctx, int reg_num, DisasExtend ext) return ctx->zero; } - switch (ctx->w ? ext : EXT_NONE) { - case EXT_NONE: - return cpu_gpr[reg_num]; - case EXT_SIGN: - t = temp_new(ctx); - tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); - return t; - case EXT_ZERO: - t = temp_new(ctx); - tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); - return t; + switch (get_ol(ctx)) { + case MXL_RV32: + switch (ext) { + case EXT_NONE: + break; + case EXT_SIGN: + t = temp_new(ctx); + tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); + return t; + case EXT_ZERO: + t = temp_new(ctx); + tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); + return t; + default: + g_assert_not_reached(); + } + break; + case MXL_RV64: + break; + default: + g_assert_not_reached(); } - g_assert_not_reached(); + return cpu_gpr[reg_num]; } static TCGv dest_gpr(DisasContext *ctx, int reg_num) { - if (reg_num == 0 || ctx->w) { + if (reg_num == 0 || get_olen(ctx) < TARGET_LONG_BITS) { return temp_new(ctx); } return cpu_gpr[reg_num]; @@ -223,10 +238,15 @@ static TCGv dest_gpr(DisasContext *ctx, int reg_num) static void gen_set_gpr(DisasContext *ctx, int reg_num, TCGv t) { if (reg_num != 0) { - if (ctx->w) { + switch (get_ol(ctx)) { + case MXL_RV32: tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); - } else { + break; + case MXL_RV64: tcg_gen_mov_tl(cpu_gpr[reg_num], t); + break; + default: + g_assert_not_reached(); } } } @@ -387,7 +407,7 @@ static bool gen_shift_imm_fn(DisasContext *ctx, arg_shift *a, DisasExtend ext, void (*func)(TCGv, TCGv, target_long)) { TCGv dest, src1; - int max_len = oper_len(ctx); + int max_len = get_olen(ctx); if (a->shamt >= max_len) { return false; @@ -406,7 +426,7 @@ static bool gen_shift_imm_tl(DisasContext *ctx, arg_shift *a, DisasExtend ext, void (*func)(TCGv, TCGv, TCGv)) { TCGv dest, src1, src2; - int max_len = oper_len(ctx); + int max_len = get_olen(ctx); if (a->shamt >= max_len) { return false; @@ -430,7 +450,7 @@ static bool gen_shift(DisasContext *ctx, arg_r *a, DisasExtend ext, TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); TCGv ext2 = tcg_temp_new(); - tcg_gen_andi_tl(ext2, src2, oper_len(ctx) - 1); + tcg_gen_andi_tl(ext2, src2, get_olen(ctx) - 1); func(dest, src1, ext2); gen_set_gpr(ctx, a->rd, dest); @@ -530,7 +550,6 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); ctx->cs = cs; - ctx->w = false; ctx->ntemp = 0; memset(ctx->temp, 0, sizeof(ctx->temp)); @@ -554,9 +573,9 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) CPURISCVState *env = cpu->env_ptr; uint16_t opcode16 = translator_lduw(env, &ctx->base, ctx->base.pc_next); + ctx->ol = ctx->xl; decode_opc(env, ctx, opcode16); ctx->base.pc_next = ctx->pc_succ_insn; - ctx->w = false; for (int i = ctx->ntemp - 1; i >= 0; --i) { tcg_temp_free(ctx->temp[i]); From 82b6a3f64d6d1ed0d5c455489a9294b59c8de65f Mon Sep 17 00:00:00 2001 From: John Wang Date: Fri, 22 Oct 2021 09:52:16 +0200 Subject: [PATCH 0725/1334] aspeed: Add support for the fp5280g2-bmc board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fp5280g2-bmc is supported by OpenBMC, It's based on the following device tree https://github.com/openbmc/linux/blob/dev-5.10/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts Signed-off-by: John Wang Reviewed-by: Cédric Le Goater Message-Id: <20211014064548.934799-1-wangzhiqiang02@inspur.com> Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index f5916e8126..a77f46b3ad 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -131,6 +131,21 @@ struct AspeedMachineState { SCU_HW_STRAP_VGA_SIZE_SET(VGA_64M_DRAM) | \ SCU_AST2500_HW_STRAP_RESERVED1) +/* FP5280G2 hardware value: 0XF100D286 */ +#define FP5280G2_BMC_HW_STRAP1 ( \ + SCU_AST2500_HW_STRAP_SPI_AUTOFETCH_ENABLE | \ + SCU_AST2500_HW_STRAP_GPIO_STRAP_ENABLE | \ + SCU_AST2500_HW_STRAP_UART_DEBUG | \ + SCU_AST2500_HW_STRAP_RESERVED28 | \ + SCU_AST2500_HW_STRAP_DDR4_ENABLE | \ + SCU_HW_STRAP_VGA_CLASS_CODE | \ + SCU_HW_STRAP_LPC_RESET_PIN | \ + SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER) | \ + SCU_AST2500_HW_STRAP_SET_AXI_AHB_RATIO(AXI_AHB_RATIO_2_1) | \ + SCU_HW_STRAP_MAC1_RGMII | \ + SCU_HW_STRAP_VGA_SIZE_SET(VGA_16M_DRAM) | \ + SCU_AST2500_HW_STRAP_RESERVED1) + /* Witherspoon hardware value: 0xF10AD216 (but use romulus definition) */ #define WITHERSPOON_BMC_HW_STRAP1 ROMULUS_BMC_HW_STRAP1 @@ -430,6 +445,15 @@ static void aspeed_machine_init(MachineState *machine) arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); } +static void at24c_eeprom_init(I2CBus *bus, uint8_t addr, uint32_t rsize) +{ + I2CSlave *i2c_dev = i2c_slave_new("at24c-eeprom", addr); + DeviceState *dev = DEVICE(i2c_dev); + + qdev_prop_set_uint32(dev, "rom-size", rsize); + i2c_slave_realize_and_unref(i2c_dev, bus, &error_abort); +} + static void palmetto_bmc_i2c_init(AspeedMachineState *bmc) { AspeedSoCState *soc = &bmc->soc; @@ -689,6 +713,34 @@ static void aspeed_eeprom_init(I2CBus *bus, uint8_t addr, uint32_t rsize) i2c_slave_realize_and_unref(i2c_dev, bus, &error_abort); } +static void fp5280g2_bmc_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + I2CSlave *i2c_mux; + + /* The at24c256 */ + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 1), 0x50, 32768); + + /* The fp5280g2 expects a TMP112 but a TMP105 is compatible */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 2), TYPE_TMP105, + 0x48); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 2), TYPE_TMP105, + 0x49); + + i2c_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 2), + "pca9546", 0x70); + /* It expects a TMP112 but a TMP105 is compatible */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 0), TYPE_TMP105, + 0x4a); + + /* It expects a ds3232 but a ds1338 is good enough */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "ds1338", 0x68); + + /* It expects a pca9555 but a pca9552 is compatible */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), TYPE_PCA9552, + 0x20); +} + static void rainier_bmc_i2c_init(AspeedMachineState *bmc) { AspeedSoCState *soc = &bmc->soc; @@ -1140,6 +1192,24 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) aspeed_soc_num_cpus(amc->soc_name); }; +static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Inspur FP5280G2 BMC (ARM1176)"; + amc->soc_name = "ast2500-a1"; + amc->hw_strap1 = FP5280G2_BMC_HW_STRAP1; + amc->fmc_model = "n25q512a"; + amc->spi_model = "mx25l25635e"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; + amc->i2c_init = fp5280g2_bmc_i2c_init; + mc->default_ram_size = 512 * MiB; + mc->default_cpus = mc->min_cpus = mc->max_cpus = + aspeed_soc_num_cpus(amc->soc_name); +}; + static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1227,6 +1297,10 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("g220a-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_g220a_class_init, + }, { + .name = MACHINE_TYPE_NAME("fp5280g2-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_fp5280g2_class_init, }, { .name = MACHINE_TYPE_NAME("quanta-q71l-bmc"), .parent = TYPE_ASPEED_MACHINE, From fc6642544eb86b520fed4e3b7792bd89e563de08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 22 Oct 2021 09:52:17 +0200 Subject: [PATCH 0726/1334] aspeed/smc: Use a container for the flash mmio address space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because AddressSpaces must not be sysbus-mapped, commit e9c568dbc225 ("hw/arm/aspeed: Do not sysbus-map mmio flash region directly, use alias") introduced an alias for the flash mmio region. Using a container is cleaner. Cc: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Francisco Iglesias Message-Id: <20211018132609.160008-5-clg@kaod.org> Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 11 +++++++---- include/hw/ssi/aspeed_smc.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 8a988c1676..ff154eb84f 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1151,14 +1151,17 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) * window in which the flash modules are mapped. The size and * address depends on the SoC model and controller type. */ + memory_region_init(&s->mmio_flash_container, OBJECT(s), + TYPE_ASPEED_SMC ".container", + asc->flash_window_size); + sysbus_init_mmio(sbd, &s->mmio_flash_container); + memory_region_init_io(&s->mmio_flash, OBJECT(s), &aspeed_smc_flash_default_ops, s, TYPE_ASPEED_SMC ".flash", asc->flash_window_size); - memory_region_init_alias(&s->mmio_flash_alias, OBJECT(s), - TYPE_ASPEED_SMC ".flash", - &s->mmio_flash, 0, asc->flash_window_size); - sysbus_init_mmio(sbd, &s->mmio_flash_alias); + memory_region_add_subregion(&s->mmio_flash_container, 0x0, + &s->mmio_flash); /* * Let's create a sub memory region for each possible peripheral. All diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 75bc793bd2..e265555819 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -52,8 +52,8 @@ struct AspeedSMCState { SysBusDevice parent_obj; MemoryRegion mmio; + MemoryRegion mmio_flash_container; MemoryRegion mmio_flash; - MemoryRegion mmio_flash_alias; qemu_irq irq; From b12fa6118f4d838d19720ec6476a1666a1b43474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 22 Oct 2021 09:52:17 +0200 Subject: [PATCH 0727/1334] speed/sdhci: Add trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Francisco Iglesias Message-Id: <20211018132609.160008-6-clg@kaod.org> Signed-off-by: Cédric Le Goater --- hw/sd/aspeed_sdhci.c | 5 +++++ hw/sd/trace-events | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index 3299844de6..df1bdf1fa4 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -14,6 +14,7 @@ #include "hw/irq.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" +#include "trace.h" #define ASPEED_SDHCI_INFO 0x00 #define ASPEED_SDHCI_INFO_SLOT1 (1 << 17) @@ -60,6 +61,8 @@ static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size) } } + trace_aspeed_sdhci_read(addr, size, (uint64_t) val); + return (uint64_t)val; } @@ -68,6 +71,8 @@ static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val, { AspeedSDHCIState *sdhci = opaque; + trace_aspeed_sdhci_write(addr, size, val); + switch (addr) { case ASPEED_SDHCI_INFO: /* The RESET bit automatically clears. */ diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 3cc2ef89ba..94a00557b2 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -68,3 +68,7 @@ pl181_fifo_push(uint32_t data) "FIFO push 0x%08" PRIx32 pl181_fifo_pop(uint32_t data) "FIFO pop 0x%08" PRIx32 pl181_fifo_transfer_complete(void) "FIFO transfer complete" pl181_data_engine_idle(void) "data engine idle" + +# aspeed_sdhci.c +aspeed_sdhci_read(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 +aspeed_sdhci_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 From b4b9a0e32f93c0700f46617524317b0580126592 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Oct 2021 12:19:16 +0200 Subject: [PATCH 0728/1334] update seabios to master branch snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new seabios release is planned for november. Update to a master branch snapshot, to (a) increase test coverage of the changes. (b) make the delta smaller when updating to the final release during the qemu 6.2 freeze. Most noteworthy this fixes the nvme boot regression caused by adding namespace support to the qemu nvme emulation. seabios shortlog ================ Alex Martens via SeaBIOS (1): nvme: fix missing newline on sq full print Alexander Graf (4): nvme: Record maximum allowed request size nvme: Allow to set PRP2 nvme: Pass large I/O requests as PRP lists nvme: Split requests by maximum allowed size Daniel P. Berrangé (1): smbios: avoid integer overflow when adding SMBIOS type 0 table David Woodhouse (1): nvme: Clean up nvme_cmd_readwrite() Gerd Hoffmann (9): output: add support for uppercase hex numbers dsdt: add support for pnp ids as strings usb: add boot prio support for mmio host adapters usb/xhci: split xhci setup into generic and pci parts usb/xhci: add support for mmio host adapters (via acpi). usb boot: add xhci mmio example nvme: improve namespace allocation nvme: drive desc should not include the newline Increase BUILD_MIN_BIOSTABLE for large roms Matt DeVillier (1): usb.c: Fix devices using non-primary interface descriptor Mike Banon (1): Support booting USB drives with a write protect switch enabled Sergei Trofimovich (1): vgasrc: ignore .node.gnu.property (binutils-2.36 support) Stefan Berger (4): tcgbios: Fix details in log entries Add implementations for sha256, sha384, and sha512 tcgbios: Use The proper sha function for each PCR bank tcgbios: Disable platform hierarchy in case of failure Stefan Ott via SeaBIOS (1): usb-hid: Increase MAX_KBD_EVENT Volker Rümelin (2): stacks: call check_irqs() in run_thread() stacks: call check_irqs() after switch_next() weitaowang-oc@zhaoxin.com (1): USB:Fix xHCI initail fail by using longer reset and CNR clear timeout value Signed-off-by: Gerd Hoffmann --- roms/seabios | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roms/seabios b/roms/seabios index 155821a199..64f37cc530 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit 155821a1990b6de78dde5f98fa5ab90e802021e0 +Subproject commit 64f37cc530f144e53c190c9e8209a51b58fd5c43 From 9fb3fcfce512da58cd048eaefd293e1d3f513de2 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Oct 2021 12:23:33 +0200 Subject: [PATCH 0729/1334] update seabios binaries Signed-off-by: Gerd Hoffmann --- pc-bios/bios-256k.bin | Bin 262144 -> 262144 bytes pc-bios/bios-microvm.bin | Bin 131072 -> 131072 bytes pc-bios/bios.bin | Bin 131072 -> 131072 bytes pc-bios/vgabios-ati.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-bochs-display.bin | Bin 28672 -> 28672 bytes pc-bios/vgabios-cirrus.bin | Bin 38912 -> 39424 bytes pc-bios/vgabios-qxl.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-ramfb.bin | Bin 28672 -> 28672 bytes pc-bios/vgabios-stdvga.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-virtio.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-vmware.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios.bin | Bin 38912 -> 38912 bytes 12 files changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index 96bdeaa487daef197cee02655116518baba34f67..dea01da468932e67251e9ca3d443ec91d870a2ec 100644 GIT binary patch literal 262144 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�x&twAAgG0U;8)q&?(QpoRm)-PkBNG3Z;+0 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